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/route_helpers.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/jail.h>
   36 #include <sys/systm.h>
   37 #include <sys/malloc.h>
   38 #include <sys/mbuf.h>
   39 #include <sys/socket.h>
   40 #include <sys/sysctl.h>
   41 #include <sys/syslog.h>
   42 #include <sys/sysproto.h>
   43 #include <sys/proc.h>
   44 #include <sys/domain.h>
   45 #include <sys/kernel.h>
   46 #include <sys/lock.h>
   47 #include <sys/rmlock.h>
   48 
   49 #include <net/if.h>
   50 #include <net/if_var.h>
   51 #include <net/if_dl.h>
   52 #include <net/route.h>
   53 #include <net/route/route_ctl.h>
   54 #include <net/route/route_var.h>
   55 #include <net/route/nhop_utils.h>
   56 #include <net/route/nhop.h>
   57 #include <net/route/nhop_var.h>
   58 #ifdef INET
   59 #include <netinet/in_fib.h>
   60 #endif
   61 #ifdef INET6
   62 #include <netinet6/in6_fib.h>
   63 #include <netinet6/in6_var.h>
   64 #endif
   65 #include <net/vnet.h>
   66 
   67 #define DEBUG_MOD_NAME  rt_helpers
   68 #define DEBUG_MAX_LEVEL LOG_DEBUG2
   69 #include <net/route/route_debug.h>
   70 _DECLARE_DEBUG(LOG_INFO);
   71 
   72 /*
   73  * RIB helper functions.
   74  */
   75 
   76 void
   77 rib_walk_ext_locked(struct rib_head *rnh, rib_walktree_f_t *wa_f,
   78     rib_walk_hook_f_t *hook_f, void *arg)
   79 {
   80         if (hook_f != NULL)
   81                 hook_f(rnh, RIB_WALK_HOOK_PRE, arg);
   82         rnh->rnh_walktree(&rnh->head, (walktree_f_t *)wa_f, arg);
   83         if (hook_f != NULL)
   84                 hook_f(rnh, RIB_WALK_HOOK_POST, arg);
   85 }
   86 
   87 /*
   88  * Calls @wa_f with @arg for each entry in the table specified by
   89  * @af and @fibnum.
   90  *
   91  * @ss_t callback is called before and after the tree traversal
   92  *  while holding table lock.
   93  *
   94  * Table is traversed under read lock unless @wlock is set.
   95  */
   96 void
   97 rib_walk_ext_internal(struct rib_head *rnh, bool wlock, rib_walktree_f_t *wa_f,
   98     rib_walk_hook_f_t *hook_f, void *arg)
   99 {
  100         RIB_RLOCK_TRACKER;
  101 
  102         if (wlock)
  103                 RIB_WLOCK(rnh);
  104         else
  105                 RIB_RLOCK(rnh);
  106         rib_walk_ext_locked(rnh, wa_f, hook_f, arg);
  107         if (wlock)
  108                 RIB_WUNLOCK(rnh);
  109         else
  110                 RIB_RUNLOCK(rnh);
  111 }
  112 
  113 void
  114 rib_walk_ext(uint32_t fibnum, int family, bool wlock, rib_walktree_f_t *wa_f,
  115     rib_walk_hook_f_t *hook_f, void *arg)
  116 {
  117         struct rib_head *rnh;
  118 
  119         if ((rnh = rt_tables_get_rnh(fibnum, family)) != NULL)
  120                 rib_walk_ext_internal(rnh, wlock, wa_f, hook_f, arg);
  121 }
  122 
  123 /*
  124  * Calls @wa_f with @arg for each entry in the table specified by
  125  * @af and @fibnum.
  126  *
  127  * Table is traversed under read lock unless @wlock is set.
  128  */
  129 void
  130 rib_walk(uint32_t fibnum, int family, bool wlock, rib_walktree_f_t *wa_f,
  131     void *arg)
  132 {
  133 
  134         rib_walk_ext(fibnum, family, wlock, wa_f, NULL, arg);
  135 }
  136 
  137 /*
  138  * Calls @wa_f with @arg for each entry in the table matching @prefix/@mask.
  139  *
  140  * The following flags are supported:
  141  *  RIB_FLAG_WLOCK: acquire exclusive lock
  142  *  RIB_FLAG_LOCKED: Assumes the table is already locked & skip locking
  143  *
  144  * By default, table is traversed under read lock.
  145  */
  146 void
  147 rib_walk_from(uint32_t fibnum, int family, uint32_t flags, struct sockaddr *prefix,
  148     struct sockaddr *mask, rib_walktree_f_t *wa_f, void *arg)
  149 {
  150         RIB_RLOCK_TRACKER;
  151         struct rib_head *rnh = rt_tables_get_rnh(fibnum, family);
  152 
  153         if (rnh == NULL)
  154                 return;
  155 
  156         if (flags & RIB_FLAG_WLOCK)
  157                 RIB_WLOCK(rnh);
  158         else if (!(flags & RIB_FLAG_LOCKED))
  159                 RIB_RLOCK(rnh);
  160 
  161         rnh->rnh_walktree_from(&rnh->head, prefix, mask, (walktree_f_t *)wa_f, arg);
  162 
  163         if (flags & RIB_FLAG_WLOCK)
  164                 RIB_WUNLOCK(rnh);
  165         else if (!(flags & RIB_FLAG_LOCKED))
  166                 RIB_RUNLOCK(rnh);
  167 }
  168 
  169 /*
  170  * Iterates over all existing fibs in system calling
  171  *  @hook_f function before/after traversing each fib.
  172  *  Calls @wa_f function for each element in current fib.
  173  * If af is not AF_UNSPEC, iterates over fibs in particular
  174  * address family.
  175  */
  176 void
  177 rib_foreach_table_walk(int family, bool wlock, rib_walktree_f_t *wa_f,
  178     rib_walk_hook_f_t *hook_f, void *arg)
  179 {
  180 
  181         for (uint32_t fibnum = 0; fibnum < rt_numfibs; fibnum++) {
  182                 /* Do we want some specific family? */
  183                 if (family != AF_UNSPEC) {
  184                         rib_walk_ext(fibnum, family, wlock, wa_f, hook_f, arg); 
  185                         continue;
  186                 }
  187 
  188                 for (int i = 1; i <= AF_MAX; i++)
  189                         rib_walk_ext(fibnum, i, wlock, wa_f, hook_f, arg);
  190         }
  191 }
  192 
  193 /*
  194  * Iterates over all existing fibs in system and deletes each element
  195  *  for which @filter_f function returns non-zero value.
  196  * If @family is not AF_UNSPEC, iterates over fibs in particular
  197  * address family.
  198  */
  199 void
  200 rib_foreach_table_walk_del(int family, rib_filter_f_t *filter_f, void *arg)
  201 {
  202 
  203         for (uint32_t fibnum = 0; fibnum < rt_numfibs; fibnum++) {
  204                 /* Do we want some specific family? */
  205                 if (family != AF_UNSPEC) {
  206                         rib_walk_del(fibnum, family, filter_f, arg, 0);
  207                         continue;
  208                 }
  209 
  210                 for (int i = 1; i <= AF_MAX; i++)
  211                         rib_walk_del(fibnum, i, filter_f, arg, 0);
  212         }
  213 }
  214 
  215 
  216 /*
  217  * Wrapper for the control plane functions for performing af-agnostic
  218  *  lookups.
  219  * @fibnum: fib to perform the lookup.
  220  * @dst: sockaddr with family and addr filled in. IPv6 addresses needs to be in
  221  *  deembedded from.
  222  * @flags: fib(9) flags.
  223  * @flowid: flow id for path selection in multipath use case.
  224  *
  225  * Returns nhop_object or NULL.
  226  *
  227  * Requires NET_EPOCH.
  228  *
  229  */
  230 struct nhop_object *
  231 rib_lookup(uint32_t fibnum, const struct sockaddr *dst, uint32_t flags,
  232     uint32_t flowid)
  233 {
  234         struct nhop_object *nh;
  235 
  236         nh = NULL;
  237 
  238         switch (dst->sa_family) {
  239 #ifdef INET
  240         case AF_INET:
  241         {
  242                 const struct sockaddr_in *a = (const struct sockaddr_in *)dst;
  243                 nh = fib4_lookup(fibnum, a->sin_addr, 0, flags, flowid);
  244                 break;
  245         }
  246 #endif
  247 #ifdef INET6
  248         case AF_INET6:
  249         {
  250                 const struct sockaddr_in6 *a = (const struct sockaddr_in6*)dst;
  251                 nh = fib6_lookup(fibnum, &a->sin6_addr, a->sin6_scope_id,
  252                     flags, flowid);
  253                 break;
  254         }
  255 #endif
  256         }
  257 
  258         return (nh);
  259 }
  260 
  261 #ifdef ROUTE_MPATH
  262 static void
  263 notify_add(struct rib_cmd_info *rc, const struct weightened_nhop *wn_src,
  264     route_notification_t *cb, void *cbdata)
  265 {
  266         rc->rc_nh_new = wn_src->nh;
  267         rc->rc_nh_weight = wn_src->weight;
  268 
  269         IF_DEBUG_LEVEL(LOG_DEBUG2) {
  270                 char nhbuf[NHOP_PRINT_BUFSIZE] __unused;
  271                 FIB_NH_LOG(LOG_DEBUG2, wn_src->nh, "RTM_ADD for %s @ w=%u",
  272                     nhop_print_buf(wn_src->nh, nhbuf, sizeof(nhbuf)),
  273                     wn_src->weight);
  274         }
  275         cb(rc, cbdata);
  276 }
  277 
  278 static void
  279 notify_del(struct rib_cmd_info *rc, const struct weightened_nhop *wn_src,
  280     route_notification_t *cb, void *cbdata)
  281 {
  282         rc->rc_nh_old = wn_src->nh;
  283         rc->rc_nh_weight = wn_src->weight;
  284 
  285         IF_DEBUG_LEVEL(LOG_DEBUG2) {
  286                 char nhbuf[NHOP_PRINT_BUFSIZE] __unused;
  287                 FIB_NH_LOG(LOG_DEBUG2, wn_src->nh, "RTM_DEL for %s @ w=%u",
  288                     nhop_print_buf(wn_src->nh, nhbuf, sizeof(nhbuf)),
  289                     wn_src->weight);
  290         }
  291         cb(rc, cbdata);
  292 }
  293 
  294 static void
  295 decompose_change_notification(const struct rib_cmd_info *rc, route_notification_t *cb,
  296     void *cbdata)
  297 {
  298         uint32_t num_old, num_new;
  299         const struct weightened_nhop *wn_old, *wn_new;
  300         struct weightened_nhop tmp = { NULL, 0 };
  301         uint32_t idx_old = 0, idx_new = 0;
  302 
  303         struct rib_cmd_info rc_del = { .rc_cmd = RTM_DELETE, .rc_rt = rc->rc_rt };
  304         struct rib_cmd_info rc_add = { .rc_cmd = RTM_ADD, .rc_rt = rc->rc_rt };
  305 
  306         if (NH_IS_NHGRP(rc->rc_nh_old)) {
  307                 wn_old = nhgrp_get_nhops((struct nhgrp_object *)rc->rc_nh_old, &num_old);
  308         } else {
  309                 tmp.nh = rc->rc_nh_old;
  310                 tmp.weight = rc->rc_nh_weight;
  311                 wn_old = &tmp;
  312                 num_old = 1;
  313         }
  314         if (NH_IS_NHGRP(rc->rc_nh_new)) {
  315                 wn_new = nhgrp_get_nhops((struct nhgrp_object *)rc->rc_nh_new, &num_new);
  316         } else {
  317                 tmp.nh = rc->rc_nh_new;
  318                 tmp.weight = rc->rc_nh_weight;
  319                 wn_new = &tmp;
  320                 num_new = 1;
  321         }
  322         IF_DEBUG_LEVEL(LOG_DEBUG) {
  323                 char buf_old[NHOP_PRINT_BUFSIZE], buf_new[NHOP_PRINT_BUFSIZE];
  324                 nhop_print_buf_any(rc->rc_nh_old, buf_old, NHOP_PRINT_BUFSIZE);
  325                 nhop_print_buf_any(rc->rc_nh_new, buf_new, NHOP_PRINT_BUFSIZE);
  326                 FIB_NH_LOG(LOG_DEBUG, wn_old[0].nh, "change %s -> %s", buf_old, buf_new);
  327         }
  328 
  329         /* Use the fact that each @wn array is sorted */
  330         /*
  331          * Here we have one (or two) multipath groups and transition
  332          *  between them needs to be reported to the caller, using series
  333          *  of primitive (RTM_DEL, RTM_ADD) operations.
  334          *
  335          * Leverage the fact that each nexthop group has its nexthops sorted
  336          *  by their indices.
  337          * [1] -> [1, 2] = A{2}
  338          * [1, 2] -> [1] = D{2}
  339          * [1, 2, 4] -> [1, 3, 4] = D{2}, A{3}
  340          * [1, 2] -> [3, 4] = D{1}, D{2}, A{3}, A{4]
  341          */
  342         while ((idx_old < num_old) && (idx_new < num_new)) {
  343                 uint32_t nh_idx_old = wn_old[idx_old].nh->nh_priv->nh_idx;
  344                 uint32_t nh_idx_new = wn_new[idx_new].nh->nh_priv->nh_idx;
  345 
  346                 if (nh_idx_old == nh_idx_new) {
  347                         if (wn_old[idx_old].weight != wn_new[idx_new].weight) {
  348                                 /* Update weight by providing del/add notifications */
  349                                 notify_del(&rc_del, &wn_old[idx_old], cb, cbdata);
  350                                 notify_add(&rc_add, &wn_new[idx_new], cb, cbdata);
  351                         }
  352                         idx_old++;
  353                         idx_new++;
  354                 } else if (nh_idx_old < nh_idx_new) {
  355                         /* [1, ~2~, 4], [1, ~3~, 4] */
  356                         notify_del(&rc_del, &wn_old[idx_old], cb, cbdata);
  357                         idx_old++;
  358                 } else {
  359                         /* nh_idx_old > nh_idx_new. */
  360                         notify_add(&rc_add, &wn_new[idx_new], cb, cbdata);
  361                         idx_new++;
  362                 }
  363         }
  364 
  365         while (idx_old < num_old) {
  366                 notify_del(&rc_del, &wn_old[idx_old], cb, cbdata);
  367                 idx_old++;
  368         }
  369 
  370         while (idx_new < num_new) {
  371                 notify_add(&rc_add, &wn_new[idx_new], cb, cbdata);
  372                 idx_new++;
  373         }
  374 }
  375 
  376 /*
  377  * Decompose multipath cmd info @rc into a list of add/del/change
  378  *  single-path operations, calling @cb callback for each operation.
  379  * Assumes at least one of the nexthops in @rc is multipath.
  380  */
  381 void
  382 rib_decompose_notification(const struct rib_cmd_info *rc, route_notification_t *cb,
  383     void *cbdata)
  384 {
  385         const struct weightened_nhop *wn;
  386         uint32_t num_nhops;
  387         struct rib_cmd_info rc_new;
  388 
  389         rc_new = *rc;
  390         switch (rc->rc_cmd) {
  391         case RTM_ADD:
  392                 if (!NH_IS_NHGRP(rc->rc_nh_new))
  393                         return;
  394                 wn = nhgrp_get_nhops((struct nhgrp_object *)rc->rc_nh_new, &num_nhops);
  395                 for (uint32_t i = 0; i < num_nhops; i++) {
  396                         notify_add(&rc_new, &wn[i], cb, cbdata);
  397                 }
  398                 break;
  399         case RTM_DELETE:
  400                 if (!NH_IS_NHGRP(rc->rc_nh_old))
  401                         return;
  402                 wn = nhgrp_get_nhops((struct nhgrp_object *)rc->rc_nh_old, &num_nhops);
  403                 for (uint32_t i = 0; i < num_nhops; i++) {
  404                         notify_del(&rc_new, &wn[i], cb, cbdata);
  405                 }
  406                 break;
  407         case RTM_CHANGE:
  408                 if (!NH_IS_NHGRP(rc->rc_nh_old) && !NH_IS_NHGRP(rc->rc_nh_new))
  409                         return;
  410                 decompose_change_notification(rc, cb, cbdata);
  411                 break;
  412         }
  413 }
  414 #endif
  415 
  416 union sockaddr_union {
  417         struct sockaddr         sa;
  418         struct sockaddr_in      sin;
  419         struct sockaddr_in6     sin6;
  420         char                    _buf[32];
  421 };
  422 
  423 /*
  424  * Creates nexhops suitable for using as a default route nhop.
  425  * Helper for the various kernel subsystems adding/changing default route.
  426  */
  427 int
  428 rib_add_default_route(uint32_t fibnum, int family, struct ifnet *ifp,
  429     struct sockaddr *gw, struct rib_cmd_info *rc)
  430 {
  431         struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT };
  432         union sockaddr_union saun = {};
  433         struct sockaddr *dst = &saun.sa;
  434         int error;
  435 
  436         switch (family) {
  437 #ifdef INET
  438         case AF_INET:
  439                 saun.sin.sin_family = AF_INET;
  440                 saun.sin.sin_len = sizeof(struct sockaddr_in);
  441                 break;
  442 #endif
  443 #ifdef INET6
  444         case AF_INET6:
  445                 saun.sin6.sin6_family = AF_INET6;
  446                 saun.sin6.sin6_len = sizeof(struct sockaddr_in6);
  447                 break;
  448 #endif
  449         default:
  450                 return (EAFNOSUPPORT);
  451         }
  452 
  453         struct ifaddr *ifa = ifaof_ifpforaddr(gw, ifp);
  454         if (ifa == NULL)
  455                 return (ENOENT);
  456 
  457         struct nhop_object *nh = nhop_alloc(fibnum, family);
  458         if (nh == NULL)
  459                 return (ENOMEM);
  460 
  461         nhop_set_gw(nh, gw, true);
  462         nhop_set_transmit_ifp(nh, ifp);
  463         nhop_set_src(nh, ifa);
  464         nhop_set_pxtype_flag(nh, NHF_DEFAULT);
  465         rnd.rnd_nhop = nhop_get_nhop(nh, &error);
  466 
  467         if (error == 0)
  468                 error = rib_add_route_px(fibnum, dst, 0, &rnd, RTM_F_CREATE, rc);
  469         return (error);
  470 }
  471 
  472 #ifdef INET
  473 /*
  474  * Checks if the found key in the trie contains (<=) a prefix covering
  475  *  @paddr/@plen.
  476  * Returns the most specific rtentry matching the condition or NULL.
  477  */
  478 static struct rtentry *
  479 get_inet_parent_prefix(uint32_t fibnum, struct in_addr addr, int plen)
  480 {
  481         struct route_nhop_data rnd;
  482         struct rtentry *rt;
  483         struct in_addr addr4;
  484         uint32_t scopeid;
  485         int parent_plen;
  486         struct radix_node *rn;
  487 
  488         rt = fib4_lookup_rt(fibnum, addr, 0, NHR_UNLOCKED, &rnd);
  489         if (rt == NULL)
  490                 return (NULL);
  491 
  492         rt_get_inet_prefix_plen(rt, &addr4, &parent_plen, &scopeid);
  493         if (parent_plen <= plen)
  494                 return (rt);
  495 
  496         /*
  497          * There can be multiple prefixes associated with the found key:
  498          * 10.0.0.0 -> 10.0.0.0/24, 10.0.0.0/23, 10.0.0.0/22, etc.
  499          * All such prefixes are linked via rn_dupedkey, from most specific
  500          *  to least specific. Iterate over them to check if any of these
  501          *  prefixes are wider than desired plen.
  502          */
  503         rn = (struct radix_node *)rt;
  504         while ((rn = rn_nextprefix(rn)) != NULL) {
  505                 rt = RNTORT(rn);
  506                 rt_get_inet_prefix_plen(rt, &addr4, &parent_plen, &scopeid);
  507                 if (parent_plen <= plen)
  508                         return (rt);
  509         }
  510 
  511         return (NULL);
  512 }
  513 
  514 /*
  515  * Returns the most specific prefix containing (>) @paddr/plen.
  516  */
  517 struct rtentry *
  518 rt_get_inet_parent(uint32_t fibnum, struct in_addr addr, int plen)
  519 {
  520         struct in_addr lookup_addr = { .s_addr = INADDR_BROADCAST };
  521         struct in_addr addr4 = addr;
  522         struct in_addr mask4;
  523         struct rtentry *rt;
  524 
  525         while (plen-- > 0) {
  526                 /* Calculate wider mask & new key to lookup */
  527                 mask4.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0);
  528                 addr4.s_addr = htonl(ntohl(addr4.s_addr) & ntohl(mask4.s_addr));
  529                 if (addr4.s_addr == lookup_addr.s_addr) {
  530                         /* Skip lookup if the key is the same */
  531                         continue;
  532                 }
  533                 lookup_addr = addr4;
  534 
  535                 rt = get_inet_parent_prefix(fibnum, lookup_addr, plen);
  536                 if (rt != NULL)
  537                         return (rt);
  538         }
  539 
  540         return (NULL);
  541 }
  542 #endif
  543 
  544 #ifdef INET6
  545 /*
  546  * Checks if the found key in the trie contains (<=) a prefix covering
  547  *  @paddr/@plen.
  548  * Returns the most specific rtentry matching the condition or NULL.
  549  */
  550 static struct rtentry *
  551 get_inet6_parent_prefix(uint32_t fibnum, const struct in6_addr *paddr, int plen)
  552 {
  553         struct route_nhop_data rnd;
  554         struct rtentry *rt;
  555         struct in6_addr addr6;
  556         uint32_t scopeid;
  557         int parent_plen;
  558         struct radix_node *rn;
  559 
  560         rt = fib6_lookup_rt(fibnum, paddr, 0, NHR_UNLOCKED, &rnd);
  561         if (rt == NULL)
  562                 return (NULL);
  563 
  564         rt_get_inet6_prefix_plen(rt, &addr6, &parent_plen, &scopeid);
  565         if (parent_plen <= plen)
  566                 return (rt);
  567 
  568         /*
  569          * There can be multiple prefixes associated with the found key:
  570          * 2001:db8:1::/64 -> 2001:db8:1::/56, 2001:db8:1::/48, etc.
  571          * All such prefixes are linked via rn_dupedkey, from most specific
  572          *  to least specific. Iterate over them to check if any of these
  573          *  prefixes are wider than desired plen.
  574          */
  575         rn = (struct radix_node *)rt;
  576         while ((rn = rn_nextprefix(rn)) != NULL) {
  577                 rt = RNTORT(rn);
  578                 rt_get_inet6_prefix_plen(rt, &addr6, &parent_plen, &scopeid);
  579                 if (parent_plen <= plen)
  580                         return (rt);
  581         }
  582 
  583         return (NULL);
  584 }
  585 
  586 void
  587 ip6_writemask(struct in6_addr *addr6, uint8_t mask)
  588 {
  589         uint32_t *cp;
  590 
  591         for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
  592                 *cp++ = 0xFFFFFFFF;
  593         if (mask > 0)
  594                 *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
  595 }
  596 
  597 /*
  598  * Returns the most specific prefix containing (>) @paddr/plen.
  599  */
  600 struct rtentry *
  601 rt_get_inet6_parent(uint32_t fibnum, const struct in6_addr *paddr, int plen)
  602 {
  603         struct in6_addr lookup_addr = in6mask128;
  604         struct in6_addr addr6 = *paddr;
  605         struct in6_addr mask6;
  606         struct rtentry *rt;
  607 
  608         while (plen-- > 0) {
  609                 /* Calculate wider mask & new key to lookup */
  610                 ip6_writemask(&mask6, plen);
  611                 IN6_MASK_ADDR(&addr6, &mask6);
  612                 if (IN6_ARE_ADDR_EQUAL(&addr6, &lookup_addr)) {
  613                         /* Skip lookup if the key is the same */
  614                         continue;
  615                 }
  616                 lookup_addr = addr6;
  617 
  618                 rt = get_inet6_parent_prefix(fibnum, &lookup_addr, plen);
  619                 if (rt != NULL)
  620                         return (rt);
  621         }
  622 
  623         return (NULL);
  624 }
  625 #endif
  626 
  627 /*
  628  * Prints rtentry @rt data in the provided @buf.
  629  * Example: rt/192.168.0.0/24
  630  */
  631 char *
  632 rt_print_buf(const struct rtentry *rt, char *buf, size_t bufsize)
  633 {
  634 #if defined(INET) || defined(INET6)
  635         char abuf[INET6_ADDRSTRLEN];
  636         uint32_t scopeid;
  637         int plen;
  638 #endif
  639 
  640         switch (rt_get_family(rt)) {
  641 #ifdef INET
  642         case AF_INET:
  643                 {
  644                         struct in_addr addr4;
  645                         rt_get_inet_prefix_plen(rt, &addr4, &plen, &scopeid);
  646                         inet_ntop(AF_INET, &addr4, abuf, sizeof(abuf));
  647                         snprintf(buf, bufsize, "rt/%s/%d", abuf, plen);
  648                 }
  649                 break;
  650 #endif
  651 #ifdef INET6
  652         case AF_INET6:
  653                 {
  654                         struct in6_addr addr6;
  655                         rt_get_inet6_prefix_plen(rt, &addr6, &plen, &scopeid);
  656                         inet_ntop(AF_INET6, &addr6, abuf, sizeof(abuf));
  657                         snprintf(buf, bufsize, "rt/%s/%d", abuf, plen);
  658                 }
  659                 break;
  660 #endif
  661         default:
  662                 snprintf(buf, bufsize, "rt/unknown_af#%d", rt_get_family(rt));
  663                 break;
  664         }
  665 
  666         return (buf);
  667 }
  668 
  669 const char *
  670 rib_print_cmd(int rib_cmd)
  671 {
  672         switch (rib_cmd) {
  673         case RTM_ADD:
  674                 return ("RTM_ADD");
  675         case RTM_CHANGE:
  676                 return ("RTM_CHANGE");
  677         case RTM_DELETE:
  678                 return ("RTM_DELETE");
  679         case RTM_GET:
  680                 return ("RTM_GET");
  681         }
  682 
  683         return ("UNKNOWN");
  684 }

Cache object: 6acc4ec04535cb444121879c11cdc465


[ 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.