The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/net/route/nhop_ctl.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2020 Alexander V. Chernikov
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD$");
   30 #include "opt_inet.h"
   31 #include "opt_inet6.h"
   32 #include "opt_route.h"
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/lock.h>
   37 #include <sys/rwlock.h>
   38 #include <sys/malloc.h>
   39 #include <sys/socket.h>
   40 #include <sys/sysctl.h>
   41 #include <sys/kernel.h>
   42 #include <sys/epoch.h>
   43 
   44 #include <net/if.h>
   45 #include <net/if_var.h>
   46 #include <net/if_private.h>
   47 #include <net/if_dl.h>
   48 #include <net/route.h>
   49 #include <net/route/route_ctl.h>
   50 #include <net/route/route_var.h>
   51 #include <net/route/nhop_utils.h>
   52 #include <net/route/nhop.h>
   53 #include <net/route/nhop_var.h>
   54 #include <net/vnet.h>
   55 
   56 #define DEBUG_MOD_NAME  nhop_ctl
   57 #define DEBUG_MAX_LEVEL LOG_DEBUG
   58 #include <net/route/route_debug.h>
   59 _DECLARE_DEBUG(LOG_INFO);
   60 
   61 /*
   62  * This file contains core functionality for the nexthop ("nhop") route subsystem.
   63  * The business logic needed to create nexhop objects is implemented here.
   64  *
   65  * Nexthops in the original sense are the objects containing all the necessary
   66  * information to forward the packet to the selected destination.
   67  * In particular, nexthop is defined by a combination of
   68  *  ifp, ifa, aifp, mtu, gw addr(if set), nh_type, nh_upper_family, mask of rt_flags and
   69  *    NHF_DEFAULT
   70  *
   71  * Additionally, each nexthop gets assigned its unique index (nexthop index).
   72  * It serves two purposes: first one is to ease the ability of userland programs to
   73  *  reference nexthops by their index. The second one allows lookup algorithms to
   74  *  to store index instead of pointer (2 bytes vs 8) as a lookup result.
   75  * All nexthops are stored in the resizable hash table.
   76  *
   77  * Basically, this file revolves around supporting 3 functions:
   78  * 1) nhop_create_from_info / nhop_create_from_nhop, which contains all
   79  *  business logic on filling the nexthop fields based on the provided request.
   80  * 2) nhop_get(), which gets a usable referenced nexthops.
   81  *
   82  * Conventions:
   83  * 1) non-exported functions start with verb
   84  * 2) exported function starts with the subsystem prefix: "nhop"
   85  */
   86 
   87 static int dump_nhop_entry(struct rib_head *rh, struct nhop_object *nh, struct sysctl_req *w);
   88 
   89 static int finalize_nhop(struct nh_control *ctl, struct nhop_object *nh, bool link);
   90 static struct ifnet *get_aifp(const struct nhop_object *nh);
   91 static void fill_sdl_from_ifp(struct sockaddr_dl_short *sdl, const struct ifnet *ifp);
   92 
   93 static void destroy_nhop_epoch(epoch_context_t ctx);
   94 static void destroy_nhop(struct nhop_object *nh);
   95 
   96 _Static_assert(__offsetof(struct nhop_object, nh_ifp) == 32,
   97     "nhop_object: wrong nh_ifp offset");
   98 _Static_assert(sizeof(struct nhop_object) <= 128,
   99     "nhop_object: size exceeds 128 bytes");
  100 
  101 static uma_zone_t nhops_zone;   /* Global zone for each and every nexthop */
  102 
  103 #define NHOP_OBJECT_ALIGNED_SIZE        roundup2(sizeof(struct nhop_object), \
  104                                                         2 * CACHE_LINE_SIZE)
  105 #define NHOP_PRIV_ALIGNED_SIZE          roundup2(sizeof(struct nhop_priv), \
  106                                                         2 * CACHE_LINE_SIZE)
  107 void
  108 nhops_init(void)
  109 {
  110 
  111         nhops_zone = uma_zcreate("routing nhops",
  112             NHOP_OBJECT_ALIGNED_SIZE + NHOP_PRIV_ALIGNED_SIZE,
  113             NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
  114 }
  115 
  116 /*
  117  * Fetches the interface of source address used by the route.
  118  * In all cases except interface-address-route it would be the
  119  * same as the transmit interfaces.
  120  * However, for the interface address this function will return
  121  * this interface ifp instead of loopback. This is needed to support
  122  * link-local IPv6 loopback communications.
  123  *
  124  * Returns found ifp.
  125  */
  126 static struct ifnet *
  127 get_aifp(const struct nhop_object *nh)
  128 {
  129         struct ifnet *aifp = NULL;
  130 
  131         /*
  132          * Adjust the "outgoing" interface.  If we're going to loop
  133          * the packet back to ourselves, the ifp would be the loopback
  134          * interface. However, we'd rather know the interface associated
  135          * to the destination address (which should probably be one of
  136          * our own addresses).
  137          */
  138         if ((nh->nh_ifp->if_flags & IFF_LOOPBACK) &&
  139                         nh->gw_sa.sa_family == AF_LINK) {
  140                 aifp = ifnet_byindex(nh->gwl_sa.sdl_index);
  141                 if (aifp == NULL) {
  142                         FIB_NH_LOG(LOG_WARNING, nh, "unable to get aifp for %s index %d",
  143                                 if_name(nh->nh_ifp), nh->gwl_sa.sdl_index);
  144                 }
  145         }
  146 
  147         if (aifp == NULL)
  148                 aifp = nh->nh_ifp;
  149 
  150         return (aifp);
  151 }
  152 
  153 int
  154 cmp_priv(const struct nhop_priv *_one, const struct nhop_priv *_two)
  155 {
  156 
  157         if (memcmp(_one->nh, _two->nh, NHOP_END_CMP) != 0)
  158                 return (0);
  159 
  160         if (memcmp(_one, _two, NH_PRIV_END_CMP) != 0)
  161                 return (0);
  162 
  163         return (1);
  164 }
  165 
  166 /*
  167  * Conditionally sets @nh mtu data based on the @info data.
  168  */
  169 static void
  170 set_nhop_mtu_from_info(struct nhop_object *nh, const struct rt_addrinfo *info)
  171 {
  172         if (info->rti_mflags & RTV_MTU)
  173                 nhop_set_mtu(nh, info->rti_rmx->rmx_mtu, true);
  174 }
  175 
  176 /*
  177  * Fills in shorted link-level sockadd version suitable to be stored inside the
  178  *  nexthop gateway buffer.
  179  */
  180 static void
  181 fill_sdl_from_ifp(struct sockaddr_dl_short *sdl, const struct ifnet *ifp)
  182 {
  183 
  184         bzero(sdl, sizeof(struct sockaddr_dl_short));
  185         sdl->sdl_family = AF_LINK;
  186         sdl->sdl_len = sizeof(struct sockaddr_dl_short);
  187         sdl->sdl_index = ifp->if_index;
  188         sdl->sdl_type = ifp->if_type;
  189 }
  190 
  191 static int
  192 set_nhop_gw_from_info(struct nhop_object *nh, struct rt_addrinfo *info)
  193 {
  194         struct sockaddr *gw;
  195 
  196         gw = info->rti_info[RTAX_GATEWAY];
  197         MPASS(gw != NULL);
  198         bool is_gw = info->rti_flags & RTF_GATEWAY;
  199 
  200         if ((gw->sa_family == AF_LINK) && !is_gw) {
  201 
  202                 /*
  203                  * Interface route with interface specified by the interface
  204                  * index in sockadd_dl structure. It is used in the IPv6 loopback
  205                  * output code, where we need to preserve the original interface
  206                  * to maintain proper scoping.
  207                  * Despite the fact that nexthop code stores original interface
  208                  * in the separate field (nh_aifp, see below), write AF_LINK
  209                  * compatible sa with shorter total length.
  210                  */
  211                 struct sockaddr_dl *sdl = (struct sockaddr_dl *)gw;
  212                 struct ifnet *ifp = ifnet_byindex(sdl->sdl_index);
  213                 if (ifp == NULL) {
  214                         FIB_NH_LOG(LOG_DEBUG, nh, "error: invalid ifindex %d",
  215                             sdl->sdl_index);
  216                         return (EINVAL);
  217                 }
  218                 nhop_set_direct_gw(nh, ifp);
  219         } else {
  220 
  221                 /*
  222                  * Multiple options here:
  223                  *
  224                  * 1) RTF_GATEWAY with IPv4/IPv6 gateway data
  225                  * 2) Interface route with IPv4/IPv6 address of the
  226                  *   matching interface. Some routing daemons do that
  227                  *   instead of specifying ifindex in AF_LINK.
  228                  *
  229                  * In both cases, save the original nexthop to make the callers
  230                  *   happy.
  231                  */
  232                 if (!nhop_set_gw(nh, gw, is_gw))
  233                         return (EINVAL);
  234         }
  235         return (0);
  236 }
  237 
  238 static void
  239 set_nhop_expire_from_info(struct nhop_object *nh, const struct rt_addrinfo *info)
  240 {
  241         uint32_t nh_expire = 0;
  242 
  243         /* Kernel -> userland timebase conversion. */
  244         if ((info->rti_mflags & RTV_EXPIRE) && (info->rti_rmx->rmx_expire > 0))
  245                 nh_expire = info->rti_rmx->rmx_expire - time_second + time_uptime;
  246         nhop_set_expire(nh, nh_expire);
  247 }
  248 
  249 /*
  250  * Creates a new nexthop based on the information in @info.
  251  *
  252  * Returns:
  253  * 0 on success, filling @nh_ret with the desired nexthop object ptr
  254  * errno otherwise
  255  */
  256 int
  257 nhop_create_from_info(struct rib_head *rnh, struct rt_addrinfo *info,
  258     struct nhop_object **nh_ret)
  259 {
  260         int error;
  261 
  262         NET_EPOCH_ASSERT();
  263 
  264         MPASS(info->rti_ifa != NULL);
  265         MPASS(info->rti_ifp != NULL);
  266 
  267         if (info->rti_info[RTAX_GATEWAY] == NULL) {
  268                 FIB_RH_LOG(LOG_DEBUG, rnh, "error: empty gateway");
  269                 return (EINVAL);
  270         }
  271 
  272         struct nhop_object *nh = nhop_alloc(rnh->rib_fibnum, rnh->rib_family);
  273         if (nh == NULL)
  274                 return (ENOMEM);
  275 
  276         if ((error = set_nhop_gw_from_info(nh, info)) != 0) {
  277                 nhop_free(nh);
  278                 return (error);
  279         }
  280         nhop_set_transmit_ifp(nh, info->rti_ifp);
  281 
  282         nhop_set_blackhole(nh, info->rti_flags & (RTF_BLACKHOLE | RTF_REJECT));
  283 
  284         error = rnh->rnh_set_nh_pfxflags(rnh->rib_fibnum, info->rti_info[RTAX_DST],
  285             info->rti_info[RTAX_NETMASK], nh);
  286 
  287         nhop_set_redirect(nh, info->rti_flags & RTF_DYNAMIC);
  288         nhop_set_pinned(nh, info->rti_flags & RTF_PINNED);
  289         set_nhop_expire_from_info(nh, info);
  290         nhop_set_rtflags(nh, info->rti_flags);
  291 
  292         set_nhop_mtu_from_info(nh, info);
  293         nhop_set_src(nh, info->rti_ifa);
  294 
  295         /*
  296          * The remaining fields are either set from nh_preadd hook
  297          * or are computed from the provided data
  298          */
  299         *nh_ret = nhop_get_nhop(nh, &error);
  300 
  301         return (error);
  302 }
  303 
  304 /*
  305  * Gets linked nhop using the provided @nh nexhop data.
  306  * If linked nhop is found, returns it, freeing the provided one.
  307  * If there is no such nexthop, attaches the remaining data to the
  308  *  provided nexthop and links it.
  309  *
  310  * Returns 0 on success, storing referenced nexthop in @pnh.
  311  * Otherwise, errno is returned.
  312  */
  313 struct nhop_object *
  314 nhop_get_nhop(struct nhop_object *nh, int *perror)
  315 {
  316         struct rib_head *rnh = nhop_get_rh(nh);
  317 
  318         if (__predict_false(rnh == NULL)) {
  319                 *perror = EAFNOSUPPORT;
  320                 nhop_free(nh);
  321                 return (NULL);
  322         }
  323 
  324         return (nhop_get_nhop_internal(rnh, nh, perror));
  325 }
  326 
  327 struct nhop_object *
  328 nhop_get_nhop_internal(struct rib_head *rnh, struct nhop_object *nh, int *perror)
  329 {
  330         struct nhop_priv *tmp_priv;
  331         int error;
  332 
  333         nh->nh_aifp = get_aifp(nh);
  334 
  335         /* Give the protocols chance to augment nexthop properties */
  336         error = rnh->rnh_augment_nh(rnh->rib_fibnum, nh);
  337         if (error != 0) {
  338                 nhop_free(nh);
  339                 *perror = error;
  340                 return (NULL);
  341         }
  342 
  343         tmp_priv = find_nhop(rnh->nh_control, nh->nh_priv);
  344         if (tmp_priv != NULL) {
  345                 nhop_free(nh);
  346                 *perror = 0;
  347                 return (tmp_priv->nh);
  348         }
  349 
  350         /*
  351          * Existing nexthop not found, need to create new one.
  352          * Note: multiple simultaneous requests
  353          *  can result in multiple equal nexhops existing in the
  354          *  nexthop table. This is not a not a problem until the
  355          *  relative number of such nexthops is significant, which
  356          *  is extremely unlikely.
  357          */
  358         *perror = finalize_nhop(rnh->nh_control, nh, true);
  359         return (*perror == 0 ? nh : NULL);
  360 }
  361 
  362 /*
  363  * Gets referenced but unlinked nhop.
  364  * Alocates/references the remaining bits of the nexthop data, so
  365  *  it can be safely linked later or used as a clone source.
  366  *
  367  * Returns 0 on success.
  368  */
  369 int
  370 nhop_get_unlinked(struct nhop_object *nh)
  371 {
  372         struct rib_head *rnh = nhop_get_rh(nh);
  373 
  374         if (__predict_false(rnh == NULL)) {
  375                 nhop_free(nh);
  376                 return (EAFNOSUPPORT);
  377         }
  378 
  379         nh->nh_aifp = get_aifp(nh);
  380 
  381         return (finalize_nhop(rnh->nh_control, nh, false));
  382 }
  383 
  384 
  385 /*
  386  * Update @nh with data supplied in @info.
  387  * This is a helper function to support route changes.
  388  *
  389  * It limits the changes that can be done to the route to the following:
  390  * 1) all combination of gateway changes
  391  * 2) route flags (FLAG[123],STATIC)
  392  * 3) route MTU
  393  *
  394  * Returns:
  395  * 0 on success, errno otherwise
  396  */
  397 static int
  398 alter_nhop_from_info(struct nhop_object *nh, struct rt_addrinfo *info)
  399 {
  400         struct sockaddr *info_gw;
  401         int error;
  402 
  403         /* Update MTU if set in the request*/
  404         set_nhop_mtu_from_info(nh, info);
  405 
  406         /* Only RTF_FLAG[123] and RTF_STATIC */
  407         uint32_t rt_flags = nhop_get_rtflags(nh) & ~RT_CHANGE_RTFLAGS_MASK;
  408         rt_flags |= info->rti_flags & RT_CHANGE_RTFLAGS_MASK;
  409         nhop_set_rtflags(nh, rt_flags);
  410 
  411         /* Consider gateway change */
  412         info_gw = info->rti_info[RTAX_GATEWAY];
  413         if (info_gw != NULL) {
  414                 error = set_nhop_gw_from_info(nh, info);
  415                 if (error != 0)
  416                         return (error);
  417         }
  418 
  419         if (info->rti_ifa != NULL)
  420                 nhop_set_src(nh, info->rti_ifa);
  421         if (info->rti_ifp != NULL)
  422                 nhop_set_transmit_ifp(nh, info->rti_ifp);
  423 
  424         return (0);
  425 }
  426 
  427 /*
  428  * Creates new nexthop based on @nh_orig and augmentation data from @info.
  429  * Helper function used in the route changes, please see
  430  *   alter_nhop_from_info() comments for more details.
  431  *
  432  * Returns:
  433  * 0 on success, filling @nh_ret with the desired nexthop object
  434  * errno otherwise
  435  */
  436 int
  437 nhop_create_from_nhop(struct rib_head *rnh, const struct nhop_object *nh_orig,
  438     struct rt_addrinfo *info, struct nhop_object **pnh)
  439 {
  440         struct nhop_object *nh;
  441         int error;
  442 
  443         NET_EPOCH_ASSERT();
  444 
  445         nh = nhop_alloc(rnh->rib_fibnum, rnh->rib_family);
  446         if (nh == NULL)
  447                 return (ENOMEM);
  448 
  449         nhop_copy(nh, nh_orig);
  450 
  451         error = alter_nhop_from_info(nh, info);
  452         if (error != 0) {
  453                 nhop_free(nh);
  454                 return (error);
  455         }
  456 
  457         *pnh = nhop_get_nhop(nh, &error);
  458 
  459         return (error);
  460 }
  461 
  462 static bool
  463 reference_nhop_deps(struct nhop_object *nh)
  464 {
  465         if (!ifa_try_ref(nh->nh_ifa))
  466                 return (false);
  467         nh->nh_aifp = get_aifp(nh);
  468         if (!if_try_ref(nh->nh_aifp)) {
  469                 ifa_free(nh->nh_ifa);
  470                 return (false);
  471         }
  472         FIB_NH_LOG(LOG_DEBUG2, nh, "nh_aifp: %s nh_ifp %s",
  473             if_name(nh->nh_aifp), if_name(nh->nh_ifp));
  474         if (!if_try_ref(nh->nh_ifp)) {
  475                 ifa_free(nh->nh_ifa);
  476                 if_rele(nh->nh_aifp);
  477                 return (false);
  478         }
  479 
  480         return (true);
  481 }
  482 
  483 /*
  484  * Alocates/references the remaining bits of nexthop data and links
  485  *  it to the hash table.
  486  * Returns 0 if successful,
  487  *  errno otherwise. @nh_priv is freed in case of error.
  488  */
  489 static int
  490 finalize_nhop(struct nh_control *ctl, struct nhop_object *nh, bool link)
  491 {
  492 
  493         /* Allocate per-cpu packet counter */
  494         nh->nh_pksent = counter_u64_alloc(M_NOWAIT);
  495         if (nh->nh_pksent == NULL) {
  496                 nhop_free(nh);
  497                 RTSTAT_INC(rts_nh_alloc_failure);
  498                 FIB_NH_LOG(LOG_WARNING, nh, "counter_u64_alloc() failed");
  499                 return (ENOMEM);
  500         }
  501 
  502         if (!reference_nhop_deps(nh)) {
  503                 counter_u64_free(nh->nh_pksent);
  504                 nhop_free(nh);
  505                 RTSTAT_INC(rts_nh_alloc_failure);
  506                 FIB_NH_LOG(LOG_WARNING, nh, "interface reference failed");
  507                 return (EAGAIN);
  508         }
  509 
  510         /* Save vnet to ease destruction */
  511         nh->nh_priv->nh_vnet = curvnet;
  512 
  513         /* Please see nhop_free() comments on the initial value */
  514         refcount_init(&nh->nh_priv->nh_linked, 2);
  515 
  516         MPASS(nh->nh_priv->nh_fibnum == ctl->ctl_rh->rib_fibnum);
  517 
  518         if (!link) {
  519                 refcount_release(&nh->nh_priv->nh_linked);
  520                 NHOPS_WLOCK(ctl);
  521                 nh->nh_priv->nh_finalized = 1;
  522                 NHOPS_WUNLOCK(ctl);
  523         } else if (link_nhop(ctl, nh->nh_priv) == 0) {
  524                 /*
  525                  * Adding nexthop to the datastructures
  526                  *  failed. Call destructor w/o waiting for
  527                  *  the epoch end, as nexthop is not used
  528                  *  and return.
  529                  */
  530                 char nhbuf[NHOP_PRINT_BUFSIZE];
  531                 FIB_NH_LOG(LOG_WARNING, nh, "failed to link %s",
  532                     nhop_print_buf(nh, nhbuf, sizeof(nhbuf)));
  533                 destroy_nhop(nh);
  534 
  535                 return (ENOBUFS);
  536         }
  537 
  538         IF_DEBUG_LEVEL(LOG_DEBUG) {
  539                 char nhbuf[NHOP_PRINT_BUFSIZE] __unused;
  540                 FIB_NH_LOG(LOG_DEBUG, nh, "finalized: %s",
  541                     nhop_print_buf(nh, nhbuf, sizeof(nhbuf)));
  542         }
  543 
  544         return (0);
  545 }
  546 
  547 static void
  548 destroy_nhop(struct nhop_object *nh)
  549 {
  550         if_rele(nh->nh_ifp);
  551         if_rele(nh->nh_aifp);
  552         ifa_free(nh->nh_ifa);
  553         counter_u64_free(nh->nh_pksent);
  554 
  555         uma_zfree(nhops_zone, nh);
  556 }
  557 
  558 /*
  559  * Epoch callback indicating nhop is safe to destroy
  560  */
  561 static void
  562 destroy_nhop_epoch(epoch_context_t ctx)
  563 {
  564         struct nhop_priv *nh_priv;
  565 
  566         nh_priv = __containerof(ctx, struct nhop_priv, nh_epoch_ctx);
  567 
  568         destroy_nhop(nh_priv->nh);
  569 }
  570 
  571 void
  572 nhop_ref_object(struct nhop_object *nh)
  573 {
  574         u_int old __diagused;
  575 
  576         old = refcount_acquire(&nh->nh_priv->nh_refcnt);
  577         KASSERT(old > 0, ("%s: nhop object %p has 0 refs", __func__, nh));
  578 }
  579 
  580 int
  581 nhop_try_ref_object(struct nhop_object *nh)
  582 {
  583 
  584         return (refcount_acquire_if_not_zero(&nh->nh_priv->nh_refcnt));
  585 }
  586 
  587 void
  588 nhop_free(struct nhop_object *nh)
  589 {
  590         struct nh_control *ctl;
  591         struct nhop_priv *nh_priv = nh->nh_priv;
  592         struct epoch_tracker et;
  593 
  594         if (!refcount_release(&nh_priv->nh_refcnt))
  595                 return;
  596 
  597         /* allows to use nhop_free() during nhop init */
  598         if (__predict_false(nh_priv->nh_finalized == 0)) {
  599                 uma_zfree(nhops_zone, nh);
  600                 return;
  601         }
  602 
  603         IF_DEBUG_LEVEL(LOG_DEBUG) {
  604                 char nhbuf[NHOP_PRINT_BUFSIZE] __unused;
  605                 FIB_NH_LOG(LOG_DEBUG, nh, "deleting %s",
  606                     nhop_print_buf(nh, nhbuf, sizeof(nhbuf)));
  607         }
  608 
  609         /*
  610          * There are only 2 places, where nh_linked can be decreased:
  611          *  rib destroy (nhops_destroy_rib) and this function.
  612          * nh_link can never be increased.
  613          *
  614          * Hence, use initial value of 2 to make use of
  615          *  refcount_release_if_not_last().
  616          *
  617          * There can be two scenarious when calling this function:
  618          *
  619          * 1) nh_linked value is 2. This means that either
  620          *  nhops_destroy_rib() has not been called OR it is running,
  621          *  but we are guaranteed that nh_control won't be freed in
  622          *  this epoch. Hence, nexthop can be safely unlinked.
  623          *
  624          * 2) nh_linked value is 1. In that case, nhops_destroy_rib()
  625          *  has been called and nhop unlink can be skipped.
  626          */
  627 
  628         NET_EPOCH_ENTER(et);
  629         if (refcount_release_if_not_last(&nh_priv->nh_linked)) {
  630                 ctl = nh_priv->nh_control;
  631                 if (unlink_nhop(ctl, nh_priv) == NULL) {
  632                         /* Do not try to reclaim */
  633                         char nhbuf[NHOP_PRINT_BUFSIZE];
  634                         FIB_NH_LOG(LOG_WARNING, nh, "failed to unlink %s",
  635                             nhop_print_buf(nh, nhbuf, sizeof(nhbuf)));
  636                         NET_EPOCH_EXIT(et);
  637                         return;
  638                 }
  639         }
  640         NET_EPOCH_EXIT(et);
  641 
  642         NET_EPOCH_CALL(destroy_nhop_epoch, &nh_priv->nh_epoch_ctx);
  643 }
  644 
  645 void
  646 nhop_ref_any(struct nhop_object *nh)
  647 {
  648 #ifdef ROUTE_MPATH
  649         if (!NH_IS_NHGRP(nh))
  650                 nhop_ref_object(nh);
  651         else
  652                 nhgrp_ref_object((struct nhgrp_object *)nh);
  653 #else
  654         nhop_ref_object(nh);
  655 #endif
  656 }
  657 
  658 void
  659 nhop_free_any(struct nhop_object *nh)
  660 {
  661 
  662 #ifdef ROUTE_MPATH
  663         if (!NH_IS_NHGRP(nh))
  664                 nhop_free(nh);
  665         else
  666                 nhgrp_free((struct nhgrp_object *)nh);
  667 #else
  668         nhop_free(nh);
  669 #endif
  670 }
  671 
  672 /* Nhop-related methods */
  673 
  674 /*
  675  * Allocates an empty unlinked nhop object.
  676  * Returns object pointer or NULL on failure
  677  */
  678 struct nhop_object *
  679 nhop_alloc(uint32_t fibnum, int family)
  680 {
  681         struct nhop_object *nh;
  682         struct nhop_priv *nh_priv;
  683 
  684         nh = (struct nhop_object *)uma_zalloc(nhops_zone, M_NOWAIT | M_ZERO);
  685         if (__predict_false(nh == NULL))
  686                 return (NULL);
  687 
  688         nh_priv = (struct nhop_priv *)((char *)nh + NHOP_OBJECT_ALIGNED_SIZE);
  689         nh->nh_priv = nh_priv;
  690         nh_priv->nh = nh;
  691 
  692         nh_priv->nh_upper_family = family;
  693         nh_priv->nh_fibnum = fibnum;
  694 
  695         /* Setup refcount early to allow nhop_free() to work */
  696         refcount_init(&nh_priv->nh_refcnt, 1);
  697 
  698         return (nh);
  699 }
  700 
  701 void
  702 nhop_copy(struct nhop_object *nh, const struct nhop_object *nh_orig)
  703 {
  704         struct nhop_priv *nh_priv = nh->nh_priv;
  705 
  706         nh->nh_flags = nh_orig->nh_flags;
  707         nh->nh_mtu = nh_orig->nh_mtu;
  708         memcpy(&nh->gw_sa, &nh_orig->gw_sa, nh_orig->gw_sa.sa_len);
  709         nh->nh_ifp = nh_orig->nh_ifp;
  710         nh->nh_ifa = nh_orig->nh_ifa;
  711         nh->nh_aifp = nh_orig->nh_aifp;
  712 
  713         nh_priv->nh_upper_family = nh_orig->nh_priv->nh_upper_family;
  714         nh_priv->nh_neigh_family = nh_orig->nh_priv->nh_neigh_family;
  715         nh_priv->nh_type = nh_orig->nh_priv->nh_type;
  716         nh_priv->rt_flags = nh_orig->nh_priv->rt_flags;
  717         nh_priv->nh_fibnum = nh_orig->nh_priv->nh_fibnum;
  718         nh_priv->nh_origin = nh_orig->nh_priv->nh_origin;
  719 }
  720 
  721 void
  722 nhop_set_direct_gw(struct nhop_object *nh, struct ifnet *ifp)
  723 {
  724         nh->nh_flags &= ~NHF_GATEWAY;
  725         nh->nh_priv->rt_flags &= ~RTF_GATEWAY;
  726         nh->nh_priv->nh_neigh_family = nh->nh_priv->nh_upper_family;
  727 
  728         fill_sdl_from_ifp(&nh->gwl_sa, ifp);
  729         memset(&nh->gw_buf[nh->gw_sa.sa_len], 0, sizeof(nh->gw_buf) - nh->gw_sa.sa_len);
  730 }
  731 
  732 bool
  733 nhop_check_gateway(int upper_family, int neigh_family)
  734 {
  735         if (upper_family == neigh_family)
  736                 return (true);
  737         else if (neigh_family == AF_UNSPEC || neigh_family == AF_LINK)
  738                 return (true);
  739 #if defined(INET) && defined(INET6)
  740         else if (upper_family == AF_INET && neigh_family == AF_INET6 &&
  741             rib_can_4o6_nhop())
  742                 return (true);
  743 #endif
  744         else
  745                 return (false);
  746 }
  747 
  748 /*
  749  * Sets gateway for the nexthop.
  750  * It can be "normal" gateway with is_gw set or a special form of
  751  * adding interface route, refering to it by specifying local interface
  752  * address. In that case is_gw is set to false.
  753  */
  754 bool
  755 nhop_set_gw(struct nhop_object *nh, const struct sockaddr *gw, bool is_gw)
  756 {
  757         if (gw->sa_len > sizeof(nh->gw_buf)) {
  758                 FIB_NH_LOG(LOG_DEBUG, nh, "nhop SA size too big: AF %d len %u",
  759                     gw->sa_family, gw->sa_len);
  760                 return (false);
  761         }
  762 
  763         if (!nhop_check_gateway(nh->nh_priv->nh_upper_family, gw->sa_family)) {
  764                 FIB_NH_LOG(LOG_DEBUG, nh,
  765                     "error: invalid dst/gateway family combination (%d, %d)",
  766                     nh->nh_priv->nh_upper_family, gw->sa_family);
  767                 return (false);
  768         }
  769 
  770         memcpy(&nh->gw_sa, gw, gw->sa_len);
  771         memset(&nh->gw_buf[gw->sa_len], 0, sizeof(nh->gw_buf) - gw->sa_len);
  772 
  773         if (is_gw) {
  774                 nh->nh_flags |= NHF_GATEWAY;
  775                 nh->nh_priv->rt_flags |= RTF_GATEWAY;
  776                 nh->nh_priv->nh_neigh_family = gw->sa_family;
  777         } else {
  778                 nh->nh_flags &= ~NHF_GATEWAY;
  779                 nh->nh_priv->rt_flags &= ~RTF_GATEWAY;
  780                 nh->nh_priv->nh_neigh_family = nh->nh_priv->nh_upper_family;
  781         }
  782 
  783         return (true);
  784 }
  785 
  786 bool
  787 nhop_set_upper_family(struct nhop_object *nh, int family)
  788 {
  789         if (!nhop_check_gateway(nh->nh_priv->nh_upper_family, family)) {
  790                 FIB_NH_LOG(LOG_DEBUG, nh,
  791                     "error: invalid upper/neigh family combination (%d, %d)",
  792                     nh->nh_priv->nh_upper_family, family);
  793                 return (false);
  794         }
  795 
  796         nh->nh_priv->nh_upper_family = family;
  797         return (true);
  798 }
  799 
  800 void
  801 nhop_set_broadcast(struct nhop_object *nh, bool is_broadcast)
  802 {
  803         if (is_broadcast) {
  804                 nh->nh_flags |= NHF_BROADCAST;
  805                 nh->nh_priv->rt_flags |= RTF_BROADCAST;
  806         } else {
  807                 nh->nh_flags &= ~NHF_BROADCAST;
  808                 nh->nh_priv->rt_flags &= ~RTF_BROADCAST;
  809         }
  810 }
  811 
  812 void
  813 nhop_set_blackhole(struct nhop_object *nh, int blackhole_rt_flag)
  814 {
  815         nh->nh_flags &= ~(NHF_BLACKHOLE | NHF_REJECT);
  816         nh->nh_priv->rt_flags &= ~(RTF_BLACKHOLE | RTF_REJECT);
  817         switch (blackhole_rt_flag) {
  818         case RTF_BLACKHOLE:
  819                 nh->nh_flags |= NHF_BLACKHOLE;
  820                 nh->nh_priv->rt_flags |= RTF_BLACKHOLE;
  821                 break;
  822         case RTF_REJECT:
  823                 nh->nh_flags |= NHF_REJECT;
  824                 nh->nh_priv->rt_flags |= RTF_REJECT;
  825                 break;
  826         }
  827 }
  828 
  829 void
  830 nhop_set_redirect(struct nhop_object *nh, bool is_redirect)
  831 {
  832         if (is_redirect) {
  833                 nh->nh_priv->rt_flags |= RTF_DYNAMIC;
  834                 nh->nh_flags |= NHF_REDIRECT;
  835         } else {
  836                 nh->nh_priv->rt_flags &= ~RTF_DYNAMIC;
  837                 nh->nh_flags &= ~NHF_REDIRECT;
  838         }
  839 }
  840 
  841 void
  842 nhop_set_pinned(struct nhop_object *nh, bool is_pinned)
  843 {
  844         if (is_pinned)
  845                 nh->nh_priv->rt_flags |= RTF_PINNED;
  846         else
  847                 nh->nh_priv->rt_flags &= ~RTF_PINNED;
  848 }
  849 
  850 uint32_t
  851 nhop_get_idx(const struct nhop_object *nh)
  852 {
  853 
  854         return (nh->nh_priv->nh_idx);
  855 }
  856 
  857 uint32_t
  858 nhop_get_uidx(const struct nhop_object *nh)
  859 {
  860         return (nh->nh_priv->nh_uidx);
  861 }
  862 
  863 void
  864 nhop_set_uidx(struct nhop_object *nh, uint32_t uidx)
  865 {
  866         nh->nh_priv->nh_uidx = uidx;
  867 }
  868 
  869 enum nhop_type
  870 nhop_get_type(const struct nhop_object *nh)
  871 {
  872 
  873         return (nh->nh_priv->nh_type);
  874 }
  875 
  876 void
  877 nhop_set_type(struct nhop_object *nh, enum nhop_type nh_type)
  878 {
  879 
  880         nh->nh_priv->nh_type = nh_type;
  881 }
  882 
  883 int
  884 nhop_get_rtflags(const struct nhop_object *nh)
  885 {
  886 
  887         return (nh->nh_priv->rt_flags);
  888 }
  889 
  890 /*
  891  * Sets generic rtflags that are not covered by other functions.
  892  */
  893 void
  894 nhop_set_rtflags(struct nhop_object *nh, int rt_flags)
  895 {
  896         nh->nh_priv->rt_flags &= ~RT_SET_RTFLAGS_MASK;
  897         nh->nh_priv->rt_flags |= (rt_flags & RT_SET_RTFLAGS_MASK);
  898 }
  899 
  900 /*
  901  * Sets flags that are specific to the prefix (NHF_HOST or NHF_DEFAULT).
  902  */
  903 void
  904 nhop_set_pxtype_flag(struct nhop_object *nh, int nh_flag)
  905 {
  906         if (nh_flag == NHF_HOST) {
  907                 nh->nh_flags |= NHF_HOST;
  908                 nh->nh_flags &= ~NHF_DEFAULT;
  909                 nh->nh_priv->rt_flags |= RTF_HOST;
  910         } else if (nh_flag == NHF_DEFAULT) {
  911                 nh->nh_flags |= NHF_DEFAULT;
  912                 nh->nh_flags &= ~NHF_HOST;
  913                 nh->nh_priv->rt_flags &= ~RTF_HOST;
  914         } else {
  915                 nh->nh_flags &= ~(NHF_HOST | NHF_DEFAULT);
  916                 nh->nh_priv->rt_flags &= ~RTF_HOST;
  917         }
  918 }
  919 
  920 /*
  921  * Sets nhop MTU. Sets RTF_FIXEDMTU if mtu is explicitly
  922  * specified by userland.
  923  */
  924 void
  925 nhop_set_mtu(struct nhop_object *nh, uint32_t mtu, bool from_user)
  926 {
  927         if (from_user) {
  928                 if (mtu != 0)
  929                         nh->nh_priv->rt_flags |= RTF_FIXEDMTU;
  930                 else
  931                         nh->nh_priv->rt_flags &= ~RTF_FIXEDMTU;
  932         }
  933         nh->nh_mtu = mtu;
  934 }
  935 
  936 void
  937 nhop_set_src(struct nhop_object *nh, struct ifaddr *ifa)
  938 {
  939         nh->nh_ifa = ifa;
  940 }
  941 
  942 void
  943 nhop_set_transmit_ifp(struct nhop_object *nh, struct ifnet *ifp)
  944 {
  945         nh->nh_ifp = ifp;
  946 }
  947 
  948 
  949 struct vnet *
  950 nhop_get_vnet(const struct nhop_object *nh)
  951 {
  952 
  953         return (nh->nh_priv->nh_vnet);
  954 }
  955 
  956 struct nhop_object *
  957 nhop_select_func(struct nhop_object *nh, uint32_t flowid)
  958 {
  959 
  960         return (nhop_select(nh, flowid));
  961 }
  962 
  963 /*
  964  * Returns address family of the traffic uses the nexthop.
  965  */
  966 int
  967 nhop_get_upper_family(const struct nhop_object *nh)
  968 {
  969         return (nh->nh_priv->nh_upper_family);
  970 }
  971 
  972 /*
  973  * Returns address family of the LLE or gateway that is used
  974  * to forward the traffic to.
  975  */
  976 int
  977 nhop_get_neigh_family(const struct nhop_object *nh)
  978 {
  979         return (nh->nh_priv->nh_neigh_family);
  980 }
  981 
  982 uint32_t
  983 nhop_get_fibnum(const struct nhop_object *nh)
  984 {
  985         return (nh->nh_priv->nh_fibnum);
  986 }
  987 
  988 void
  989 nhop_set_fibnum(struct nhop_object *nh, uint32_t fibnum)
  990 {
  991         nh->nh_priv->nh_fibnum = fibnum;
  992 }
  993 
  994 uint32_t
  995 nhop_get_expire(const struct nhop_object *nh)
  996 {
  997         return (nh->nh_priv->nh_expire);
  998 }
  999 
 1000 void
 1001 nhop_set_expire(struct nhop_object *nh, uint32_t expire)
 1002 {
 1003         MPASS(!NH_IS_LINKED(nh));
 1004         nh->nh_priv->nh_expire = expire;
 1005 }
 1006 
 1007 struct rib_head *
 1008 nhop_get_rh(const struct nhop_object *nh)
 1009 {
 1010         uint32_t fibnum = nhop_get_fibnum(nh);
 1011         int family = nhop_get_neigh_family(nh);
 1012 
 1013         return (rt_tables_get_rnh(fibnum, family));
 1014 }
 1015 
 1016 uint8_t
 1017 nhop_get_origin(const struct nhop_object *nh)
 1018 {
 1019         return (nh->nh_priv->nh_origin);
 1020 }
 1021 
 1022 void
 1023 nhop_set_origin(struct nhop_object *nh, uint8_t origin)
 1024 {
 1025         nh->nh_priv->nh_origin = origin;
 1026 }
 1027 
 1028 void
 1029 nhops_update_ifmtu(struct rib_head *rh, struct ifnet *ifp, uint32_t mtu)
 1030 {
 1031         struct nh_control *ctl;
 1032         struct nhop_priv *nh_priv;
 1033         struct nhop_object *nh;
 1034 
 1035         ctl = rh->nh_control;
 1036 
 1037         NHOPS_WLOCK(ctl);
 1038         CHT_SLIST_FOREACH(&ctl->nh_head, nhops, nh_priv) {
 1039                 nh = nh_priv->nh;
 1040                 if (nh->nh_ifp == ifp) {
 1041                         if ((nh_priv->rt_flags & RTF_FIXEDMTU) == 0 ||
 1042                             nh->nh_mtu > mtu) {
 1043                                 /* Update MTU directly */
 1044                                 nh->nh_mtu = mtu;
 1045                         }
 1046                 }
 1047         } CHT_SLIST_FOREACH_END;
 1048         NHOPS_WUNLOCK(ctl);
 1049 
 1050 }
 1051 
 1052 /*
 1053  * Prints nexthop @nh data in the provided @buf.
 1054  * Example: nh#33/inet/em0/192.168.0.1
 1055  */
 1056 char *
 1057 nhop_print_buf(const struct nhop_object *nh, char *buf, size_t bufsize)
 1058 {
 1059 #if defined(INET) || defined(INET6)
 1060         char abuf[INET6_ADDRSTRLEN];
 1061 #endif
 1062         struct nhop_priv *nh_priv = nh->nh_priv;
 1063         const char *upper_str = rib_print_family(nh->nh_priv->nh_upper_family);
 1064 
 1065         switch (nh->gw_sa.sa_family) {
 1066 #ifdef INET
 1067         case AF_INET:
 1068                 inet_ntop(AF_INET, &nh->gw4_sa.sin_addr, abuf, sizeof(abuf));
 1069                 snprintf(buf, bufsize, "nh#%d/%s/%s/%s", nh_priv->nh_idx, upper_str,
 1070                     if_name(nh->nh_ifp), abuf);
 1071                 break;
 1072 #endif
 1073 #ifdef INET6
 1074         case AF_INET6:
 1075                 inet_ntop(AF_INET6, &nh->gw6_sa.sin6_addr, abuf, sizeof(abuf));
 1076                 snprintf(buf, bufsize, "nh#%d/%s/%s/%s", nh_priv->nh_idx, upper_str,
 1077                     if_name(nh->nh_ifp), abuf);
 1078                 break;
 1079 #endif
 1080         case AF_LINK:
 1081                 snprintf(buf, bufsize, "nh#%d/%s/%s/resolve", nh_priv->nh_idx, upper_str,
 1082                     if_name(nh->nh_ifp));
 1083                 break;
 1084         default:
 1085                 snprintf(buf, bufsize, "nh#%d/%s/%s/????", nh_priv->nh_idx, upper_str,
 1086                     if_name(nh->nh_ifp));
 1087                 break;
 1088         }
 1089 
 1090         return (buf);
 1091 }
 1092 
 1093 char *
 1094 nhop_print_buf_any(const struct nhop_object *nh, char *buf, size_t bufsize)
 1095 {
 1096 #ifdef ROUTE_MPATH
 1097         if (NH_IS_NHGRP(nh))
 1098                 return (nhgrp_print_buf((const struct nhgrp_object *)nh, buf, bufsize));
 1099         else
 1100 #endif
 1101                 return (nhop_print_buf(nh, buf, bufsize));
 1102 }
 1103 
 1104 /*
 1105  * Dumps a single entry to sysctl buffer.
 1106  *
 1107  * Layout:
 1108  *  rt_msghdr - generic RTM header to allow users to skip non-understood messages
 1109  *  nhop_external - nexhop description structure (with length)
 1110  *  nhop_addrs - structure encapsulating GW/SRC sockaddrs
 1111  */
 1112 static int
 1113 dump_nhop_entry(struct rib_head *rh, struct nhop_object *nh, struct sysctl_req *w)
 1114 {
 1115         struct {
 1116                 struct rt_msghdr        rtm;
 1117                 struct nhop_external    nhe;
 1118                 struct nhop_addrs       na;
 1119         } arpc;
 1120         struct nhop_external *pnhe;
 1121         struct sockaddr *gw_sa, *src_sa;
 1122         struct sockaddr_storage ss;
 1123         size_t addrs_len;
 1124         int error;
 1125 
 1126         memset(&arpc, 0, sizeof(arpc));
 1127 
 1128         arpc.rtm.rtm_msglen = sizeof(arpc);
 1129         arpc.rtm.rtm_version = RTM_VERSION;
 1130         arpc.rtm.rtm_type = RTM_GET;
 1131         //arpc.rtm.rtm_flags = RTF_UP;
 1132         arpc.rtm.rtm_flags = nh->nh_priv->rt_flags;
 1133 
 1134         /* nhop_external */
 1135         pnhe = &arpc.nhe;
 1136         pnhe->nh_len = sizeof(struct nhop_external);
 1137         pnhe->nh_idx = nh->nh_priv->nh_idx;
 1138         pnhe->nh_fib = rh->rib_fibnum;
 1139         pnhe->ifindex = nh->nh_ifp->if_index;
 1140         pnhe->aifindex = nh->nh_aifp->if_index;
 1141         pnhe->nh_family = nh->nh_priv->nh_upper_family;
 1142         pnhe->nh_type = nh->nh_priv->nh_type;
 1143         pnhe->nh_mtu = nh->nh_mtu;
 1144         pnhe->nh_flags = nh->nh_flags;
 1145 
 1146         memcpy(pnhe->nh_prepend, nh->nh_prepend, sizeof(nh->nh_prepend));
 1147         pnhe->prepend_len = nh->nh_prepend_len;
 1148         pnhe->nh_refcount = nh->nh_priv->nh_refcnt;
 1149         pnhe->nh_pksent = counter_u64_fetch(nh->nh_pksent);
 1150 
 1151         /* sockaddr container */
 1152         addrs_len = sizeof(struct nhop_addrs);
 1153         arpc.na.gw_sa_off = addrs_len;
 1154         gw_sa = (struct sockaddr *)&nh->gw4_sa;
 1155         addrs_len += gw_sa->sa_len;
 1156 
 1157         src_sa = nh->nh_ifa->ifa_addr;
 1158         if (src_sa->sa_family == AF_LINK) {
 1159                 /* Shorten structure */
 1160                 memset(&ss, 0, sizeof(struct sockaddr_storage));
 1161                 fill_sdl_from_ifp((struct sockaddr_dl_short *)&ss,
 1162                     nh->nh_ifa->ifa_ifp);
 1163                 src_sa = (struct sockaddr *)&ss;
 1164         }
 1165         arpc.na.src_sa_off = addrs_len;
 1166         addrs_len += src_sa->sa_len;
 1167 
 1168         /* Write total container length */
 1169         arpc.na.na_len = addrs_len;
 1170 
 1171         arpc.rtm.rtm_msglen += arpc.na.na_len - sizeof(struct nhop_addrs);
 1172 
 1173         error = SYSCTL_OUT(w, &arpc, sizeof(arpc));
 1174         if (error == 0)
 1175                 error = SYSCTL_OUT(w, gw_sa, gw_sa->sa_len);
 1176         if (error == 0)
 1177                 error = SYSCTL_OUT(w, src_sa, src_sa->sa_len);
 1178 
 1179         return (error);
 1180 }
 1181 
 1182 uint32_t
 1183 nhops_get_count(struct rib_head *rh)
 1184 {
 1185         struct nh_control *ctl;
 1186         uint32_t count;
 1187 
 1188         ctl = rh->nh_control;
 1189 
 1190         NHOPS_RLOCK(ctl);
 1191         count = ctl->nh_head.items_count;
 1192         NHOPS_RUNLOCK(ctl);
 1193 
 1194         return (count);
 1195 }
 1196 
 1197 int
 1198 nhops_dump_sysctl(struct rib_head *rh, struct sysctl_req *w)
 1199 {
 1200         struct nh_control *ctl;
 1201         struct nhop_priv *nh_priv;
 1202         int error;
 1203 
 1204         ctl = rh->nh_control;
 1205 
 1206         NHOPS_RLOCK(ctl);
 1207         FIB_RH_LOG(LOG_DEBUG, rh, "dump %u items", ctl->nh_head.items_count);
 1208         CHT_SLIST_FOREACH(&ctl->nh_head, nhops, nh_priv) {
 1209                 error = dump_nhop_entry(rh, nh_priv->nh, w);
 1210                 if (error != 0) {
 1211                         NHOPS_RUNLOCK(ctl);
 1212                         return (error);
 1213                 }
 1214         } CHT_SLIST_FOREACH_END;
 1215         NHOPS_RUNLOCK(ctl);
 1216 
 1217         return (0);
 1218 }

Cache object: 5b5b3a7be2a420d83007b80613f3be1d


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.