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_RWTUN | |||||
| #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 20 Lines • Show All 64 Lines • ▼ Show 20 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 | |||||
| get_info_weight(const struct rt_addrinfo *info, uint32_t default_weight) | |||||
| { | |||||
| uint32_t weight; | |||||
| if (info->rti_mflags & RTV_WEIGHT) | |||||
| weight = info->rti_rmx->rmx_weight; | |||||
| else | |||||
| weight = default_weight; | |||||
| /* Keep upper 1 byte for adm distance purposes */ | |||||
| if (weight > RT_MAX_WEIGHT) | |||||
| weight = RT_MAX_WEIGHT; | |||||
| return (weight); | |||||
| } | |||||
| static void | |||||
| rt_set_expire_info(struct rtentry *rt, const struct rt_addrinfo *info) | |||||
| { | |||||
| /* 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; | |||||
| } | |||||
| /* | |||||
| * 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 226 Lines • ▼ Show 20 Lines | if (netmask) { | ||||
| bcopy(dst, ndst, dst->sa_len); | bcopy(dst, ndst, dst->sa_len); | ||||
| /* | /* | ||||
| * We use the ifa reference returned by rt_getifa_fib(). | * We use the ifa reference returned by rt_getifa_fib(). | ||||
| * This moved from below so that rnh->rnh_addaddr() can | * This moved from below so that rnh->rnh_addaddr() can | ||||
| * examine the ifa and ifa->ifa_ifp if it so desires. | * examine the ifa and ifa->ifa_ifp if it so desires. | ||||
| */ | */ | ||||
| ifa = info->rti_ifa; | ifa = info->rti_ifa; | ||||
| rt->rt_weight = 1; | rt->rt_weight = get_info_weight(info, RT_DEFAULT_WEIGHT); | ||||
| rt_set_expire_info(rt, info); | |||||
| rt_setmetrics(info, rt); | |||||
| *prt = rt; | *prt = rt; | ||||
| 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; | ||||
| if (error != 0) { | |||||
| nhop_free(nh); | nhop_free(nh); | ||||
| 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 All 35 Lines | |||||
| /* | /* | ||||
| * Conditionally unlinks rtentry matching data inside @info from @rnh. | * Conditionally unlinks rtentry matching data inside @info from @rnh. | ||||
| * Returns 0 on success with operation result stored in @rc. | * Returns 0 on success with operation result stored in @rc. | ||||
| * On error, returns: | * On error, returns: | ||||
| * ESRCH - if prefix was not found, | * ESRCH - if prefix was not found, | ||||
| * EADDRINUSE - if trying to delete higher priority route. | * EADDRINUSE - if trying to delete higher priority route. | ||||
| * ENOENT - if supplied filter function returned 0 (not matched). | * ENOENT - if supplied filter function returned 0 (not matched). | ||||
| */ | */ | ||||
| static int | int | ||||
| rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info, struct rib_cmd_info *rc) | rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info, struct rib_cmd_info *rc) | ||||
| { | { | ||||
| struct rtentry *rt; | struct rtentry *rt; | ||||
| struct nhop_object *nh; | struct nhop_object *nh; | ||||
| struct radix_node *rn; | struct radix_node *rn; | ||||
| 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); | ||||
| 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); | ||||
| nh_orig = rt->rt_nhop; | nh_orig = rt->rt_nhop; | ||||
| if (rnd->rnd_nhop != NULL) { | if (rnd->rnd_nhop != NULL) { | ||||
| /* Changing expiration & nexthop & weight to a new one */ | /* Changing expiration & nexthop & weight to a new one */ | ||||
| rt_setmetrics(info, rt); | rt_set_expire_info(rt, info); | ||||
| rt->rt_nhop = rnd->rnd_nhop; | rt->rt_nhop = rnd->rnd_nhop; | ||||
| rt->rt_weight = rnd->rnd_weight; | rt->rt_weight = rnd->rnd_weight; | ||||
| if (rt->rt_expire > 0) | if (rt->rt_expire > 0) | ||||
| tmproutes_update(rnh, rt); | tmproutes_update(rnh, rt); | ||||
| } else { | } else { | ||||
| /* Route deletion requested. */ | /* Route deletion requested. */ | ||||
| struct sockaddr *ndst, *netmask; | struct sockaddr *ndst, *netmask; | ||||
| struct radix_node *rn; | struct radix_node *rn; | ||||
| ▲ Show 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | rt_checkdelroute(struct radix_node *rn, void *arg) | ||||
| /* | /* | ||||
| * 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); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 201 Lines • Show Last 20 Lines | |||||
So in the case of ! ROUTE_MPATH, the call to uma_zfree is suppressed now?