Changeset View
Changeset View
Standalone View
Standalone View
sys/net/route/route_ctl.c
Show All 23 Lines | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_inet.h" | #include "opt_inet.h" | ||||
#include "opt_inet6.h" | #include "opt_inet6.h" | ||||
#include "opt_mpath.h" | #include "opt_route.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/syslog.h> | #include <sys/syslog.h> | ||||
Show All 37 Lines | static int add_route(struct rib_head *rnh, struct rt_addrinfo *info, | ||||
struct rib_cmd_info *rc); | struct rib_cmd_info *rc); | ||||
static int add_route_nhop(struct rib_head *rnh, struct rtentry *rt, | static int add_route_nhop(struct rib_head *rnh, struct rtentry *rt, | ||||
struct rt_addrinfo *info, struct route_nhop_data *rnd, | struct rt_addrinfo *info, struct route_nhop_data *rnd, | ||||
struct rib_cmd_info *rc); | struct rib_cmd_info *rc); | ||||
static int del_route(struct rib_head *rnh, struct rt_addrinfo *info, | static int del_route(struct rib_head *rnh, struct rt_addrinfo *info, | ||||
struct rib_cmd_info *rc); | struct rib_cmd_info *rc); | ||||
static int change_route(struct rib_head *rnh, struct rt_addrinfo *info, | static int change_route(struct rib_head *rnh, struct rt_addrinfo *info, | ||||
struct route_nhop_data *nhd_orig, struct rib_cmd_info *rc); | struct route_nhop_data *nhd_orig, struct rib_cmd_info *rc); | ||||
static int change_route_nhop(struct rib_head *rnh, struct rtentry *rt, | |||||
struct rt_addrinfo *info, struct route_nhop_data *rnd, | |||||
struct rib_cmd_info *rc); | |||||
static int rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info, | static int rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info, | ||||
struct rib_cmd_info *rc); | struct rib_cmd_info *rc); | ||||
static void rib_notify(struct rib_head *rnh, enum rib_subscription_type type, | static void rib_notify(struct rib_head *rnh, enum rib_subscription_type type, | ||||
struct rib_cmd_info *rc); | struct rib_cmd_info *rc); | ||||
static void destroy_subscription_epoch(epoch_context_t ctx); | static void destroy_subscription_epoch(epoch_context_t ctx); | ||||
static bool rib_can_multipath(struct rib_head *rh); | |||||
/* Per-vnet multipath routing configuration */ | |||||
SYSCTL_DECL(_net_route); | |||||
#define V_rib_route_multipath VNET(rib_route_multipath) | |||||
#ifdef ROUTE_MPATH | |||||
VNET_DEFINE(u_int, rib_route_multipath) = 1; | |||||
#define _MP_FLAGS CTLFLAG_RW | |||||
#else | |||||
VNET_DEFINE(u_int, rib_route_multipath) = 0; | |||||
#define _MP_FLAGS CTLFLAG_RD | |||||
#endif | |||||
SYSCTL_UINT(_net_route, OID_AUTO, multipath, _MP_FLAGS | CTLFLAG_VNET, | |||||
&VNET_NAME(rib_route_multipath), 0, "Enable route multipath"); | |||||
#undef _MP_FLAGS | |||||
/* Routing table UMA zone */ | /* Routing table UMA zone */ | ||||
VNET_DEFINE_STATIC(uma_zone_t, rtzone); | VNET_DEFINE_STATIC(uma_zone_t, rtzone); | ||||
#define V_rtzone VNET(rtzone) | #define V_rtzone VNET(rtzone) | ||||
void | void | ||||
vnet_rtzone_init() | vnet_rtzone_init() | ||||
{ | { | ||||
Show All 17 Lines | destroy_rtentry(struct rtentry *rt) | ||||
/* | /* | ||||
* At this moment rnh, nh_control may be already freed. | * At this moment rnh, nh_control may be already freed. | ||||
* nhop interface may have been migrated to a different vnet. | * nhop interface may have been migrated to a different vnet. | ||||
* Use vnet stored in the nexthop to delete the entry. | * Use vnet stored in the nexthop to delete the entry. | ||||
*/ | */ | ||||
CURVNET_SET(nhop_get_vnet(rt->rt_nhop)); | CURVNET_SET(nhop_get_vnet(rt->rt_nhop)); | ||||
/* Unreference nexthop */ | /* Unreference nexthop */ | ||||
nhop_free(rt->rt_nhop); | nhop_free_any(rt->rt_nhop); | ||||
uma_zfree(V_rtzone, rt); | uma_zfree(V_rtzone, rt); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
/* | /* | ||||
* Epoch callback indicating rtentry is safe to destroy | * Epoch callback indicating rtentry is safe to destroy | ||||
Show All 30 Lines | get_rnh(uint32_t fibnum, const struct rt_addrinfo *info) | ||||
KASSERT((fibnum < rt_numfibs), ("rib_add_route: bad fibnum")); | KASSERT((fibnum < rt_numfibs), ("rib_add_route: bad fibnum")); | ||||
dst = info->rti_info[RTAX_DST]; | dst = info->rti_info[RTAX_DST]; | ||||
rnh = rt_tables_get_rnh(fibnum, dst->sa_family); | rnh = rt_tables_get_rnh(fibnum, dst->sa_family); | ||||
return (rnh); | return (rnh); | ||||
} | } | ||||
#ifdef ROUTE_MPATH | |||||
static bool | |||||
rib_can_multipath(struct rib_head *rh) | |||||
{ | |||||
int result; | |||||
CURVNET_SET(rh->rib_vnet); | |||||
result = !!V_rib_route_multipath; | |||||
CURVNET_RESTORE(); | |||||
return (result); | |||||
} | |||||
/* | |||||
* Check is nhop is multipath-eligible. | |||||
* Avoid nhops without gateways and redirects. | |||||
* | |||||
* Returns 1 for multipath-eligible nexthop, | |||||
* 0 otherwise. | |||||
*/ | |||||
bool | |||||
nhop_can_multipath(const struct nhop_object *nh) | |||||
{ | |||||
if ((nh->nh_flags & NHF_MULTIPATH) != 0) | |||||
return (1); | |||||
if ((nh->nh_flags & NHF_GATEWAY) == 0) | |||||
return (0); | |||||
if ((nh->nh_flags & NHF_REDIRECT) != 0) | |||||
return (0); | |||||
return (1); | |||||
} | |||||
#endif | |||||
static int | static int | ||||
get_info_weight(const struct rt_addrinfo *info, uint32_t default_weight) | get_info_weight(const struct rt_addrinfo *info, uint32_t default_weight) | ||||
{ | { | ||||
uint32_t weight; | uint32_t weight; | ||||
if (info->rti_mflags & RTV_WEIGHT) | if (info->rti_mflags & RTV_WEIGHT) | ||||
weight = info->rti_rmx->rmx_weight; | weight = info->rti_rmx->rmx_weight; | ||||
else | else | ||||
Show All 15 Lines | rt->rt_expire = info->rti_rmx->rmx_expire ? | ||||
info->rti_rmx->rmx_expire - time_second + time_uptime : 0; | info->rti_rmx->rmx_expire - time_second + time_uptime : 0; | ||||
} | } | ||||
/* | /* | ||||
* Check if specified @gw matches gw data in the nexthop @nh. | * Check if specified @gw matches gw data in the nexthop @nh. | ||||
* | * | ||||
* Returns true if matches, false otherwise. | * Returns true if matches, false otherwise. | ||||
*/ | */ | ||||
static bool | bool | ||||
match_nhop_gw(const struct nhop_object *nh, const struct sockaddr *gw) | match_nhop_gw(const struct nhop_object *nh, const struct sockaddr *gw) | ||||
{ | { | ||||
if (nh->gw_sa.sa_family != gw->sa_family) | if (nh->gw_sa.sa_family != gw->sa_family) | ||||
return (false); | return (false); | ||||
switch (gw->sa_family) { | switch (gw->sa_family) { | ||||
case AF_INET: | case AF_INET: | ||||
▲ Show 20 Lines • Show All 238 Lines • ▼ Show 20 Lines | create_rtentry(struct rib_head *rnh, struct rt_addrinfo *info, | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
add_route(struct rib_head *rnh, struct rt_addrinfo *info, | add_route(struct rib_head *rnh, struct rt_addrinfo *info, | ||||
struct rib_cmd_info *rc) | struct rib_cmd_info *rc) | ||||
{ | { | ||||
struct nhop_object *nh_orig; | struct nhop_object *nh_orig; | ||||
struct route_nhop_data rnd; | struct route_nhop_data rnd_orig, rnd_add; | ||||
struct nhop_object *nh; | struct nhop_object *nh; | ||||
struct rtentry *rt, *rt_orig; | struct rtentry *rt, *rt_orig; | ||||
int error; | int error; | ||||
error = create_rtentry(rnh, info, &rt); | error = create_rtentry(rnh, info, &rt); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
rnd.rnd_nhop = rt->rt_nhop; | rnd_add.rnd_nhop = rt->rt_nhop; | ||||
rnd.rnd_weight = rt->rt_weight; | rnd_add.rnd_weight = rt->rt_weight; | ||||
nh = rt->rt_nhop; | nh = rt->rt_nhop; | ||||
RIB_WLOCK(rnh); | RIB_WLOCK(rnh); | ||||
#ifdef RADIX_MPATH | error = add_route_nhop(rnh, rt, info, &rnd_add, rc); | ||||
struct sockaddr *netmask; | |||||
netmask = info->rti_info[RTAX_NETMASK]; | |||||
/* do not permit exactly the same dst/mask/gw pair */ | |||||
if (rt_mpath_capable(rnh) && | |||||
rt_mpath_conflict(rnh, rt, netmask)) { | |||||
RIB_WUNLOCK(rnh); | |||||
nhop_free(nh); | |||||
uma_zfree(V_rtzone, rt); | |||||
return (EEXIST); | |||||
} | |||||
#endif | |||||
error = add_route_nhop(rnh, rt, info, &rnd, rc); | |||||
if (error == 0) { | if (error == 0) { | ||||
RIB_WUNLOCK(rnh); | RIB_WUNLOCK(rnh); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* addition failed. Lookup prefix in the rib to determine the cause */ | /* addition failed. Lookup prefix in the rib to determine the cause */ | ||||
rt_orig = lookup_prefix(rnh, info, &rnd); | rt_orig = lookup_prefix(rnh, info, &rnd_orig); | ||||
if (rt_orig == NULL) { | if (rt_orig == NULL) { | ||||
/* No prefix -> rnh_addaddr() failed to allocate memory */ | /* No prefix -> rnh_addaddr() failed to allocate memory */ | ||||
RIB_WUNLOCK(rnh); | RIB_WUNLOCK(rnh); | ||||
nhop_free(nh); | nhop_free(nh); | ||||
uma_zfree(V_rtzone, rt); | uma_zfree(V_rtzone, rt); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
/* We have existing route in the RIB. */ | /* We have existing route in the RIB. */ | ||||
nh_orig = rnd.rnd_nhop; | nh_orig = rnd_orig.rnd_nhop; | ||||
/* Check if new route has higher preference */ | /* Check if new route has higher preference */ | ||||
if (can_override_nhop(info, nh_orig) > 0) { | if (can_override_nhop(info, nh_orig) > 0) { | ||||
/* Update nexthop to the new route */ | /* Update nexthop to the new route */ | ||||
change_route_nhop(rnh, rt_orig, info, &rnd, rc); | change_route_nhop(rnh, rt_orig, info, &rnd_add, rc); | ||||
RIB_WUNLOCK(rnh); | RIB_WUNLOCK(rnh); | ||||
uma_zfree(V_rtzone, rt); | uma_zfree(V_rtzone, rt); | ||||
nhop_free(nh_orig); | nhop_free(nh_orig); | ||||
return (0); | return (0); | ||||
} | } | ||||
RIB_WUNLOCK(rnh); | RIB_WUNLOCK(rnh); | ||||
#ifdef ROUTE_MPATH | |||||
if (rib_can_multipath(rnh) && nhop_can_multipath(rnd_add.rnd_nhop) && | |||||
nhop_can_multipath(rnd_orig.rnd_nhop)) | |||||
error = add_route_mpath(rnh, info, rt, &rnd_add, &rnd_orig, rc); | |||||
else | |||||
#endif | |||||
/* Unable to add - another route with the same preference exists */ | /* Unable to add - another route with the same preference exists */ | ||||
error = EEXIST; | error = EEXIST; | ||||
/* | |||||
* ROUTE_MPATH disabled: failed to add route, free both nhop and rt. | |||||
* ROUTE_MPATH enabled: original nhop reference is unused in any case, | |||||
* free rt only if not _adding_ new route to rib (e.g. the case | |||||
* when initial lookup returned existing route, but then it got | |||||
* deleted prior to multipath group insertion, leading to a simple | |||||
* non-multipath add as a result). | |||||
*/ | |||||
nhop_free(nh); | nhop_free(nh); | ||||
if ((error != 0) || rc->rc_cmd != RTM_ADD) | |||||
uma_zfree(V_rtzone, rt); | uma_zfree(V_rtzone, rt); | ||||
donner: So in the case of ! ROUTE_MPATH, the call to uma_zfree is suppressed now? | |||||
Done Inline ActionsNot exactly. We have error set to EEXIST above, which renders this condition to be true. melifaro: Not exactly. We have error set to EEXIST above, which renders this condition to be true. | |||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Removes route defined by @info from the kernel table specified by @fibnum and | * Removes route defined by @info from the kernel table specified by @fibnum and | ||||
* sa_family in @info->rti_info[RTAX_DST]. | * sa_family in @info->rti_info[RTAX_DST]. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info, struct rib_cmd_info *rc) | ||||
struct route_nhop_data rnd; | struct route_nhop_data rnd; | ||||
int error; | int error; | ||||
rt = lookup_prefix(rnh, info, &rnd); | rt = lookup_prefix(rnh, info, &rnd); | ||||
if (rt == NULL) | if (rt == NULL) | ||||
return (ESRCH); | return (ESRCH); | ||||
nh = rt->rt_nhop; | nh = rt->rt_nhop; | ||||
#ifdef ROUTE_MPATH | |||||
if (NH_IS_NHGRP(nh)) { | |||||
error = del_route_mpath(rnh, info, rt, | |||||
(struct nhgrp_object *)nh, rc); | |||||
return (error); | |||||
} | |||||
#endif | |||||
error = check_info_match_nhop(info, rt, nh); | error = check_info_match_nhop(info, rt, nh); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
if (can_override_nhop(info, nh) < 0) | if (can_override_nhop(info, nh) < 0) | ||||
return (EADDRINUSE); | return (EADDRINUSE); | ||||
/* | /* | ||||
* Remove the item from the tree and return it. | * Remove the item from the tree and return it. | ||||
* Complain if it is not there and do no more processing. | * Complain if it is not there and do no more processing. | ||||
*/ | */ | ||||
#ifdef RADIX_MPATH | |||||
info->rti_info[RTAX_GATEWAY] = &nh->gw_sa; | |||||
if (rt_mpath_capable(rnh)) { | |||||
rn = rt_mpath_unlink(rnh, info, rt, &error); | |||||
if (error != 0) | |||||
return (error); | |||||
} else | |||||
#endif | |||||
rn = rnh->rnh_deladdr(info->rti_info[RTAX_DST], | rn = rnh->rnh_deladdr(info->rti_info[RTAX_DST], | ||||
info->rti_info[RTAX_NETMASK], &rnh->head); | info->rti_info[RTAX_NETMASK], &rnh->head); | ||||
if (rn == NULL) | if (rn == NULL) | ||||
return (ESRCH); | return (ESRCH); | ||||
if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) | if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) | ||||
panic ("rtrequest delete"); | panic ("rtrequest delete"); | ||||
Show All 24 Lines | if (error != 0) | ||||
return (error); | return (error); | ||||
rib_notify(rnh, RIB_NOTIFY_DELAYED, rc); | rib_notify(rnh, RIB_NOTIFY_DELAYED, rc); | ||||
/* | /* | ||||
* If the caller wants it, then it can have it, | * If the caller wants it, then it can have it, | ||||
* the entry will be deleted after the end of the current epoch. | * the entry will be deleted after the end of the current epoch. | ||||
*/ | */ | ||||
if (rc->rc_cmd == RTM_DELETE) | |||||
rtfree(rc->rc_rt); | rtfree(rc->rc_rt); | ||||
#ifdef ROUTE_MPATH | |||||
else { | |||||
/* | |||||
* Deleting 1 path may result in RTM_CHANGE to | |||||
* a different mpath group/nhop. | |||||
* Free old mpath group. | |||||
*/ | |||||
nhop_free_any(rc->rc_nh_old); | |||||
} | |||||
#endif | |||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
rib_change_route(uint32_t fibnum, struct rt_addrinfo *info, | rib_change_route(uint32_t fibnum, struct rt_addrinfo *info, | ||||
struct rib_cmd_info *rc) | struct rib_cmd_info *rc) | ||||
{ | { | ||||
Show All 29 Lines | rib_change_route(uint32_t fibnum, struct rt_addrinfo *info, | ||||
rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST], | rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST], | ||||
info->rti_info[RTAX_NETMASK], &rnh->head); | info->rti_info[RTAX_NETMASK], &rnh->head); | ||||
if (rt == NULL) { | if (rt == NULL) { | ||||
RIB_RUNLOCK(rnh); | RIB_RUNLOCK(rnh); | ||||
return (ESRCH); | 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) { | |||||
RIB_RUNLOCK(rnh); | |||||
return (ESRCH); | |||||
} | |||||
} | |||||
#endif | |||||
rnd_orig.rnd_nhop = rt->rt_nhop; | rnd_orig.rnd_nhop = rt->rt_nhop; | ||||
rnd_orig.rnd_weight = rt->rt_weight; | rnd_orig.rnd_weight = rt->rt_weight; | ||||
RIB_RUNLOCK(rnh); | RIB_RUNLOCK(rnh); | ||||
for (int i = 0; i < RIB_MAX_RETRIES; i++) { | for (int i = 0; i < RIB_MAX_RETRIES; i++) { | ||||
error = change_route(rnh, info, &rnd_orig, rc); | error = change_route(rnh, info, &rnd_orig, rc); | ||||
if (error != EAGAIN) | if (error != EAGAIN) | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
change_route(struct rib_head *rnh, struct rt_addrinfo *info, | change_nhop(struct rib_head *rnh, struct rt_addrinfo *info, | ||||
struct route_nhop_data *rnd_orig, struct rib_cmd_info *rc) | struct nhop_object *nh_orig, struct nhop_object **nh_new) | ||||
{ | { | ||||
int error = 0; | |||||
int free_ifa = 0; | int free_ifa = 0; | ||||
struct nhop_object *nh, *nh_orig; | int error; | ||||
struct route_nhop_data rnd_new; | |||||
nh = NULL; | |||||
nh_orig = rnd_orig->rnd_nhop; | |||||
if (nh_orig == NULL) | |||||
return (ESRCH); | |||||
/* | /* | ||||
* New gateway could require new ifaddr, ifp; | * New gateway could require new ifaddr, ifp; | ||||
* flags may also be different; ifp may be specified | * flags may also be different; ifp may be specified | ||||
* by ll sockaddr when protocol address is ambiguous | * by ll sockaddr when protocol address is ambiguous | ||||
*/ | */ | ||||
if (((nh_orig->nh_flags & NHF_GATEWAY) && | if (((nh_orig->nh_flags & NHF_GATEWAY) && | ||||
info->rti_info[RTAX_GATEWAY] != NULL) || | info->rti_info[RTAX_GATEWAY] != NULL) || | ||||
info->rti_info[RTAX_IFP] != NULL || | info->rti_info[RTAX_IFP] != NULL || | ||||
(info->rti_info[RTAX_IFA] != NULL && | (info->rti_info[RTAX_IFA] != NULL && | ||||
!sa_equal(info->rti_info[RTAX_IFA], nh_orig->nh_ifa->ifa_addr))) { | !sa_equal(info->rti_info[RTAX_IFA], nh_orig->nh_ifa->ifa_addr))) { | ||||
error = rt_getifa_fib(info, rnh->rib_fibnum); | error = rt_getifa_fib(info, rnh->rib_fibnum); | ||||
if (info->rti_ifa != NULL) | if (info->rti_ifa != NULL) | ||||
free_ifa = 1; | free_ifa = 1; | ||||
if (error != 0) { | if (error != 0) { | ||||
if (free_ifa) { | if (free_ifa) { | ||||
ifa_free(info->rti_ifa); | ifa_free(info->rti_ifa); | ||||
info->rti_ifa = NULL; | info->rti_ifa = NULL; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
error = nhop_create_from_nhop(rnh, nh_orig, info, &nh); | error = nhop_create_from_nhop(rnh, nh_orig, info, nh_new); | ||||
if (free_ifa) { | if (free_ifa) { | ||||
ifa_free(info->rti_ifa); | ifa_free(info->rti_ifa); | ||||
info->rti_ifa = NULL; | info->rti_ifa = NULL; | ||||
} | } | ||||
return (error); | |||||
} | |||||
#ifdef ROUTE_MPATH | |||||
static int | |||||
change_mpath_route(struct rib_head *rnh, struct rt_addrinfo *info, | |||||
struct route_nhop_data *rnd_orig, struct rib_cmd_info *rc) | |||||
{ | |||||
int error = 0; | |||||
struct nhop_object *nh, *nh_orig, *nh_new; | |||||
struct route_nhop_data rnd_new; | |||||
nh = NULL; | |||||
nh_orig = rnd_orig->rnd_nhop; | |||||
struct weightened_nhop *wn = NULL, *wn_new; | |||||
uint32_t num_nhops; | |||||
wn = nhgrp_get_nhops((struct nhgrp_object *)nh_orig, &num_nhops); | |||||
nh_orig = NULL; | |||||
for (int i = 0; i < num_nhops; i++) { | |||||
if (check_info_match_nhop(info, NULL, wn[i].nh)) { | |||||
nh_orig = wn[i].nh; | |||||
break; | |||||
} | |||||
} | |||||
if (nh_orig == NULL) | |||||
return (ESRCH); | |||||
error = change_nhop(rnh, info, nh_orig, &nh_new); | |||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
rnd_new.rnd_nhop = nh; | wn_new = mallocarray(num_nhops, sizeof(struct weightened_nhop), | ||||
if (info->rti_mflags & RTV_WEIGHT) | M_TEMP, M_NOWAIT | M_ZERO); | ||||
rnd_new.rnd_weight = info->rti_rmx->rmx_weight; | if (wn_new == NULL) { | ||||
else | nhop_free(nh_new); | ||||
rnd_new.rnd_weight = rnd_orig->rnd_weight; | return (EAGAIN); | ||||
} | |||||
memcpy(wn_new, wn, num_nhops * sizeof(struct weightened_nhop)); | |||||
for (int i = 0; i < num_nhops; i++) { | |||||
if (wn[i].nh == nh_orig) { | |||||
wn[i].nh = nh_new; | |||||
wn[i].weight = get_info_weight(info, rnd_orig->rnd_weight); | |||||
break; | |||||
} | |||||
} | |||||
error = nhgrp_get_group(rnh, wn_new, num_nhops, &rnd_new); | |||||
nhop_free(nh_new); | |||||
free(wn_new, M_TEMP); | |||||
if (error != 0) | |||||
return (error); | |||||
error = change_route_conditional(rnh, NULL, info, rnd_orig, &rnd_new, rc); | error = change_route_conditional(rnh, NULL, info, rnd_orig, &rnd_new, rc); | ||||
return (error); | return (error); | ||||
} | } | ||||
#endif | |||||
static int | |||||
change_route(struct rib_head *rnh, struct rt_addrinfo *info, | |||||
struct route_nhop_data *rnd_orig, struct rib_cmd_info *rc) | |||||
{ | |||||
int error = 0; | |||||
struct nhop_object *nh, *nh_orig; | |||||
struct route_nhop_data rnd_new; | |||||
nh = NULL; | |||||
nh_orig = rnd_orig->rnd_nhop; | |||||
if (nh_orig == NULL) | |||||
return (ESRCH); | |||||
if (NH_IS_NHGRP(nh_orig)) | |||||
return (change_mpath_route(rnh, info, rnd_orig, rc)); | |||||
rnd_new.rnd_weight = get_info_weight(info, rnd_orig->rnd_weight); | |||||
error = change_nhop(rnh, info, nh_orig, &rnd_new.rnd_nhop); | |||||
if (error != 0) | |||||
return (error); | |||||
error = change_route_conditional(rnh, NULL, info, rnd_orig, &rnd_new, rc); | |||||
return (error); | |||||
} | |||||
/* | /* | ||||
* Insert @rt with nhop data from @rnd_new to @rnh. | * Insert @rt with nhop data from @rnd_new to @rnh. | ||||
* Returns 0 on success and stores operation results in @rc. | * Returns 0 on success and stores operation results in @rc. | ||||
*/ | */ | ||||
static int | static int | ||||
add_route_nhop(struct rib_head *rnh, struct rtentry *rt, | add_route_nhop(struct rib_head *rnh, struct rtentry *rt, | ||||
struct rt_addrinfo *info, struct route_nhop_data *rnd, | struct rt_addrinfo *info, struct route_nhop_data *rnd, | ||||
struct rib_cmd_info *rc) | struct rib_cmd_info *rc) | ||||
Show All 33 Lines | add_route_nhop(struct rib_head *rnh, struct rtentry *rt, | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Switch @rt nhop/weigh to the ones specified in @rnd. | * Switch @rt nhop/weigh to the ones specified in @rnd. | ||||
* Conditionally set rt_expire if set in @info. | * Conditionally set rt_expire if set in @info. | ||||
* Returns 0 on success. | * Returns 0 on success. | ||||
*/ | */ | ||||
static int | int | ||||
change_route_nhop(struct rib_head *rnh, struct rtentry *rt, | change_route_nhop(struct rib_head *rnh, struct rtentry *rt, | ||||
struct rt_addrinfo *info, struct route_nhop_data *rnd, | struct rt_addrinfo *info, struct route_nhop_data *rnd, | ||||
struct rib_cmd_info *rc) | struct rib_cmd_info *rc) | ||||
{ | { | ||||
struct nhop_object *nh_orig; | struct nhop_object *nh_orig; | ||||
RIB_WLOCK_ASSERT(rnh); | RIB_WLOCK_ASSERT(rnh); | ||||
Show All 11 Lines | if (rnd->rnd_nhop != NULL) { | ||||
struct sockaddr *ndst, *netmask; | struct sockaddr *ndst, *netmask; | ||||
struct radix_node *rn; | struct radix_node *rn; | ||||
ndst = (struct sockaddr *)rt_key(rt); | ndst = (struct sockaddr *)rt_key(rt); | ||||
netmask = info->rti_info[RTAX_NETMASK]; | netmask = info->rti_info[RTAX_NETMASK]; | ||||
rn = rnh->rnh_deladdr(ndst, netmask, &rnh->head); | rn = rnh->rnh_deladdr(ndst, netmask, &rnh->head); | ||||
if (rn == NULL) | if (rn == NULL) | ||||
return (ESRCH); | return (ESRCH); | ||||
rt = RNTORT(rn); | |||||
rt->rte_flags &= ~RTF_UP; | |||||
} | } | ||||
/* Finalize notification */ | /* Finalize notification */ | ||||
rnh->rnh_gen++; | rnh->rnh_gen++; | ||||
rc->rc_cmd = (rnd->rnd_nhop != NULL) ? RTM_CHANGE : RTM_DELETE; | rc->rc_cmd = (rnd->rnd_nhop != NULL) ? RTM_CHANGE : RTM_DELETE; | ||||
rc->rc_rt = rt; | rc->rc_rt = rt; | ||||
rc->rc_nh_old = nh_orig; | rc->rc_nh_old = nh_orig; | ||||
▲ Show 20 Lines • Show All 118 Lines • ▼ Show 20 Lines | rt_checkdelroute(struct radix_node *rn, void *arg) | ||||
int error; | int error; | ||||
di = (struct rt_delinfo *)arg; | di = (struct rt_delinfo *)arg; | ||||
rt = (struct rtentry *)rn; | rt = (struct rtentry *)rn; | ||||
info = &di->info; | info = &di->info; | ||||
info->rti_info[RTAX_DST] = rt_key(rt); | info->rti_info[RTAX_DST] = rt_key(rt); | ||||
info->rti_info[RTAX_NETMASK] = rt_mask(rt); | info->rti_info[RTAX_NETMASK] = rt_mask(rt); | ||||
info->rti_info[RTAX_GATEWAY] = &rt->rt_nhop->gw_sa; | |||||
error = rt_unlinkrte(di->rnh, info, &di->rc); | error = rt_unlinkrte(di->rnh, info, &di->rc); | ||||
/* | /* | ||||
* Add deleted rtentries to the list to GC them | * Add deleted rtentries to the list to GC them | ||||
* after dropping the lock. | * after dropping the lock. | ||||
* | * | ||||
* XXX: Delayed notifications not implemented | * XXX: Delayed notifications not implemented | ||||
* for nexthop updates. | * for nexthop updates. | ||||
*/ | */ | ||||
if (error == 0) { | if ((error == 0) && (di->rc.rc_cmd == RTM_DELETE)) { | ||||
/* Add to the list and return */ | /* 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 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. | ||||
*/ | */ | ||||
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; | |||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
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; | ||||
Show All 9 Lines | rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, bool report) | ||||
/* We might have something to reclaim. */ | /* We might have something to reclaim. */ | ||||
bzero(&di.rc, sizeof(di.rc)); | bzero(&di.rc, sizeof(di.rc)); | ||||
di.rc.rc_cmd = RTM_DELETE; | di.rc.rc_cmd = RTM_DELETE; | ||||
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; | ||||
nh = rt->rt_nhop; | |||||
di.rc.rc_rt = rt; | di.rc.rc_rt = rt; | ||||
di.rc.rc_nh_old = rt->rt_nhop; | di.rc.rc_nh_old = nh; | ||||
rib_notify(rnh, RIB_NOTIFY_DELAYED, &di.rc); | rib_notify(rnh, RIB_NOTIFY_DELAYED, &di.rc); | ||||
/* 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); | ||||
if (report) | if (report) { | ||||
rt_routemsg(RTM_DELETE, rt, rt->rt_nhop->nh_ifp, 0, | #ifdef ROUTE_MPATH | ||||
fibnum); | struct nhgrp_object *nhg; | ||||
struct weightened_nhop *wn; | |||||
uint32_t num_nhops; | |||||
if (NH_IS_NHGRP(nh)) { | |||||
nhg = (struct nhgrp_object *)nh; | |||||
wn = nhgrp_get_nhops(nhg, &num_nhops); | |||||
for (int i = 0; i < num_nhops; i++) | |||||
rt_routemsg(RTM_DELETE, rt, | |||||
wn[i].nh->nh_ifp, 0, fibnum); | |||||
} else | |||||
#endif | |||||
rt_routemsg(RTM_DELETE, rt, nh->nh_ifp, 0, fibnum); | |||||
} | |||||
rtfree(rt); | rtfree(rt); | ||||
} | } | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
} | } | ||||
static void | static void | ||||
rib_notify(struct rib_head *rnh, enum rib_subscription_type type, | rib_notify(struct rib_head *rnh, enum rib_subscription_type type, | ||||
▲ Show 20 Lines • Show All 141 Lines • Show Last 20 Lines |
So in the case of ! ROUTE_MPATH, the call to uma_zfree is suppressed now?