diff --git a/sys/net/route.c b/sys/net/route.c --- a/sys/net/route.c +++ b/sys/net/route.c @@ -570,7 +570,7 @@ } /* - * Look up rt_addrinfo for a specific fib. + * Fills in rti_ifp and rti_ifa for the provided fib. * * Assume basic consistency checks are executed by callers: * RTAX_DST exists, if RTF_GATEWAY is set, RTAX_GATEWAY exists as well. diff --git a/sys/net/route/nhgrp.c b/sys/net/route/nhgrp.c --- a/sys/net/route/nhgrp.c +++ b/sys/net/route/nhgrp.c @@ -179,6 +179,13 @@ NHOPS_WUNLOCK(ctl); +#if DEBUG_MAX_LEVEL >= LOG_DEBUG2 + { + char nhgrp_buf[NHOP_PRINT_BUFSIZE]; + nhgrp_print_buf(grp_priv->nhg, nhgrp_buf, sizeof(nhgrp_buf)); + FIB_RH_LOG(LOG_DEBUG2, ctl->ctl_rh, "linked %s", nhgrp_buf); + } +#endif consider_resize(ctl, new_num_buckets, new_num_items); return (1); @@ -207,6 +214,14 @@ NHOPS_WUNLOCK(ctl); +#if DEBUG_MAX_LEVEL >= LOG_DEBUG2 + { + char nhgrp_buf[NHOP_PRINT_BUFSIZE]; + nhgrp_print_buf(nhg_priv_ret->nhg, nhgrp_buf, sizeof(nhgrp_buf)); + FIB_RH_LOG(LOG_DEBUG2, ctl->ctl_rh, "unlinked idx#%d %s", idx, + nhgrp_buf); + } +#endif return (nhg_priv_ret); } diff --git a/sys/net/route/nhop.h b/sys/net/route/nhop.h --- a/sys/net/route/nhop.h +++ b/sys/net/route/nhop.h @@ -175,6 +175,29 @@ struct sockaddr_dl; struct rib_head; +/* flags that can be set using nhop_set_rtflags() */ +#define RT_SET_RTFLAGS_MASK (RTF_PROTO1 | RTF_PROTO2 | RTF_PROTO3 | RTF_STATIC) +#define RT_CHANGE_RTFLAGS_MASK RT_SET_RTFLAGS_MASK + +struct nhop_object *nhop_alloc(uint32_t fibnum, int family); +void nhop_copy(struct nhop_object *nh, const struct nhop_object *nh_orig); +struct nhop_object *nhop_get_nhop(struct nhop_object *nh, int *perror); + +void nhop_set_direct_gw(struct nhop_object *nh, struct ifnet *ifp); +bool nhop_set_gw(struct nhop_object *nh, const struct sockaddr *sa, bool is_gw); + + +void nhop_set_mtu(struct nhop_object *nh, uint32_t mtu, bool from_user); +void nhop_set_rtflags(struct nhop_object *nh, int rt_flags); +void nhop_set_pxtype_flag(struct nhop_object *nh, int nh_flag); +void nhop_set_broadcast(struct nhop_object *nh, bool is_broadcast); +void nhop_set_blackhole(struct nhop_object *nh, int blackhole_rt_flag); +void nhop_set_pinned(struct nhop_object *nh, bool is_pinned); +void nhop_set_redirect(struct nhop_object *nh, bool is_redirect); +void nhop_set_type(struct nhop_object *nh, enum nhop_type nh_type); +void nhop_set_src(struct nhop_object *nh, struct ifaddr *ifa); +void nhop_set_transmit_ifp(struct nhop_object *nh, struct ifnet *ifp); + uint32_t nhop_get_idx(const struct nhop_object *nh); enum nhop_type nhop_get_type(const struct nhop_object *nh); int nhop_get_rtflags(const struct nhop_object *nh); diff --git a/sys/net/route/nhop.c b/sys/net/route/nhop.c --- a/sys/net/route/nhop.c +++ b/sys/net/route/nhop.c @@ -304,6 +304,7 @@ nh_priv->nh_idx = idx; nh_priv->nh_control = ctl; + nh_priv->nh_finalized = 1; CHT_SLIST_INSERT_HEAD(&ctl->nh_head, nhops, nh_priv); diff --git a/sys/net/route/nhop_ctl.c b/sys/net/route/nhop_ctl.c --- a/sys/net/route/nhop_ctl.c +++ b/sys/net/route/nhop_ctl.c @@ -85,16 +85,13 @@ static int dump_nhop_entry(struct rib_head *rh, struct nhop_object *nh, struct sysctl_req *w); -static struct nhop_priv *alloc_nhop_structure(void); -static int get_nhop(struct rib_head *rnh, struct rt_addrinfo *info, - struct nhop_priv **pnh_priv); -static int finalize_nhop(struct nh_control *ctl, struct rt_addrinfo *info, - struct nhop_priv *nh_priv); +static int finalize_nhop(struct nh_control *ctl, struct nhop_object *nh); static struct ifnet *get_aifp(const struct nhop_object *nh); static void fill_sdl_from_ifp(struct sockaddr_dl_short *sdl, const struct ifnet *ifp); static void destroy_nhop_epoch(epoch_context_t ctx); -static void destroy_nhop(struct nhop_priv *nh_priv); +static void destroy_nhop(struct nhop_object *nh); +static struct rib_head *nhop_get_rh(const struct nhop_object *nh); _Static_assert(__offsetof(struct nhop_object, nh_ifp) == 32, "nhop_object: wrong nh_ifp offset"); @@ -172,24 +169,8 @@ static void set_nhop_mtu_from_info(struct nhop_object *nh, const struct rt_addrinfo *info) { - - if (info->rti_mflags & RTV_MTU) { - if (info->rti_rmx->rmx_mtu != 0) { - /* - * MTU was explicitly provided by user. - * Keep it. - */ - - nh->nh_priv->rt_flags |= RTF_FIXEDMTU; - } else { - /* - * User explicitly sets MTU to 0. - * Assume rollback to default. - */ - nh->nh_priv->rt_flags &= ~RTF_FIXEDMTU; - } - nh->nh_mtu = info->rti_rmx->rmx_mtu; - } + if (info->rti_mflags & RTV_MTU) + nhop_set_mtu(nh, info->rti_rmx->rmx_mtu, true); } /* @@ -213,9 +194,10 @@ struct sockaddr *gw; gw = info->rti_info[RTAX_GATEWAY]; - KASSERT(gw != NULL, ("gw is NULL")); + MPASS(gw != NULL); + bool is_gw = info->rti_flags & RTF_GATEWAY; - if ((gw->sa_family == AF_LINK) && !(info->rti_flags & RTF_GATEWAY)) { + if ((gw->sa_family == AF_LINK) && !is_gw) { /* * Interface route with interface specified by the interface @@ -233,7 +215,7 @@ sdl->sdl_index); return (EINVAL); } - fill_sdl_from_ifp(&nh->gwl_sa, ifp); + nhop_set_direct_gw(nh, ifp); } else { /* @@ -247,31 +229,12 @@ * In both cases, save the original nexthop to make the callers * happy. */ - if (gw->sa_len > sizeof(struct sockaddr_in6)) { - FIB_NH_LOG(LOG_DEBUG, nh, "nhop SA size too big: AF %d len %u", - gw->sa_family, gw->sa_len); + if (!nhop_set_gw(nh, gw, is_gw)) return (EINVAL); - } - memcpy(&nh->gw_sa, gw, gw->sa_len); } return (0); } -static uint16_t -convert_rt_to_nh_flags(int rt_flags) -{ - uint16_t res; - - res = (rt_flags & RTF_REJECT) ? NHF_REJECT : 0; - res |= (rt_flags & RTF_HOST) ? NHF_HOST : 0; - res |= (rt_flags & RTF_BLACKHOLE) ? NHF_BLACKHOLE : 0; - res |= (rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) ? NHF_REDIRECT : 0; - res |= (rt_flags & RTF_BROADCAST) ? NHF_BROADCAST : 0; - res |= (rt_flags & RTF_GATEWAY) ? NHF_GATEWAY : 0; - - return (res); -} - static void set_nhop_expire_from_info(struct nhop_object *nh, const struct rt_addrinfo *info) { @@ -283,43 +246,6 @@ nhop_set_expire(nh, nh_expire); } -static int -fill_nhop_from_info(struct nhop_priv *nh_priv, struct rt_addrinfo *info) -{ - int error, rt_flags; - struct nhop_object *nh; - - nh = nh_priv->nh; - - rt_flags = info->rti_flags & NHOP_RT_FLAG_MASK; - - nh->nh_priv->rt_flags = rt_flags; - nh_priv->nh_upper_family = info->rti_info[RTAX_DST]->sa_family; - nh_priv->nh_type = 0; // hook responsibility to set nhop type - nh->nh_flags = convert_rt_to_nh_flags(rt_flags); - - set_nhop_mtu_from_info(nh, info); - if ((error = set_nhop_gw_from_info(nh, info)) != 0) - return (error); - if (nh->gw_sa.sa_family == AF_LINK) - nh_priv->nh_neigh_family = nh_priv->nh_upper_family; - else - nh_priv->nh_neigh_family = nh->gw_sa.sa_family; - set_nhop_expire_from_info(nh, info); - - nh->nh_ifp = (info->rti_ifp != NULL) ? info->rti_ifp : info->rti_ifa->ifa_ifp; - nh->nh_ifa = info->rti_ifa; - /* depends on the gateway */ - nh->nh_aifp = get_aifp(nh); - - /* - * Note some of the remaining data is set by the - * per-address-family pre-add hook. - */ - - return (0); -} - /* * Creates a new nexthop based on the information in @info. * @@ -331,81 +257,94 @@ nhop_create_from_info(struct rib_head *rnh, struct rt_addrinfo *info, struct nhop_object **nh_ret) { - struct nhop_priv *nh_priv; int error; NET_EPOCH_ASSERT(); + MPASS(info->rti_ifa != NULL); + MPASS(info->rti_ifp != NULL); + if (info->rti_info[RTAX_GATEWAY] == NULL) { FIB_RH_LOG(LOG_DEBUG, rnh, "error: empty gateway"); return (EINVAL); } - nh_priv = alloc_nhop_structure(); + struct nhop_object *nh = nhop_alloc(rnh->rib_fibnum, rnh->rib_family); + if (nh == NULL) + return (ENOMEM); - error = fill_nhop_from_info(nh_priv, info); - if (error != 0) { - uma_zfree(nhops_zone, nh_priv->nh); + if ((error = set_nhop_gw_from_info(nh, info)) != 0) { + nhop_free(nh); return (error); } + nhop_set_transmit_ifp(nh, info->rti_ifp); - error = get_nhop(rnh, info, &nh_priv); - if (error == 0) - *nh_ret = nh_priv->nh; + nhop_set_blackhole(nh, info->rti_flags & (RTF_BLACKHOLE | RTF_REJECT)); + + error = rnh->rnh_set_nh_pfxflags(rnh->rib_fibnum, info->rti_info[RTAX_DST], + info->rti_info[RTAX_NETMASK], nh); + + nhop_set_redirect(nh, info->rti_flags & RTF_DYNAMIC); + nhop_set_pinned(nh, info->rti_flags & RTF_PINNED); + set_nhop_expire_from_info(nh, info); + nhop_set_rtflags(nh, info->rti_flags); + + set_nhop_mtu_from_info(nh, info); + nhop_set_src(nh, info->rti_ifa); + + /* + * The remaining fields are either set from nh_preadd hook + * or are computed from the provided data + */ + *nh_ret = nhop_get_nhop(nh, &error); return (error); } /* - * Gets linked nhop using the provided @pnh_priv nexhop data. + * Gets linked nhop using the provided @nh nexhop data. * If linked nhop is found, returns it, freeing the provided one. * If there is no such nexthop, attaches the remaining data to the * provided nexthop and links it. * - * Returns 0 on success, storing referenced nexthop in @pnh_priv. + * Returns 0 on success, storing referenced nexthop in @pnh. * Otherwise, errno is returned. */ -static int -get_nhop(struct rib_head *rnh, struct rt_addrinfo *info, - struct nhop_priv **pnh_priv) +struct nhop_object * +nhop_get_nhop(struct nhop_object *nh, int *perror) { - const struct sockaddr *dst, *netmask; - struct nhop_priv *nh_priv, *tmp_priv; + struct nhop_priv *tmp_priv; int error; - nh_priv = *pnh_priv; + nh->nh_aifp = get_aifp(nh); - /* Give the protocols chance to augment the request data */ - dst = info->rti_info[RTAX_DST]; - netmask = info->rti_info[RTAX_NETMASK]; + struct rib_head *rnh = nhop_get_rh(nh); - error = rnh->rnh_preadd(rnh->rib_fibnum, dst, netmask, nh_priv->nh); + /* Give the protocols chance to augment nexthop properties */ + error = rnh->rnh_augment_nh(rnh->rib_fibnum, nh); if (error != 0) { - uma_zfree(nhops_zone, nh_priv->nh); - return (error); + nhop_free(nh); + *perror = error; + return (NULL); } - tmp_priv = find_nhop(rnh->nh_control, nh_priv); + tmp_priv = find_nhop(rnh->nh_control, nh->nh_priv); if (tmp_priv != NULL) { - uma_zfree(nhops_zone, nh_priv->nh); - *pnh_priv = tmp_priv; - return (0); + nhop_free(nh); + *perror = 0; + return (tmp_priv->nh); } /* * Existing nexthop not found, need to create new one. - * Note: multiple simultaneous get_nhop() requests + * Note: multiple simultaneous requests * can result in multiple equal nexhops existing in the * nexthop table. This is not a not a problem until the * relative number of such nexthops is significant, which * is extremely unlikely. */ - - error = finalize_nhop(rnh->nh_control, info, nh_priv); - if (error != 0) - return (error); - - return (0); + *perror = finalize_nhop(rnh->nh_control, nh); + return (*perror == 0 ? nh : NULL); } /* @@ -413,28 +352,26 @@ * This is a helper function to support route changes. * * It limits the changes that can be done to the route to the following: - * 1) all combination of gateway changes (gw, interface, blackhole/reject) - * 2) route flags (FLAG[123],STATIC,BLACKHOLE,REJECT) + * 1) all combination of gateway changes + * 2) route flags (FLAG[123],STATIC) * 3) route MTU * * Returns: - * 0 on success + * 0 on success, errno otherwise */ static int alter_nhop_from_info(struct nhop_object *nh, struct rt_addrinfo *info) { - struct nhop_priv *nh_priv = nh->nh_priv; struct sockaddr *info_gw; int error; /* Update MTU if set in the request*/ set_nhop_mtu_from_info(nh, info); - /* XXX: allow only one of BLACKHOLE,REJECT,GATEWAY */ - - /* Allow some flags (FLAG1,STATIC,BLACKHOLE,REJECT) to be toggled on change. */ - nh_priv->rt_flags &= ~RTF_FMASK; - nh_priv->rt_flags |= info->rti_flags & RTF_FMASK; + /* Only RTF_FLAG[123] and RTF_STATIC */ + uint32_t rt_flags = nhop_get_rtflags(nh) & ~RT_CHANGE_RTFLAGS_MASK; + rt_flags |= info->rti_flags & RT_CHANGE_RTFLAGS_MASK; + nhop_set_rtflags(nh, rt_flags); /* Consider gateway change */ info_gw = info->rti_info[RTAX_GATEWAY]; @@ -442,22 +379,12 @@ error = set_nhop_gw_from_info(nh, info); if (error != 0) return (error); - if (nh->gw_sa.sa_family == AF_LINK) - nh_priv->nh_neigh_family = nh_priv->nh_upper_family; - else - nh_priv->nh_neigh_family = nh->gw_sa.sa_family; - /* Update RTF_GATEWAY flag status */ - nh_priv->rt_flags &= ~RTF_GATEWAY; - nh_priv->rt_flags |= (RTF_GATEWAY & info->rti_flags); } - /* Update datapath flags */ - nh->nh_flags = convert_rt_to_nh_flags(nh_priv->rt_flags); if (info->rti_ifa != NULL) - nh->nh_ifa = info->rti_ifa; + nhop_set_src(nh, info->rti_ifa); if (info->rti_ifp != NULL) - nh->nh_ifp = info->rti_ifp; - nh->nh_aifp = get_aifp(nh); + nhop_set_transmit_ifp(nh, info->rti_ifp); return (0); } @@ -475,64 +402,28 @@ nhop_create_from_nhop(struct rib_head *rnh, const struct nhop_object *nh_orig, struct rt_addrinfo *info, struct nhop_object **pnh) { - struct nhop_priv *nh_priv; struct nhop_object *nh; int error; NET_EPOCH_ASSERT(); - nh_priv = alloc_nhop_structure(); - nh = nh_priv->nh; - - /* Start with copying data from original nexthop */ - nh_priv->nh_upper_family = nh_orig->nh_priv->nh_upper_family; - nh_priv->nh_neigh_family = nh_orig->nh_priv->nh_neigh_family; - nh_priv->rt_flags = nh_orig->nh_priv->rt_flags; - nh_priv->nh_type = nh_orig->nh_priv->nh_type; - nh_priv->nh_fibnum = nh_orig->nh_priv->nh_fibnum; + nh = nhop_alloc(rnh->rib_fibnum, rnh->rib_family); + if (nh == NULL) + return (ENOMEM); - nh->nh_ifp = nh_orig->nh_ifp; - nh->nh_ifa = nh_orig->nh_ifa; - nh->nh_aifp = nh_orig->nh_aifp; - nh->nh_mtu = nh_orig->nh_mtu; - nh->nh_flags = nh_orig->nh_flags; - memcpy(&nh->gw_sa, &nh_orig->gw_sa, nh_orig->gw_sa.sa_len); + nhop_copy(nh, nh_orig); error = alter_nhop_from_info(nh, info); if (error != 0) { - uma_zfree(nhops_zone, nh_priv->nh); + nhop_free(nh); return (error); } - error = get_nhop(rnh, info, &nh_priv); - if (error == 0) - *pnh = nh_priv->nh; + *pnh = nhop_get_nhop(nh, &error); return (error); } -/* - * Allocates memory for public/private nexthop structures. - * - * Returns pointer to nhop_priv or NULL. - */ -static struct nhop_priv * -alloc_nhop_structure(void) -{ - struct nhop_object *nh; - struct nhop_priv *nh_priv; - - nh = (struct nhop_object *)uma_zalloc(nhops_zone, M_NOWAIT | M_ZERO); - if (nh == NULL) - return (NULL); - nh_priv = (struct nhop_priv *)((char *)nh + NHOP_OBJECT_ALIGNED_SIZE); - - nh->nh_priv = nh_priv; - nh_priv->nh = nh; - - return (nh_priv); -} - static bool reference_nhop_deps(struct nhop_object *nh) { @@ -543,7 +434,8 @@ ifa_free(nh->nh_ifa); return (false); } - FIB_NH_LOG(LOG_DEBUG, nh, "AIFP: %p nh_ifp %p", nh->nh_aifp, nh->nh_ifp); + FIB_NH_LOG(LOG_DEBUG2, nh, "nh_aifp: %s nh_ifp %s", + if_name(nh->nh_aifp), if_name(nh->nh_ifp)); if (!if_try_ref(nh->nh_ifp)) { ifa_free(nh->nh_ifa); if_rele(nh->nh_aifp); @@ -560,15 +452,13 @@ * errno otherwise. @nh_priv is freed in case of error. */ static int -finalize_nhop(struct nh_control *ctl, struct rt_addrinfo *info, - struct nhop_priv *nh_priv) +finalize_nhop(struct nh_control *ctl, struct nhop_object *nh) { - struct nhop_object *nh = nh_priv->nh; /* Allocate per-cpu packet counter */ nh->nh_pksent = counter_u64_alloc(M_NOWAIT); if (nh->nh_pksent == NULL) { - uma_zfree(nhops_zone, nh); + nhop_free(nh); RTSTAT_INC(rts_nh_alloc_failure); FIB_NH_LOG(LOG_WARNING, nh, "counter_u64_alloc() failed"); return (ENOMEM); @@ -576,23 +466,21 @@ if (!reference_nhop_deps(nh)) { counter_u64_free(nh->nh_pksent); - uma_zfree(nhops_zone, nh); + nhop_free(nh); RTSTAT_INC(rts_nh_alloc_failure); FIB_NH_LOG(LOG_WARNING, nh, "interface reference failed"); return (EAGAIN); } /* Save vnet to ease destruction */ - nh_priv->nh_vnet = curvnet; - - refcount_init(&nh_priv->nh_refcnt, 1); + nh->nh_priv->nh_vnet = curvnet; /* Please see nhop_free() comments on the initial value */ - refcount_init(&nh_priv->nh_linked, 2); + refcount_init(&nh->nh_priv->nh_linked, 2); - nh_priv->nh_fibnum = ctl->ctl_rh->rib_fibnum; + nh->nh_priv->nh_fibnum = ctl->ctl_rh->rib_fibnum; - if (link_nhop(ctl, nh_priv) == 0) { + if (link_nhop(ctl, nh->nh_priv) == 0) { /* * Adding nexthop to the datastructures * failed. Call destructor w/o waiting for @@ -602,7 +490,7 @@ char nhbuf[NHOP_PRINT_BUFSIZE]; FIB_NH_LOG(LOG_WARNING, nh, "failed to link %s", nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); - destroy_nhop(nh_priv); + destroy_nhop(nh); return (ENOBUFS); } @@ -616,12 +504,8 @@ } static void -destroy_nhop(struct nhop_priv *nh_priv) +destroy_nhop(struct nhop_object *nh) { - struct nhop_object *nh; - - nh = nh_priv->nh; - if_rele(nh->nh_ifp); if_rele(nh->nh_aifp); ifa_free(nh->nh_ifa); @@ -640,7 +524,7 @@ nh_priv = __containerof(ctx, struct nhop_priv, nh_epoch_ctx); - destroy_nhop(nh_priv); + destroy_nhop(nh_priv->nh); } void @@ -669,6 +553,12 @@ if (!refcount_release(&nh_priv->nh_refcnt)) return; + /* allows to use nhop_free() during nhop init */ + if (__predict_false(nh_priv->nh_finalized == 0)) { + uma_zfree(nhops_zone, nh); + return; + } + #if DEBUG_MAX_LEVEL >= LOG_DEBUG char nhbuf[NHOP_PRINT_BUFSIZE]; FIB_NH_LOG(LOG_DEBUG, nh, "deleting %s", nhop_print_buf(nh, nhbuf, sizeof(nhbuf))); @@ -738,7 +628,144 @@ #endif } -/* Helper functions */ +/* Nhop-related methods */ + +/* + * Allocates an empty unlinked nhop object. + * Returns object pointer or NULL on failure + */ +struct nhop_object * +nhop_alloc(uint32_t fibnum, int family) +{ + struct nhop_object *nh; + struct nhop_priv *nh_priv; + + nh = (struct nhop_object *)uma_zalloc(nhops_zone, M_NOWAIT | M_ZERO); + if (__predict_false(nh == NULL)) + return (NULL); + + nh_priv = (struct nhop_priv *)((char *)nh + NHOP_OBJECT_ALIGNED_SIZE); + nh->nh_priv = nh_priv; + nh_priv->nh = nh; + + nh_priv->nh_upper_family = family; + nh_priv->nh_fibnum = fibnum; + + /* Setup refcount early to allow nhop_free() to work */ + refcount_init(&nh_priv->nh_refcnt, 1); + + return (nh); +} + +void +nhop_copy(struct nhop_object *nh, const struct nhop_object *nh_orig) +{ + struct nhop_priv *nh_priv = nh->nh_priv; + + nh->nh_flags = nh_orig->nh_flags; + nh->nh_mtu = nh_orig->nh_mtu; + memcpy(&nh->gw_sa, &nh_orig->gw_sa, nh_orig->gw_sa.sa_len); + nh->nh_ifp = nh_orig->nh_ifp; + nh->nh_ifa = nh_orig->nh_ifa; + nh->nh_aifp = nh_orig->nh_aifp; + + nh_priv->nh_upper_family = nh_orig->nh_priv->nh_upper_family; + nh_priv->nh_neigh_family = nh_orig->nh_priv->nh_neigh_family; + nh_priv->nh_type = nh_orig->nh_priv->nh_type; + nh_priv->rt_flags = nh_orig->nh_priv->rt_flags; + nh_priv->nh_fibnum = nh_orig->nh_priv->nh_fibnum; +} + +void +nhop_set_direct_gw(struct nhop_object *nh, struct ifnet *ifp) +{ + nh->nh_flags &= ~NHF_GATEWAY; + nh->nh_priv->rt_flags &= ~RTF_GATEWAY; + nh->nh_priv->nh_neigh_family = nh->nh_priv->nh_upper_family; + + fill_sdl_from_ifp(&nh->gwl_sa, ifp); + memset(&nh->gw_buf[nh->gw_sa.sa_len], 0, sizeof(nh->gw_buf) - nh->gw_sa.sa_len); +} + +/* + * Sets gateway for the nexthop. + * It can be "normal" gateway with is_gw set or a special form of + * adding interface route, refering to it by specifying local interface + * address. In that case is_gw is set to false. + */ +bool +nhop_set_gw(struct nhop_object *nh, const struct sockaddr *gw, bool is_gw) +{ + if (gw->sa_len > sizeof(nh->gw_buf)) { + FIB_NH_LOG(LOG_DEBUG, nh, "nhop SA size too big: AF %d len %u", + gw->sa_family, gw->sa_len); + return (false); + } + memcpy(&nh->gw_sa, gw, gw->sa_len); + memset(&nh->gw_buf[gw->sa_len], 0, sizeof(nh->gw_buf) - gw->sa_len); + + if (is_gw) { + nh->nh_flags |= NHF_GATEWAY; + nh->nh_priv->rt_flags |= RTF_GATEWAY; + nh->nh_priv->nh_neigh_family = gw->sa_family; + } else { + nh->nh_flags &= ~NHF_GATEWAY; + nh->nh_priv->rt_flags &= ~RTF_GATEWAY; + nh->nh_priv->nh_neigh_family = nh->nh_priv->nh_upper_family; + } + + return (true); +} + +void +nhop_set_broadcast(struct nhop_object *nh, bool is_broadcast) +{ + if (is_broadcast) { + nh->nh_flags |= NHF_BROADCAST; + nh->nh_priv->rt_flags |= RTF_BROADCAST; + } else { + nh->nh_flags &= ~NHF_BROADCAST; + nh->nh_priv->rt_flags &= ~RTF_BROADCAST; + } +} + +void +nhop_set_blackhole(struct nhop_object *nh, int blackhole_rt_flag) +{ + nh->nh_flags &= ~(NHF_BLACKHOLE | NHF_REJECT); + nh->nh_priv->rt_flags &= ~(RTF_BLACKHOLE | RTF_REJECT); + switch (blackhole_rt_flag) { + case RTF_BLACKHOLE: + nh->nh_flags |= NHF_BLACKHOLE; + nh->nh_priv->rt_flags |= RTF_BLACKHOLE; + break; + case RTF_REJECT: + nh->nh_flags |= NHF_REJECT; + nh->nh_priv->rt_flags |= RTF_REJECT; + break; + } +} + +void +nhop_set_redirect(struct nhop_object *nh, bool is_redirect) +{ + if (is_redirect) { + nh->nh_priv->rt_flags |= RTF_DYNAMIC; + nh->nh_flags |= NHF_REDIRECT; + } else { + nh->nh_priv->rt_flags &= ~RTF_DYNAMIC; + nh->nh_flags &= ~NHF_REDIRECT; + } +} + +void +nhop_set_pinned(struct nhop_object *nh, bool is_pinned) +{ + if (is_pinned) + nh->nh_priv->rt_flags |= RTF_PINNED; + else + nh->nh_priv->rt_flags &= ~RTF_PINNED; +} uint32_t nhop_get_idx(const struct nhop_object *nh) @@ -768,13 +795,65 @@ return (nh->nh_priv->rt_flags); } +/* + * Sets generic rtflags that are not covered by other functions. + */ void nhop_set_rtflags(struct nhop_object *nh, int rt_flags) { + nh->nh_priv->rt_flags &= ~RT_SET_RTFLAGS_MASK; + nh->nh_priv->rt_flags |= (rt_flags & RT_SET_RTFLAGS_MASK); +} - nh->nh_priv->rt_flags = rt_flags; +/* + * Sets flags that are specific to the prefix (NHF_HOST or NHF_DEFAULT). + */ +void +nhop_set_pxtype_flag(struct nhop_object *nh, int nh_flag) +{ + if (nh_flag == NHF_HOST) { + nh->nh_flags |= NHF_HOST; + nh->nh_flags &= ~NHF_DEFAULT; + nh->nh_priv->rt_flags |= RTF_HOST; + } else if (nh_flag == NHF_DEFAULT) { + nh->nh_flags |= NHF_DEFAULT; + nh->nh_flags &= ~NHF_HOST; + nh->nh_priv->rt_flags &= ~RTF_HOST; + } else { + nh->nh_flags &= ~(NHF_HOST | NHF_DEFAULT); + nh->nh_priv->rt_flags &= ~RTF_HOST; + } +} + +/* + * Sets nhop MTU. Sets RTF_FIXEDMTU if mtu is explicitly + * specified by userland. + */ +void +nhop_set_mtu(struct nhop_object *nh, uint32_t mtu, bool from_user) +{ + if (from_user) { + if (mtu != 0) + nh->nh_priv->rt_flags |= RTF_FIXEDMTU; + else + nh->nh_priv->rt_flags &= ~RTF_FIXEDMTU; + } + nh->nh_mtu = mtu; } +void +nhop_set_src(struct nhop_object *nh, struct ifaddr *ifa) +{ + nh->nh_ifa = ifa; +} + +void +nhop_set_transmit_ifp(struct nhop_object *nh, struct ifnet *ifp) +{ + nh->nh_ifp = ifp; +} + + struct vnet * nhop_get_vnet(const struct nhop_object *nh) { @@ -827,6 +906,15 @@ nh->nh_priv->nh_expire = expire; } +static struct rib_head * +nhop_get_rh(const struct nhop_object *nh) +{ + uint32_t fibnum = nhop_get_fibnum(nh); + int family = nhop_get_neigh_family(nh); + + return (rt_tables_get_rnh(fibnum, family)); +} + void nhops_update_ifmtu(struct rib_head *rh, struct ifnet *ifp, uint32_t mtu) { diff --git a/sys/net/route/nhop_var.h b/sys/net/route/nhop_var.h --- a/sys/net/route/nhop_var.h +++ b/sys/net/route/nhop_var.h @@ -85,6 +85,7 @@ void *cb_func; /* function handling additional rewrite caps */ u_int nh_refcnt; /* number of references, refcount(9) */ u_int nh_linked; /* refcount(9), == 2 if linked to the list */ + int nh_finalized; /* non-zero if finalized() was called */ struct nhop_object *nh; /* backreference to the dataplane nhop */ struct nh_control *nh_control; /* backreference to the rnh */ struct nhop_priv *nh_next; /* hash table membership */ diff --git a/sys/net/route/route_var.h b/sys/net/route/route_var.h --- a/sys/net/route/route_var.h +++ b/sys/net/route/route_var.h @@ -48,8 +48,11 @@ #endif struct nh_control; -typedef int rnh_preadd_entry_f_t(u_int fibnum, const struct sockaddr *addr, +/* Sets prefix-specific nexthop flags (NHF_DEFAULT, RTF/NHF_HOST, RTF_BROADCAST,..) */ +typedef int rnh_set_nh_pfxflags_f_t(u_int fibnum, const struct sockaddr *addr, const struct sockaddr *mask, struct nhop_object *nh); +/* Fills in family-specific details that are not yet set up (mtu, nhop type, ..) */ +typedef int rnh_augment_nh_f_t(u_int fibnum, struct nhop_object *nh); struct rib_head { struct radix_head head; @@ -59,7 +62,7 @@ rn_lookup_f_t *rnh_lookup; /* exact match for sockaddr */ rn_walktree_t *rnh_walktree; /* traverse tree */ rn_walktree_from_t *rnh_walktree_from; /* traverse tree below a */ - rnh_preadd_entry_f_t *rnh_preadd; /* hook to alter record prior to insertion */ + rnh_set_nh_pfxflags_f_t *rnh_set_nh_pfxflags; /* hook to alter record prior to insertion */ rt_gen_t rnh_gen; /* datapath generation counter */ int rnh_multipath; /* multipath capable ? */ struct radix_node rnh_nodes[3]; /* empty tree for common case */ @@ -76,6 +79,7 @@ uint32_t rib_algo_fixed:1;/* fixed algorithm */ uint32_t rib_algo_init:1;/* algo init done */ struct nh_control *nh_control; /* nexthop subsystem data */ + rnh_augment_nh_f_t *rnh_augment_nh;/* hook to alter nexthop prior to insertion */ CK_STAILQ_HEAD(, rib_subscription) rnh_subscribers;/* notification subscribers */ }; @@ -204,11 +208,6 @@ * RTF_PINNED, RTF_REJECT, RTF_BLACKHOLE, RTF_BROADCAST */ -/* Nexthop rt flags mask */ -#define NHOP_RT_FLAG_MASK (RTF_GATEWAY | RTF_HOST | RTF_REJECT | RTF_DYNAMIC | \ - RTF_MODIFIED | RTF_STATIC | RTF_BLACKHOLE | RTF_PROTO1 | RTF_PROTO2 | \ - RTF_PROTO3 | RTF_FIXEDMTU | RTF_PINNED | RTF_BROADCAST) - /* rtentry rt flag mask */ #define RTE_RT_FLAG_MASK (RTF_UP | RTF_HOST) @@ -250,8 +249,6 @@ void nhop_ref_any(struct nhop_object *nh); void nhop_free_any(struct nhop_object *nh); -void nhop_set_type(struct nhop_object *nh, enum nhop_type nh_type); -void nhop_set_rtflags(struct nhop_object *nh, int rt_flags); int nhop_create_from_info(struct rib_head *rnh, struct rt_addrinfo *info, struct nhop_object **nh_ret); diff --git a/sys/netinet/in_rmx.c b/sys/netinet/in_rmx.c --- a/sys/netinet/in_rmx.c +++ b/sys/netinet/in_rmx.c @@ -52,18 +52,15 @@ #include static int -rib4_preadd(u_int fibnum, const struct sockaddr *addr, const struct sockaddr *mask, +rib4_set_nh_pfxflags(u_int fibnum, const struct sockaddr *addr, const struct sockaddr *mask, struct nhop_object *nh) { const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; - uint16_t nh_type; - int rt_flags; - - /* XXX: RTF_LOCAL && RTF_MULTICAST */ - - rt_flags = nhop_get_rtflags(nh); + const struct sockaddr_in *mask4 = (const struct sockaddr_in *)mask; + bool is_broadcast = false; - if (rt_flags & RTF_HOST) { + if (mask == NULL) { + nhop_set_pxtype_flag(nh, NHF_HOST); /* * Backward compatibility: * if the destination is broadcast, @@ -76,13 +73,21 @@ * add these routes to support some cases with active-active * load balancing. Given that, retain this support. */ - if (in_broadcast(addr4->sin_addr, nh->nh_ifp)) { - rt_flags |= RTF_BROADCAST; - nhop_set_rtflags(nh, rt_flags); - nh->nh_flags |= NHF_BROADCAST; - } - } + if (in_broadcast(addr4->sin_addr, nh->nh_ifp)) + is_broadcast = true; + } else if (mask4->sin_addr.s_addr == 0) + nhop_set_pxtype_flag(nh, NHF_DEFAULT); + else + nhop_set_pxtype_flag(nh, 0); + + nhop_set_broadcast(nh, is_broadcast); + + return (0); +} +static int +rib4_augment_nh(u_int fibnum, struct nhop_object *nh) +{ /* * Check route MTU: * inherit interface MTU if not set or @@ -93,14 +98,9 @@ } else if (nh->nh_mtu > nh->nh_ifp->if_mtu) nh->nh_mtu = nh->nh_ifp->if_mtu; - /* Ensure that default route nhop has special flag */ - const struct sockaddr_in *mask4 = (const struct sockaddr_in *)mask; - if ((rt_flags & RTF_HOST) == 0 && mask4 != NULL && - mask4->sin_addr.s_addr == 0) - nh->nh_flags |= NHF_DEFAULT; - /* Set nhop type to basic per-AF nhop */ if (nhop_get_type(nh) == 0) { + uint16_t nh_type; if (nh->nh_flags & NHF_GATEWAY) nh_type = NH_TYPE_IPV4_ETHER_NHOP; else @@ -124,7 +124,8 @@ if (rh == NULL) return (NULL); - rh->rnh_preadd = rib4_preadd; + rh->rnh_set_nh_pfxflags = rib4_set_nh_pfxflags; + rh->rnh_augment_nh = rib4_augment_nh; return (rh); } diff --git a/sys/netinet6/in6_rmx.c b/sys/netinet6/in6_rmx.c --- a/sys/netinet6/in6_rmx.c +++ b/sys/netinet6/in6_rmx.c @@ -94,13 +94,24 @@ #include static int -rib6_preadd(u_int fibnum, const struct sockaddr *addr, const struct sockaddr *mask, +rib6_set_nh_pfxflags(u_int fibnum, const struct sockaddr *addr, const struct sockaddr *mask, struct nhop_object *nh) { - uint16_t nh_type; + const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *)mask; + + if (mask6 == NULL) + nhop_set_pxtype_flag(nh, NHF_HOST); + else if (IN6_IS_ADDR_UNSPECIFIED(&mask6->sin6_addr)) + nhop_set_pxtype_flag(nh, NHF_DEFAULT); + else + nhop_set_pxtype_flag(nh, 0); - /* XXX: RTF_LOCAL */ + return (0); +} +static int +rib6_augment_nh(u_int fibnum, struct nhop_object *nh) +{ /* * Check route MTU: * inherit interface MTU if not set or @@ -111,14 +122,9 @@ } else if (nh->nh_mtu > IN6_LINKMTU(nh->nh_ifp)) nh->nh_mtu = IN6_LINKMTU(nh->nh_ifp); - /* Ensure that default route nhop has special flag */ - const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *)mask; - if ((nhop_get_rtflags(nh) & RTF_HOST) == 0 && mask6 != NULL && - IN6_IS_ADDR_UNSPECIFIED(&mask6->sin6_addr)) - nh->nh_flags |= NHF_DEFAULT; - /* Set nexthop type */ if (nhop_get_type(nh) == 0) { + uint16_t nh_type; if (nh->nh_flags & NHF_GATEWAY) nh_type = NH_TYPE_IPV6_ETHER_NHOP; else @@ -145,7 +151,8 @@ if (rh == NULL) return (NULL); - rh->rnh_preadd = rib6_preadd; + rh->rnh_set_nh_pfxflags = rib6_set_nh_pfxflags; + rh->rnh_augment_nh = rib6_augment_nh; rs = rib_subscribe_internal(rh, nd6_subscription_cb, NULL, RIB_NOTIFY_IMMEDIATE, true); diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -2042,6 +2042,7 @@ struct rt_addrinfo info = { .rti_ifa = ifa, + .rti_ifp = ifp, .rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST), .rti_info = { [RTAX_DST] = (struct sockaddr *)dst,