back to topotato report
topotato coverage report
Current view: top level - lib - bfd.c (source / functions) Hit Total Coverage
Test: test_pim_cbsr.py::PIMCandidateBSRTest Lines: 38 539 7.1 %
Date: 2023-02-16 02:09:14 Functions: 10 53 18.9 %

          Line data    Source code
       1             : /**
       2             :  * bfd.c: BFD handling routines
       3             :  *
       4             :  * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
       5             :  *
       6             :  * This file is part of GNU Zebra.
       7             :  *
       8             :  * GNU Zebra is free software; you can redistribute it and/or modify it
       9             :  * under the terms of the GNU General Public License as published by the
      10             :  * Free Software Foundation; either version 2, or (at your option) any
      11             :  * later version.
      12             :  *
      13             :  * GNU Zebra is distributed in the hope that it will be useful, but
      14             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16             :  * General Public License for more details.
      17             :  *
      18             :  * You should have received a copy of the GNU General Public License along
      19             :  * with this program; see the file COPYING; if not, write to the Free Software
      20             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      21             :  */
      22             : 
      23             : #include <zebra.h>
      24             : 
      25             : #include "command.h"
      26             : #include "memory.h"
      27             : #include "prefix.h"
      28             : #include "thread.h"
      29             : #include "stream.h"
      30             : #include "vrf.h"
      31             : #include "zclient.h"
      32             : #include "libfrr.h"
      33             : #include "table.h"
      34             : #include "vty.h"
      35             : #include "bfd.h"
      36             : 
      37          27 : DEFINE_MTYPE_STATIC(LIB, BFD_INFO, "BFD info");
      38          27 : DEFINE_MTYPE_STATIC(LIB, BFD_SOURCE, "BFD source cache");
      39             : 
      40             : /**
      41             :  * BFD protocol integration configuration.
      42             :  */
      43             : 
      44             : /** Events definitions. */
      45             : enum bfd_session_event {
      46             :         /** Remove the BFD session configuration. */
      47             :         BSE_UNINSTALL,
      48             :         /** Install the BFD session configuration. */
      49             :         BSE_INSTALL,
      50             : };
      51             : 
      52             : /**
      53             :  * BFD source selection result cache.
      54             :  *
      55             :  * This structure will keep track of the result based on the destination
      56             :  * prefix. When the result changes all related BFD sessions with automatic
      57             :  * source will be updated.
      58             :  */
      59             : struct bfd_source_cache {
      60             :         /** Address VRF belongs. */
      61             :         vrf_id_t vrf_id;
      62             :         /** Destination network address. */
      63             :         struct prefix address;
      64             :         /** Source selected. */
      65             :         struct prefix source;
      66             :         /** Is the source address valid? */
      67             :         bool valid;
      68             :         /** BFD sessions using this. */
      69             :         size_t refcount;
      70             : 
      71             :         SLIST_ENTRY(bfd_source_cache) entry;
      72             : };
      73             : SLIST_HEAD(bfd_source_list, bfd_source_cache);
      74             : 
      75             : /**
      76             :  * Data structure to do the necessary tricks to hide the BFD protocol
      77             :  * integration internals.
      78             :  */
      79             : struct bfd_session_params {
      80             :         /** Contains the session parameters and more. */
      81             :         struct bfd_session_arg args;
      82             :         /** Contains the session state. */
      83             :         struct bfd_session_status bss;
      84             :         /** Protocol implementation status update callback. */
      85             :         bsp_status_update updatecb;
      86             :         /** Protocol implementation custom data pointer. */
      87             :         void *arg;
      88             : 
      89             :         /**
      90             :          * Next event.
      91             :          *
      92             :          * This variable controls what action to execute when the command batch
      93             :          * finishes. Normally we'd use `thread_add_event` value, however since
      94             :          * that function is going to be called multiple times and the value
      95             :          * might be different we'll use this variable to keep track of it.
      96             :          */
      97             :         enum bfd_session_event lastev;
      98             :         /**
      99             :          * BFD session configuration event.
     100             :          *
     101             :          * Multiple actions might be asked during a command batch (either via
     102             :          * configuration load or northbound batch), so we'll use this to
     103             :          * install/uninstall the BFD session parameters only once.
     104             :          */
     105             :         struct thread *installev;
     106             : 
     107             :         /** BFD session installation state. */
     108             :         bool installed;
     109             : 
     110             :         /** Automatic source selection. */
     111             :         bool auto_source;
     112             :         /** Currently selected source. */
     113             :         struct bfd_source_cache *source_cache;
     114             : 
     115             :         /** Global BFD paramaters list. */
     116             :         TAILQ_ENTRY(bfd_session_params) entry;
     117             : };
     118             : 
     119             : struct bfd_sessions_global {
     120             :         /**
     121             :          * Global BFD session parameters list for (re)installation and update
     122             :          * without code duplication among daemons.
     123             :          */
     124             :         TAILQ_HEAD(bsplist, bfd_session_params) bsplist;
     125             :         /** BFD automatic source selection cache. */
     126             :         struct bfd_source_list source_list;
     127             : 
     128             :         /** Pointer to FRR's event manager. */
     129             :         struct thread_master *tm;
     130             :         /** Pointer to zebra client data structure. */
     131             :         struct zclient *zc;
     132             : 
     133             :         /** Debugging state. */
     134             :         bool debugging;
     135             :         /** Is shutting down? */
     136             :         bool shutting_down;
     137             : };
     138             : 
     139             : /** Global configuration variable. */
     140             : static struct bfd_sessions_global bsglobal;
     141             : 
     142             : /** Global empty address for IPv4/IPv6. */
     143             : static const struct in6_addr i6a_zero;
     144             : 
     145             : /*
     146             :  * Prototypes
     147             :  */
     148             : 
     149             : static void bfd_source_cache_get(struct bfd_session_params *session);
     150             : static void bfd_source_cache_put(struct bfd_session_params *session);
     151             : 
     152             : /*
     153             :  * bfd_get_peer_info - Extract the Peer information for which the BFD session
     154             :  *                     went down from the message sent from Zebra to clients.
     155             :  */
     156           0 : static struct interface *bfd_get_peer_info(struct stream *s, struct prefix *dp,
     157             :                                            struct prefix *sp, int *status,
     158             :                                            int *remote_cbit, vrf_id_t vrf_id)
     159             : {
     160           0 :         unsigned int ifindex;
     161           0 :         struct interface *ifp = NULL;
     162           0 :         int plen;
     163           0 :         int local_remote_cbit;
     164             : 
     165             :         /*
     166             :          * If the ifindex lookup fails the
     167             :          * rest of the data in the stream is
     168             :          * not read.  All examples of this function
     169             :          * call immediately use the dp->family which
     170             :          * is not good.  Ensure we are not using
     171             :          * random data
     172             :          */
     173           0 :         memset(dp, 0, sizeof(*dp));
     174           0 :         memset(sp, 0, sizeof(*sp));
     175             : 
     176             :         /* Get interface index. */
     177           0 :         STREAM_GETL(s, ifindex);
     178             : 
     179             :         /* Lookup index. */
     180           0 :         if (ifindex != 0) {
     181           0 :                 ifp = if_lookup_by_index(ifindex, vrf_id);
     182           0 :                 if (ifp == NULL) {
     183           0 :                         if (bsglobal.debugging)
     184           0 :                                 zlog_debug(
     185             :                                         "%s: Can't find interface by ifindex: %d ",
     186             :                                         __func__, ifindex);
     187           0 :                         return NULL;
     188             :                 }
     189             :         }
     190             : 
     191             :         /* Fetch destination address. */
     192           0 :         STREAM_GETC(s, dp->family);
     193             : 
     194           0 :         plen = prefix_blen(dp);
     195           0 :         STREAM_GET(&dp->u.prefix, s, plen);
     196           0 :         STREAM_GETC(s, dp->prefixlen);
     197             : 
     198             :         /* Get BFD status. */
     199           0 :         STREAM_GETL(s, (*status));
     200             : 
     201           0 :         STREAM_GETC(s, sp->family);
     202             : 
     203           0 :         plen = prefix_blen(sp);
     204           0 :         STREAM_GET(&sp->u.prefix, s, plen);
     205           0 :         STREAM_GETC(s, sp->prefixlen);
     206             : 
     207           0 :         STREAM_GETC(s, local_remote_cbit);
     208           0 :         if (remote_cbit)
     209           0 :                 *remote_cbit = local_remote_cbit;
     210             :         return ifp;
     211             : 
     212           0 : stream_failure:
     213             :         /*
     214             :          * Clean dp and sp because caller
     215             :          * will immediately check them valid or not
     216             :          */
     217           0 :         memset(dp, 0, sizeof(*dp));
     218           0 :         memset(sp, 0, sizeof(*sp));
     219           0 :         return NULL;
     220             : }
     221             : 
     222             : /*
     223             :  * bfd_get_status_str - Convert BFD status to a display string.
     224             :  */
     225           0 : const char *bfd_get_status_str(int status)
     226             : {
     227           0 :         switch (status) {
     228             :         case BFD_STATUS_DOWN:
     229             :                 return "Down";
     230           0 :         case BFD_STATUS_UP:
     231           0 :                 return "Up";
     232           0 :         case BFD_STATUS_ADMIN_DOWN:
     233           0 :                 return "Admin Down";
     234           0 :         case BFD_STATUS_UNKNOWN:
     235             :         default:
     236           0 :                 return "Unknown";
     237             :         }
     238             : }
     239             : 
     240             : /*
     241             :  * bfd_last_update - Calculate the last BFD update time and convert it
     242             :  *                   into a dd:hh:mm:ss display format.
     243             :  */
     244           0 : static void bfd_last_update(time_t last_update, char *buf, size_t len)
     245             : {
     246           0 :         time_t curr;
     247           0 :         time_t diff;
     248           0 :         struct tm tm;
     249           0 :         struct timeval tv;
     250             : 
     251             :         /* If no BFD status update has ever been received, print `never'. */
     252           0 :         if (last_update == 0) {
     253           0 :                 snprintf(buf, len, "never");
     254           0 :                 return;
     255             :         }
     256             : 
     257             :         /* Get current time. */
     258           0 :         monotime(&tv);
     259           0 :         curr = tv.tv_sec;
     260           0 :         diff = curr - last_update;
     261           0 :         gmtime_r(&diff, &tm);
     262             : 
     263           0 :         snprintf(buf, len, "%d:%02d:%02d:%02d", tm.tm_yday, tm.tm_hour,
     264             :                  tm.tm_min, tm.tm_sec);
     265             : }
     266             : 
     267             : /*
     268             :  * bfd_client_sendmsg - Format and send a client register
     269             :  *                    command to Zebra to be forwarded to BFD
     270             :  */
     271           9 : void bfd_client_sendmsg(struct zclient *zclient, int command,
     272             :                         vrf_id_t vrf_id)
     273             : {
     274           9 :         struct stream *s;
     275           9 :         enum zclient_send_status ret;
     276             : 
     277             :         /* Check socket. */
     278           9 :         if (!zclient || zclient->sock < 0) {
     279           6 :                 if (bsglobal.debugging)
     280           0 :                         zlog_debug(
     281             :                                 "%s: Can't send BFD client register, Zebra client not established",
     282             :                                 __func__);
     283           6 :                 return;
     284             :         }
     285             : 
     286           3 :         s = zclient->obuf;
     287           3 :         stream_reset(s);
     288           3 :         zclient_create_header(s, command, vrf_id);
     289             : 
     290           3 :         stream_putl(s, getpid());
     291             : 
     292           3 :         stream_putw_at(s, 0, stream_get_endp(s));
     293             : 
     294           3 :         ret = zclient_send_message(zclient);
     295             : 
     296           3 :         if (ret == ZCLIENT_SEND_FAILURE) {
     297           0 :                 if (bsglobal.debugging)
     298           0 :                         zlog_debug(
     299             :                                 "%s:  %ld: zclient_send_message() failed",
     300             :                                 __func__, (long)getpid());
     301           0 :                 return;
     302             :         }
     303             : 
     304             :         return;
     305             : }
     306             : 
     307           0 : int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args)
     308             : {
     309           0 :         struct stream *s;
     310           0 :         size_t addrlen;
     311             : 
     312             :         /* Individual reg/dereg messages are suppressed during shutdown. */
     313           0 :         if (bsglobal.shutting_down) {
     314           0 :                 if (bsglobal.debugging)
     315           0 :                         zlog_debug(
     316             :                                 "%s: Suppressing BFD peer reg/dereg messages",
     317             :                                 __func__);
     318           0 :                 return -1;
     319             :         }
     320             : 
     321             :         /* Check socket. */
     322           0 :         if (!zc || zc->sock < 0) {
     323           0 :                 if (bsglobal.debugging)
     324           0 :                         zlog_debug("%s: zclient unavailable", __func__);
     325           0 :                 return -1;
     326             :         }
     327             : 
     328           0 :         s = zc->obuf;
     329           0 :         stream_reset(s);
     330             : 
     331             :         /* Create new message. */
     332           0 :         zclient_create_header(s, args->command, args->vrf_id);
     333           0 :         stream_putl(s, getpid());
     334             : 
     335             :         /* Encode destination address. */
     336           0 :         stream_putw(s, args->family);
     337           0 :         addrlen = (args->family == AF_INET) ? sizeof(struct in_addr)
     338           0 :                                             : sizeof(struct in6_addr);
     339           0 :         stream_put(s, &args->dst, addrlen);
     340             : 
     341             :         /*
     342             :          * For more BFD integration protocol details, see function
     343             :          * `_ptm_msg_read` in `bfdd/ptm_adapter.c`.
     344             :          */
     345             : #if HAVE_BFDD > 0
     346             :         /* Session timers. */
     347           0 :         stream_putl(s, args->min_rx);
     348           0 :         stream_putl(s, args->min_tx);
     349           0 :         stream_putc(s, args->detection_multiplier);
     350             : 
     351             :         /* Is multi hop? */
     352           0 :         stream_putc(s, args->mhop != 0);
     353             : 
     354             :         /* Source address. */
     355           0 :         stream_putw(s, args->family);
     356           0 :         stream_put(s, &args->src, addrlen);
     357             : 
     358             :         /* Send the expected hops. */
     359           0 :         stream_putc(s, args->hops);
     360             : 
     361             :         /* Send interface name if any. */
     362           0 :         if (args->mhop) {
     363             :                 /* Don't send interface. */
     364           0 :                 stream_putc(s, 0);
     365           0 :                 if (bsglobal.debugging && args->ifnamelen)
     366           0 :                         zlog_debug("%s: multi hop is configured, not sending interface",
     367             :                                    __func__);
     368             :         } else {
     369           0 :                 stream_putc(s, args->ifnamelen);
     370           0 :                 if (args->ifnamelen)
     371           0 :                         stream_put(s, args->ifname, args->ifnamelen);
     372             :         }
     373             : 
     374             :         /* Send the C bit indicator. */
     375           0 :         stream_putc(s, args->cbit);
     376             : 
     377             :         /* Send profile name if any. */
     378           0 :         stream_putc(s, args->profilelen);
     379           0 :         if (args->profilelen)
     380           0 :                 stream_put(s, args->profile, args->profilelen);
     381             : #else /* PTM BFD */
     382             :         /* Encode timers if this is a registration message. */
     383             :         if (args->command != ZEBRA_BFD_DEST_DEREGISTER) {
     384             :                 stream_putl(s, args->min_rx);
     385             :                 stream_putl(s, args->min_tx);
     386             :                 stream_putc(s, args->detection_multiplier);
     387             :         }
     388             : 
     389             :         if (args->mhop) {
     390             :                 /* Multi hop indicator. */
     391             :                 stream_putc(s, 1);
     392             : 
     393             :                 /* Multi hop always sends the source address. */
     394             :                 stream_putw(s, args->family);
     395             :                 stream_put(s, &args->src, addrlen);
     396             : 
     397             :                 /* Send the expected hops. */
     398             :                 stream_putc(s, args->hops);
     399             :         } else {
     400             :                 /* Multi hop indicator. */
     401             :                 stream_putc(s, 0);
     402             : 
     403             :                 /* Single hop only sends the source address when IPv6. */
     404             :                 if (args->family == AF_INET6) {
     405             :                         stream_putw(s, args->family);
     406             :                         stream_put(s, &args->src, addrlen);
     407             :                 }
     408             : 
     409             :                 /* Send interface name if any. */
     410             :                 stream_putc(s, args->ifnamelen);
     411             :                 if (args->ifnamelen)
     412             :                         stream_put(s, args->ifname, args->ifnamelen);
     413             :         }
     414             : 
     415             :         /* Send the C bit indicator. */
     416             :         stream_putc(s, args->cbit);
     417             : #endif /* HAVE_BFDD */
     418             : 
     419             :         /* Finish the message by writing the size. */
     420           0 :         stream_putw_at(s, 0, stream_get_endp(s));
     421             : 
     422             :         /* Send message to zebra. */
     423           0 :         if (zclient_send_message(zc) == ZCLIENT_SEND_FAILURE) {
     424           0 :                 if (bsglobal.debugging)
     425           0 :                         zlog_debug("%s: zclient_send_message failed", __func__);
     426           0 :                 return -1;
     427             :         }
     428             : 
     429             :         return 0;
     430             : }
     431             : 
     432           0 : struct bfd_session_params *bfd_sess_new(bsp_status_update updatecb, void *arg)
     433             : {
     434           0 :         struct bfd_session_params *bsp;
     435             : 
     436           0 :         bsp = XCALLOC(MTYPE_BFD_INFO, sizeof(*bsp));
     437             : 
     438             :         /* Save application data. */
     439           0 :         bsp->updatecb = updatecb;
     440           0 :         bsp->arg = arg;
     441             : 
     442             :         /* Set defaults. */
     443           0 :         bsp->args.detection_multiplier = BFD_DEF_DETECT_MULT;
     444           0 :         bsp->args.hops = 1;
     445           0 :         bsp->args.min_rx = BFD_DEF_MIN_RX;
     446           0 :         bsp->args.min_tx = BFD_DEF_MIN_TX;
     447           0 :         bsp->args.vrf_id = VRF_DEFAULT;
     448             : 
     449             :         /* Register in global list. */
     450           0 :         TAILQ_INSERT_TAIL(&bsglobal.bsplist, bsp, entry);
     451             : 
     452           0 :         return bsp;
     453             : }
     454             : 
     455           0 : static bool _bfd_sess_valid(const struct bfd_session_params *bsp)
     456             : {
     457             :         /* Peer/local address not configured. */
     458           0 :         if (bsp->args.family == 0)
     459             :                 return false;
     460             : 
     461             :         /* Address configured but invalid. */
     462           0 :         if (bsp->args.family != AF_INET && bsp->args.family != AF_INET6) {
     463           0 :                 if (bsglobal.debugging)
     464           0 :                         zlog_debug("%s: invalid session family: %d", __func__,
     465             :                                    bsp->args.family);
     466           0 :                 return false;
     467             :         }
     468             : 
     469             :         /* Invalid address. */
     470           0 :         if (memcmp(&bsp->args.dst, &i6a_zero, sizeof(i6a_zero)) == 0) {
     471           0 :                 if (bsglobal.debugging) {
     472           0 :                         if (bsp->args.family == AF_INET)
     473           0 :                                 zlog_debug("%s: invalid address: %pI4",
     474             :                                            __func__,
     475             :                                            (struct in_addr *)&bsp->args.dst);
     476             :                         else
     477           0 :                                 zlog_debug("%s: invalid address: %pI6",
     478             :                                            __func__, &bsp->args.dst);
     479             :                 }
     480           0 :                 return false;
     481             :         }
     482             : 
     483             :         /* Multi hop requires local address. */
     484           0 :         if (bsp->args.mhop
     485           0 :             && memcmp(&i6a_zero, &bsp->args.src, sizeof(i6a_zero)) == 0) {
     486           0 :                 if (bsglobal.debugging)
     487           0 :                         zlog_debug(
     488             :                                 "%s: multi hop but no local address provided",
     489             :                                 __func__);
     490           0 :                 return false;
     491             :         }
     492             : 
     493             :         /* Check VRF ID. */
     494           0 :         if (bsp->args.vrf_id == VRF_UNKNOWN) {
     495           0 :                 if (bsglobal.debugging)
     496           0 :                         zlog_debug("%s: asked for unknown VRF", __func__);
     497           0 :                 return false;
     498             :         }
     499             : 
     500             :         return true;
     501             : }
     502             : 
     503           0 : static void _bfd_sess_send(struct thread *t)
     504             : {
     505           0 :         struct bfd_session_params *bsp = THREAD_ARG(t);
     506           0 :         int rv;
     507             : 
     508             :         /* Validate configuration before trying to send bogus data. */
     509           0 :         if (!_bfd_sess_valid(bsp))
     510             :                 return;
     511             : 
     512           0 :         if (bsp->lastev == BSE_INSTALL) {
     513           0 :                 bsp->args.command = bsp->installed ? ZEBRA_BFD_DEST_UPDATE
     514           0 :                                                    : ZEBRA_BFD_DEST_REGISTER;
     515             :         } else
     516           0 :                 bsp->args.command = ZEBRA_BFD_DEST_DEREGISTER;
     517             : 
     518             :         /* If not installed and asked for uninstall, do nothing. */
     519           0 :         if (!bsp->installed && bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER)
     520             :                 return;
     521             : 
     522           0 :         rv = zclient_bfd_command(bsglobal.zc, &bsp->args);
     523             :         /* Command was sent successfully. */
     524           0 :         if (rv == 0) {
     525             :                 /* Update installation status. */
     526           0 :                 if (bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER)
     527           0 :                         bsp->installed = false;
     528           0 :                 else if (bsp->args.command == ZEBRA_BFD_DEST_REGISTER)
     529           0 :                         bsp->installed = true;
     530             :         } else {
     531           0 :                 struct ipaddr src, dst;
     532             : 
     533           0 :                 src.ipa_type = bsp->args.family;
     534           0 :                 src.ipaddr_v6 = bsp->args.src;
     535           0 :                 dst.ipa_type = bsp->args.family;
     536           0 :                 dst.ipaddr_v6 = bsp->args.dst;
     537             : 
     538           0 :                 zlog_err(
     539             :                         "%s: BFD session %pIA -> %pIA interface %s VRF %s(%u) was not %s",
     540             :                         __func__, &src, &dst,
     541             :                         bsp->args.ifnamelen ? bsp->args.ifname : "*",
     542             :                         vrf_id_to_name(bsp->args.vrf_id), bsp->args.vrf_id,
     543             :                         bsp->lastev == BSE_INSTALL ? "installed"
     544             :                                                    : "uninstalled");
     545             :         }
     546             : }
     547             : 
     548           0 : static void _bfd_sess_remove(struct bfd_session_params *bsp)
     549             : {
     550             :         /* Not installed, nothing to do. */
     551           0 :         if (!bsp->installed)
     552             :                 return;
     553             : 
     554             :         /* Cancel any pending installation request. */
     555           0 :         THREAD_OFF(bsp->installev);
     556             : 
     557             :         /* Send request to remove any session. */
     558           0 :         bsp->lastev = BSE_UNINSTALL;
     559           0 :         thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
     560             : }
     561             : 
     562           6 : void bfd_sess_free(struct bfd_session_params **bsp)
     563             : {
     564           6 :         if (*bsp == NULL)
     565             :                 return;
     566             : 
     567             :         /* Remove any installed session. */
     568           0 :         _bfd_sess_remove(*bsp);
     569             : 
     570             :         /* Remove from global list. */
     571           0 :         TAILQ_REMOVE(&bsglobal.bsplist, (*bsp), entry);
     572             : 
     573           0 :         bfd_source_cache_put(*bsp);
     574             : 
     575             :         /* Free the memory and point to NULL. */
     576           0 :         XFREE(MTYPE_BFD_INFO, (*bsp));
     577             : }
     578             : 
     579           0 : static bool bfd_sess_address_changed(const struct bfd_session_params *bsp,
     580             :                                      uint32_t family,
     581             :                                      const struct in6_addr *src,
     582             :                                      const struct in6_addr *dst)
     583             : {
     584           0 :         size_t addrlen;
     585             : 
     586           0 :         if (bsp->args.family != family)
     587             :                 return true;
     588             : 
     589           0 :         addrlen = (family == AF_INET) ? sizeof(struct in_addr)
     590           0 :                                       : sizeof(struct in6_addr);
     591           0 :         if ((src == NULL && memcmp(&bsp->args.src, &i6a_zero, addrlen))
     592           0 :             || (src && memcmp(src, &bsp->args.src, addrlen))
     593           0 :             || memcmp(dst, &bsp->args.dst, addrlen))
     594           0 :                 return true;
     595             : 
     596             :         return false;
     597             : }
     598             : 
     599           0 : void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp,
     600             :                              const struct in_addr *src,
     601             :                              const struct in_addr *dst)
     602             : {
     603           0 :         if (!bfd_sess_address_changed(bsp, AF_INET, (struct in6_addr *)src,
     604             :                                       (struct in6_addr *)dst))
     605             :                 return;
     606             : 
     607             :         /* If already installed, remove the old setting. */
     608           0 :         _bfd_sess_remove(bsp);
     609             :         /* Address changed so we must reapply auto source. */
     610           0 :         bfd_source_cache_put(bsp);
     611             : 
     612           0 :         bsp->args.family = AF_INET;
     613             : 
     614             :         /* Clean memory, set zero value and avoid static analyser warnings. */
     615           0 :         memset(&bsp->args.src, 0, sizeof(bsp->args.src));
     616           0 :         memset(&bsp->args.dst, 0, sizeof(bsp->args.dst));
     617             : 
     618             :         /* Copy the equivalent of IPv4 to arguments structure. */
     619           0 :         if (src)
     620           0 :                 memcpy(&bsp->args.src, src, sizeof(struct in_addr));
     621             : 
     622           0 :         assert(dst);
     623           0 :         memcpy(&bsp->args.dst, dst, sizeof(struct in_addr));
     624             : 
     625           0 :         if (bsp->auto_source)
     626           0 :                 bfd_source_cache_get(bsp);
     627             : }
     628             : 
     629           0 : void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
     630             :                              const struct in6_addr *src,
     631             :                              const struct in6_addr *dst)
     632             : {
     633           0 :         if (!bfd_sess_address_changed(bsp, AF_INET6, src, dst))
     634             :                 return;
     635             : 
     636             :         /* If already installed, remove the old setting. */
     637           0 :         _bfd_sess_remove(bsp);
     638             :         /* Address changed so we must reapply auto source. */
     639           0 :         bfd_source_cache_put(bsp);
     640             : 
     641           0 :         bsp->args.family = AF_INET6;
     642             : 
     643             :         /* Clean memory, set zero value and avoid static analyser warnings. */
     644           0 :         memset(&bsp->args.src, 0, sizeof(bsp->args.src));
     645             : 
     646           0 :         if (src)
     647           0 :                 bsp->args.src = *src;
     648             : 
     649           0 :         assert(dst);
     650           0 :         bsp->args.dst = *dst;
     651             : 
     652           0 :         if (bsp->auto_source)
     653           0 :                 bfd_source_cache_get(bsp);
     654             : }
     655             : 
     656           0 : void bfd_sess_set_interface(struct bfd_session_params *bsp, const char *ifname)
     657             : {
     658           0 :         if ((ifname == NULL && bsp->args.ifnamelen == 0)
     659           0 :             || (ifname && strcmp(bsp->args.ifname, ifname) == 0))
     660             :                 return;
     661             : 
     662             :         /* If already installed, remove the old setting. */
     663           0 :         _bfd_sess_remove(bsp);
     664             : 
     665           0 :         if (ifname == NULL) {
     666           0 :                 bsp->args.ifname[0] = 0;
     667           0 :                 bsp->args.ifnamelen = 0;
     668           0 :                 return;
     669             :         }
     670             : 
     671           0 :         if (strlcpy(bsp->args.ifname, ifname, sizeof(bsp->args.ifname))
     672             :             > sizeof(bsp->args.ifname))
     673           0 :                 zlog_warn("%s: interface name truncated: %s", __func__, ifname);
     674             : 
     675           0 :         bsp->args.ifnamelen = strlen(bsp->args.ifname);
     676             : }
     677             : 
     678           0 : void bfd_sess_set_profile(struct bfd_session_params *bsp, const char *profile)
     679             : {
     680           0 :         if (profile == NULL) {
     681           0 :                 bsp->args.profile[0] = 0;
     682           0 :                 bsp->args.profilelen = 0;
     683           0 :                 return;
     684             :         }
     685             : 
     686           0 :         if (strlcpy(bsp->args.profile, profile, sizeof(bsp->args.profile))
     687             :             > sizeof(bsp->args.profile))
     688           0 :                 zlog_warn("%s: profile name truncated: %s", __func__, profile);
     689             : 
     690           0 :         bsp->args.profilelen = strlen(bsp->args.profile);
     691             : }
     692             : 
     693           0 : void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id)
     694             : {
     695           0 :         if (bsp->args.vrf_id == vrf_id)
     696             :                 return;
     697             : 
     698             :         /* If already installed, remove the old setting. */
     699           0 :         _bfd_sess_remove(bsp);
     700             :         /* Address changed so we must reapply auto source. */
     701           0 :         bfd_source_cache_put(bsp);
     702             : 
     703           0 :         bsp->args.vrf_id = vrf_id;
     704             : 
     705           0 :         if (bsp->auto_source)
     706           0 :                 bfd_source_cache_get(bsp);
     707             : }
     708             : 
     709           0 : void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t hops)
     710             : {
     711           0 :         if (bsp->args.hops == hops)
     712             :                 return;
     713             : 
     714             :         /* If already installed, remove the old setting. */
     715           0 :         _bfd_sess_remove(bsp);
     716             : 
     717           0 :         bsp->args.hops = hops;
     718           0 :         bsp->args.mhop = (hops > 1);
     719             : }
     720             : 
     721             : 
     722           0 : void bfd_sess_set_cbit(struct bfd_session_params *bsp, bool enable)
     723             : {
     724           0 :         bsp->args.cbit = enable;
     725           0 : }
     726             : 
     727           0 : void bfd_sess_set_timers(struct bfd_session_params *bsp,
     728             :                          uint8_t detection_multiplier, uint32_t min_rx,
     729             :                          uint32_t min_tx)
     730             : {
     731           0 :         bsp->args.detection_multiplier = detection_multiplier;
     732           0 :         bsp->args.min_rx = min_rx;
     733           0 :         bsp->args.min_tx = min_tx;
     734           0 : }
     735             : 
     736           0 : void bfd_sess_set_auto_source(struct bfd_session_params *bsp, bool enable)
     737             : {
     738           0 :         if (bsp->auto_source == enable)
     739             :                 return;
     740             : 
     741           0 :         bsp->auto_source = enable;
     742           0 :         if (enable)
     743           0 :                 bfd_source_cache_get(bsp);
     744             :         else
     745           0 :                 bfd_source_cache_put(bsp);
     746             : }
     747             : 
     748           0 : void bfd_sess_install(struct bfd_session_params *bsp)
     749             : {
     750           0 :         bsp->lastev = BSE_INSTALL;
     751           0 :         thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
     752           0 : }
     753             : 
     754           0 : void bfd_sess_uninstall(struct bfd_session_params *bsp)
     755             : {
     756           0 :         bsp->lastev = BSE_UNINSTALL;
     757           0 :         thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
     758           0 : }
     759             : 
     760           0 : enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp)
     761             : {
     762           0 :         return bsp->bss.state;
     763             : }
     764             : 
     765           0 : uint8_t bfd_sess_hop_count(const struct bfd_session_params *bsp)
     766             : {
     767           0 :         return bsp->args.hops;
     768             : }
     769             : 
     770           0 : const char *bfd_sess_profile(const struct bfd_session_params *bsp)
     771             : {
     772           0 :         return bsp->args.profilelen ? bsp->args.profile : NULL;
     773             : }
     774             : 
     775           0 : void bfd_sess_addresses(const struct bfd_session_params *bsp, int *family,
     776             :                         struct in6_addr *src, struct in6_addr *dst)
     777             : {
     778           0 :         *family = bsp->args.family;
     779           0 :         if (src)
     780           0 :                 *src = bsp->args.src;
     781           0 :         if (dst)
     782           0 :                 *dst = bsp->args.dst;
     783           0 : }
     784             : 
     785           0 : const char *bfd_sess_interface(const struct bfd_session_params *bsp)
     786             : {
     787           0 :         if (bsp->args.ifnamelen)
     788           0 :                 return bsp->args.ifname;
     789             : 
     790             :         return NULL;
     791             : }
     792             : 
     793           0 : const char *bfd_sess_vrf(const struct bfd_session_params *bsp)
     794             : {
     795           0 :         return vrf_id_to_name(bsp->args.vrf_id);
     796             : }
     797             : 
     798           0 : vrf_id_t bfd_sess_vrf_id(const struct bfd_session_params *bsp)
     799             : {
     800           0 :         return bsp->args.vrf_id;
     801             : }
     802             : 
     803           0 : bool bfd_sess_cbit(const struct bfd_session_params *bsp)
     804             : {
     805           0 :         return bsp->args.cbit;
     806             : }
     807             : 
     808           0 : void bfd_sess_timers(const struct bfd_session_params *bsp,
     809             :                      uint8_t *detection_multiplier, uint32_t *min_rx,
     810             :                      uint32_t *min_tx)
     811             : {
     812           0 :         *detection_multiplier = bsp->args.detection_multiplier;
     813           0 :         *min_rx = bsp->args.min_rx;
     814           0 :         *min_tx = bsp->args.min_tx;
     815           0 : }
     816             : 
     817           0 : bool bfd_sess_auto_source(const struct bfd_session_params *bsp)
     818             : {
     819           0 :         return bsp->auto_source;
     820             : }
     821             : 
     822           0 : void bfd_sess_show(struct vty *vty, struct json_object *json,
     823             :                    struct bfd_session_params *bsp)
     824             : {
     825           0 :         json_object *json_bfd = NULL;
     826           0 :         char time_buf[64];
     827             : 
     828           0 :         if (!bsp)
     829           0 :                 return;
     830             : 
     831             :         /* Show type. */
     832           0 :         if (json) {
     833           0 :                 json_bfd = json_object_new_object();
     834           0 :                 if (bsp->args.mhop)
     835           0 :                         json_object_string_add(json_bfd, "type", "multi hop");
     836             :                 else
     837           0 :                         json_object_string_add(json_bfd, "type", "single hop");
     838             :         } else
     839           0 :                 vty_out(vty, "  BFD: Type: %s\n",
     840           0 :                         bsp->args.mhop ? "multi hop" : "single hop");
     841             : 
     842             :         /* Show configuration. */
     843           0 :         if (json) {
     844           0 :                 json_object_int_add(json_bfd, "detectMultiplier",
     845           0 :                                     bsp->args.detection_multiplier);
     846           0 :                 json_object_int_add(json_bfd, "rxMinInterval",
     847           0 :                                     bsp->args.min_rx);
     848           0 :                 json_object_int_add(json_bfd, "txMinInterval",
     849           0 :                                     bsp->args.min_tx);
     850             :         } else {
     851           0 :                 vty_out(vty,
     852             :                         "  Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n",
     853             :                         bsp->args.detection_multiplier, bsp->args.min_rx,
     854             :                         bsp->args.min_tx);
     855             :         }
     856             : 
     857           0 :         bfd_last_update(bsp->bss.last_event, time_buf, sizeof(time_buf));
     858           0 :         if (json) {
     859           0 :                 json_object_string_add(json_bfd, "status",
     860           0 :                                        bfd_get_status_str(bsp->bss.state));
     861           0 :                 json_object_string_add(json_bfd, "lastUpdate", time_buf);
     862             :         } else
     863           0 :                 vty_out(vty, "  Status: %s, Last update: %s\n",
     864           0 :                         bfd_get_status_str(bsp->bss.state), time_buf);
     865             : 
     866           0 :         if (json)
     867           0 :                 json_object_object_add(json, "peerBfdInfo", json_bfd);
     868             :         else
     869           0 :                 vty_out(vty, "\n");
     870             : }
     871             : 
     872             : /*
     873             :  * Zebra communication related.
     874             :  */
     875             : 
     876             : /**
     877             :  * Callback for reinstallation of all registered BFD sessions.
     878             :  *
     879             :  * Use this as `zclient` `bfd_dest_replay` callback.
     880             :  */
     881           0 : int zclient_bfd_session_replay(ZAPI_CALLBACK_ARGS)
     882             : {
     883           0 :         struct bfd_session_params *bsp;
     884             : 
     885           0 :         if (!zclient->bfd_integration)
     886             :                 return 0;
     887             : 
     888             :         /* Do nothing when shutting down. */
     889           0 :         if (bsglobal.shutting_down)
     890             :                 return 0;
     891             : 
     892           0 :         if (bsglobal.debugging)
     893           0 :                 zlog_debug("%s: sending all sessions registered", __func__);
     894             : 
     895             :         /* Send the client registration */
     896           0 :         bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
     897             : 
     898             :         /* Replay all activated peers. */
     899           0 :         TAILQ_FOREACH (bsp, &bsglobal.bsplist, entry) {
     900             :                 /* Skip not installed sessions. */
     901           0 :                 if (!bsp->installed)
     902           0 :                         continue;
     903             : 
     904             :                 /* We are reconnecting, so we must send installation. */
     905           0 :                 bsp->installed = false;
     906             : 
     907             :                 /* Cancel any pending installation request. */
     908           0 :                 THREAD_OFF(bsp->installev);
     909             : 
     910             :                 /* Ask for installation. */
     911           0 :                 bsp->lastev = BSE_INSTALL;
     912           0 :                 thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
     913             :         }
     914             : 
     915             :         return 0;
     916             : }
     917             : 
     918           0 : int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS)
     919             : {
     920           0 :         struct bfd_session_params *bsp, *bspn;
     921           0 :         size_t sessions_updated = 0;
     922           0 :         struct interface *ifp;
     923           0 :         int remote_cbit = false;
     924           0 :         int state = BFD_STATUS_UNKNOWN;
     925           0 :         time_t now;
     926           0 :         size_t addrlen;
     927           0 :         struct prefix dp;
     928           0 :         struct prefix sp;
     929           0 :         char ifstr[128], cbitstr[32];
     930             : 
     931           0 :         if (!zclient->bfd_integration)
     932             :                 return 0;
     933             : 
     934             :         /* Do nothing when shutting down. */
     935           0 :         if (bsglobal.shutting_down)
     936             :                 return 0;
     937             : 
     938           0 :         ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit,
     939             :                                 vrf_id);
     940             :         /*
     941             :          * When interface lookup fails or an invalid stream is read, we must
     942             :          * not proceed otherwise it will trigger an assertion while checking
     943             :          * family type below.
     944             :          */
     945           0 :         if (dp.family == 0 || sp.family == 0)
     946             :                 return 0;
     947             : 
     948           0 :         if (bsglobal.debugging) {
     949           0 :                 ifstr[0] = 0;
     950           0 :                 if (ifp)
     951           0 :                         snprintf(ifstr, sizeof(ifstr), " (interface %s)",
     952           0 :                                  ifp->name);
     953             : 
     954           0 :                 snprintf(cbitstr, sizeof(cbitstr), " (CPI bit %s)",
     955           0 :                          remote_cbit ? "yes" : "no");
     956             : 
     957           0 :                 zlog_debug("%s: %pFX -> %pFX%s VRF %s(%u)%s: %s", __func__, &sp,
     958             :                            &dp, ifstr, vrf_id_to_name(vrf_id), vrf_id, cbitstr,
     959             :                            bfd_get_status_str(state));
     960             :         }
     961             : 
     962           0 :         switch (dp.family) {
     963             :         case AF_INET:
     964             :                 addrlen = sizeof(struct in_addr);
     965             :                 break;
     966           0 :         case AF_INET6:
     967           0 :                 addrlen = sizeof(struct in6_addr);
     968           0 :                 break;
     969             : 
     970             :         default:
     971             :                 /* Unexpected value. */
     972           0 :                 assert(0);
     973             :                 break;
     974             :         }
     975             : 
     976             :         /* Cache current time to avoid multiple monotime clock calls. */
     977           0 :         now = monotime(NULL);
     978             : 
     979             :         /* Notify all matching sessions about update. */
     980           0 :         TAILQ_FOREACH_SAFE (bsp, &bsglobal.bsplist, entry, bspn) {
     981             :                 /* Skip not installed entries. */
     982           0 :                 if (!bsp->installed)
     983           0 :                         continue;
     984             :                 /* Skip different VRFs. */
     985           0 :                 if (bsp->args.vrf_id != vrf_id)
     986           0 :                         continue;
     987             :                 /* Skip different families. */
     988           0 :                 if (bsp->args.family != dp.family)
     989           0 :                         continue;
     990             :                 /* Skip different interface. */
     991           0 :                 if (bsp->args.ifnamelen && ifp
     992           0 :                     && strcmp(bsp->args.ifname, ifp->name) != 0)
     993           0 :                         continue;
     994             :                 /* Skip non matching destination addresses. */
     995           0 :                 if (memcmp(&bsp->args.dst, &dp.u, addrlen) != 0)
     996           0 :                         continue;
     997             :                 /*
     998             :                  * Source comparison test:
     999             :                  * We will only compare source if BFD daemon provided the
    1000             :                  * source address and the protocol set a source address in
    1001             :                  * the configuration otherwise we'll just skip it.
    1002             :                  */
    1003           0 :                 if (sp.family && memcmp(&bsp->args.src, &i6a_zero, addrlen) != 0
    1004           0 :                     && memcmp(&sp.u, &i6a_zero, addrlen) != 0
    1005           0 :                     && memcmp(&bsp->args.src, &sp.u, addrlen) != 0)
    1006           0 :                         continue;
    1007             :                 /* No session state change. */
    1008           0 :                 if ((int)bsp->bss.state == state)
    1009           0 :                         continue;
    1010             : 
    1011           0 :                 bsp->bss.last_event = now;
    1012           0 :                 bsp->bss.previous_state = bsp->bss.state;
    1013           0 :                 bsp->bss.state = state;
    1014           0 :                 bsp->bss.remote_cbit = remote_cbit;
    1015           0 :                 bsp->updatecb(bsp, &bsp->bss, bsp->arg);
    1016           0 :                 sessions_updated++;
    1017             :         }
    1018             : 
    1019           0 :         if (bsglobal.debugging)
    1020           0 :                 zlog_debug("%s:   sessions updated: %zu", __func__,
    1021             :                            sessions_updated);
    1022             : 
    1023             :         return 0;
    1024             : }
    1025             : 
    1026             : /**
    1027             :  * Frees all allocated resources and stops any activity.
    1028             :  *
    1029             :  * Must be called after every BFD session has been successfully
    1030             :  * unconfigured otherwise this function will `free()` any available
    1031             :  * session causing existing pointers to dangle.
    1032             :  *
    1033             :  * This is just a comment, in practice it will be called by the FRR
    1034             :  * library late finish hook. \see `bfd_protocol_integration_init`.
    1035             :  */
    1036           6 : static int bfd_protocol_integration_finish(void)
    1037             : {
    1038           6 :         if (bsglobal.zc == NULL)
    1039             :                 return 0;
    1040             : 
    1041           6 :         while (!TAILQ_EMPTY(&bsglobal.bsplist)) {
    1042           0 :                 struct bfd_session_params *session =
    1043             :                         TAILQ_FIRST(&bsglobal.bsplist);
    1044           0 :                 bfd_sess_free(&session);
    1045             :         }
    1046             : 
    1047             :         /*
    1048             :          * BFD source cache is linked to sessions, if all sessions are gone
    1049             :          * then the source cache must be empty.
    1050             :          */
    1051           6 :         if (!SLIST_EMPTY(&bsglobal.source_list))
    1052           0 :                 zlog_warn("BFD integration source cache not empty");
    1053             : 
    1054             :         return 0;
    1055             : }
    1056             : 
    1057           6 : void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm)
    1058             : {
    1059             :         /* Initialize data structure. */
    1060           6 :         TAILQ_INIT(&bsglobal.bsplist);
    1061           6 :         SLIST_INIT(&bsglobal.source_list);
    1062             : 
    1063             :         /* Copy pointers. */
    1064           6 :         bsglobal.zc = zc;
    1065           6 :         bsglobal.tm = tm;
    1066             : 
    1067             :         /* Enable BFD callbacks. */
    1068           6 :         zc->bfd_integration = true;
    1069             : 
    1070             :         /* Send the client registration */
    1071           6 :         bfd_client_sendmsg(zc, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
    1072             : 
    1073           6 :         hook_register(frr_fini, bfd_protocol_integration_finish);
    1074           6 : }
    1075             : 
    1076           0 : void bfd_protocol_integration_set_debug(bool enable)
    1077             : {
    1078           0 :         bsglobal.debugging = enable;
    1079           0 : }
    1080             : 
    1081           3 : void bfd_protocol_integration_set_shutdown(bool enable)
    1082             : {
    1083           3 :         bsglobal.shutting_down = enable;
    1084           3 : }
    1085             : 
    1086           0 : bool bfd_protocol_integration_debug(void)
    1087             : {
    1088           0 :         return bsglobal.debugging;
    1089             : }
    1090             : 
    1091           0 : bool bfd_protocol_integration_shutting_down(void)
    1092             : {
    1093           0 :         return bsglobal.shutting_down;
    1094             : }
    1095             : 
    1096             : /*
    1097             :  * BFD automatic source selection
    1098             :  *
    1099             :  * This feature will use the next hop tracking (NHT) provided by zebra
    1100             :  * to find out the source address by looking at the output interface.
    1101             :  *
    1102             :  * When the interface address / routing table change we'll be notified
    1103             :  * and be able to update the source address accordingly.
    1104             :  *
    1105             :  *     <daemon>                 zebra
    1106             :  *         |
    1107             :  * +-----------------+
    1108             :  * | BFD session set |
    1109             :  * | to auto source  |
    1110             :  * +-----------------+
    1111             :  *         |
    1112             :  *         \                 +-----------------+
    1113             :  *          -------------->  | Resolves        |
    1114             :  *                           | destination     |
    1115             :  *                           | address         |
    1116             :  *                           +-----------------+
    1117             :  *                                |
    1118             :  * +-----------------+            /
    1119             :  * | Sets resolved   | <----------
    1120             :  * | source address  |
    1121             :  * +-----------------+
    1122             :  */
    1123             : static bool
    1124           0 : bfd_source_cache_session_match(const struct bfd_source_cache *source,
    1125             :                                const struct bfd_session_params *session)
    1126             : {
    1127           0 :         const struct in_addr *address;
    1128           0 :         const struct in6_addr *address_v6;
    1129             : 
    1130           0 :         if (session->args.vrf_id != source->vrf_id)
    1131             :                 return false;
    1132           0 :         if (session->args.family != source->address.family)
    1133             :                 return false;
    1134             : 
    1135           0 :         switch (session->args.family) {
    1136           0 :         case AF_INET:
    1137           0 :                 address = (const struct in_addr *)&session->args.dst;
    1138           0 :                 if (address->s_addr != source->address.u.prefix4.s_addr)
    1139           0 :                         return false;
    1140             :                 break;
    1141           0 :         case AF_INET6:
    1142           0 :                 address_v6 = &session->args.dst;
    1143           0 :                 if (memcmp(address_v6, &source->address.u.prefix6,
    1144             :                            sizeof(struct in6_addr)))
    1145           0 :                         return false;
    1146             :                 break;
    1147             :         default:
    1148             :                 return false;
    1149             :         }
    1150             : 
    1151             :         return true;
    1152             : }
    1153             : 
    1154             : static struct bfd_source_cache *
    1155           0 : bfd_source_cache_find(vrf_id_t vrf_id, const struct prefix *prefix)
    1156             : {
    1157           0 :         struct bfd_source_cache *source;
    1158             : 
    1159           0 :         SLIST_FOREACH (source, &bsglobal.source_list, entry) {
    1160           0 :                 if (source->vrf_id != vrf_id)
    1161           0 :                         continue;
    1162           0 :                 if (!prefix_same(&source->address, prefix))
    1163           0 :                         continue;
    1164             : 
    1165             :                 return source;
    1166             :         }
    1167             : 
    1168             :         return NULL;
    1169             : }
    1170             : 
    1171           0 : static void bfd_source_cache_get(struct bfd_session_params *session)
    1172             : {
    1173           0 :         struct bfd_source_cache *source;
    1174           0 :         struct prefix target = {};
    1175             : 
    1176           0 :         switch (session->args.family) {
    1177           0 :         case AF_INET:
    1178           0 :                 target.family = AF_INET;
    1179           0 :                 target.prefixlen = IPV4_MAX_BITLEN;
    1180           0 :                 memcpy(&target.u.prefix4, &session->args.dst,
    1181             :                        sizeof(struct in_addr));
    1182           0 :                 break;
    1183           0 :         case AF_INET6:
    1184           0 :                 target.family = AF_INET6;
    1185           0 :                 target.prefixlen = IPV6_MAX_BITLEN;
    1186           0 :                 memcpy(&target.u.prefix6, &session->args.dst,
    1187             :                        sizeof(struct in6_addr));
    1188           0 :                 break;
    1189             :         default:
    1190             :                 return;
    1191             :         }
    1192             : 
    1193           0 :         source = bfd_source_cache_find(session->args.vrf_id, &target);
    1194           0 :         if (source) {
    1195           0 :                 if (session->source_cache == source)
    1196             :                         return;
    1197             : 
    1198           0 :                 bfd_source_cache_put(session);
    1199           0 :                 session->source_cache = source;
    1200           0 :                 source->refcount++;
    1201           0 :                 return;
    1202             :         }
    1203             : 
    1204           0 :         source = XCALLOC(MTYPE_BFD_SOURCE, sizeof(*source));
    1205           0 :         prefix_copy(&source->address, &target);
    1206           0 :         source->vrf_id = session->args.vrf_id;
    1207           0 :         SLIST_INSERT_HEAD(&bsglobal.source_list, source, entry);
    1208             : 
    1209           0 :         bfd_source_cache_put(session);
    1210           0 :         session->source_cache = source;
    1211           0 :         source->refcount = 1;
    1212             : 
    1213           0 :         return;
    1214             : }
    1215             : 
    1216           0 : static void bfd_source_cache_put(struct bfd_session_params *session)
    1217             : {
    1218           0 :         if (session->source_cache == NULL)
    1219             :                 return;
    1220             : 
    1221           0 :         session->source_cache->refcount--;
    1222           0 :         if (session->source_cache->refcount > 0) {
    1223           0 :                 session->source_cache = NULL;
    1224           0 :                 return;
    1225             :         }
    1226             : 
    1227           0 :         SLIST_REMOVE(&bsglobal.source_list, session->source_cache,
    1228             :                      bfd_source_cache, entry);
    1229           0 :         XFREE(MTYPE_BFD_SOURCE, session->source_cache);
    1230             : }
    1231             : 
    1232             : /** Updates BFD running session if source address has changed. */
    1233             : static void
    1234           0 : bfd_source_cache_update_session(const struct bfd_source_cache *source,
    1235             :                                 struct bfd_session_params *session)
    1236             : {
    1237           0 :         const struct in_addr *address;
    1238           0 :         const struct in6_addr *address_v6;
    1239             : 
    1240           0 :         switch (session->args.family) {
    1241           0 :         case AF_INET:
    1242           0 :                 address = (const struct in_addr *)&session->args.src;
    1243           0 :                 if (memcmp(address, &source->source.u.prefix4,
    1244             :                            sizeof(struct in_addr)) == 0)
    1245             :                         return;
    1246             : 
    1247           0 :                 _bfd_sess_remove(session);
    1248           0 :                 memcpy(&session->args.src, &source->source.u.prefix4,
    1249             :                        sizeof(struct in_addr));
    1250           0 :                 break;
    1251           0 :         case AF_INET6:
    1252           0 :                 address_v6 = &session->args.src;
    1253           0 :                 if (memcmp(address_v6, &source->source.u.prefix6,
    1254             :                            sizeof(struct in6_addr)) == 0)
    1255             :                         return;
    1256             : 
    1257           0 :                 _bfd_sess_remove(session);
    1258           0 :                 memcpy(&session->args.src, &source->source.u.prefix6,
    1259             :                        sizeof(struct in6_addr));
    1260           0 :                 break;
    1261             :         default:
    1262             :                 return;
    1263             :         }
    1264             : 
    1265           0 :         bfd_sess_install(session);
    1266             : }
    1267             : 
    1268             : static void
    1269           0 : bfd_source_cache_update_sessions(const struct bfd_source_cache *source)
    1270             : {
    1271           0 :         struct bfd_session_params *session;
    1272             : 
    1273           0 :         if (!source->valid)
    1274             :                 return;
    1275             : 
    1276           0 :         TAILQ_FOREACH (session, &bsglobal.bsplist, entry) {
    1277           0 :                 if (!session->auto_source)
    1278           0 :                         continue;
    1279           0 :                 if (!bfd_source_cache_session_match(source, session))
    1280           0 :                         continue;
    1281             : 
    1282           0 :                 bfd_source_cache_update_session(source, session);
    1283             :         }
    1284             : }
    1285             : 
    1286             : /**
    1287             :  * Try to translate next hop information into source address.
    1288             :  *
    1289             :  * \returns `true` if source changed otherwise `false`.
    1290             :  */
    1291           0 : static bool bfd_source_cache_update(struct bfd_source_cache *source,
    1292             :                                     const struct zapi_route *route)
    1293             : {
    1294           0 :         size_t nh_index;
    1295             : 
    1296           0 :         for (nh_index = 0; nh_index < route->nexthop_num; nh_index++) {
    1297           0 :                 const struct zapi_nexthop *nh = &route->nexthops[nh_index];
    1298           0 :                 const struct interface *interface;
    1299           0 :                 const struct connected *connected;
    1300           0 :                 const struct listnode *node;
    1301             : 
    1302           0 :                 interface = if_lookup_by_index(nh->ifindex, nh->vrf_id);
    1303           0 :                 if (interface == NULL) {
    1304           0 :                         zlog_err("next hop interface not found (index %d)",
    1305             :                                  nh->ifindex);
    1306           0 :                         continue;
    1307             :                 }
    1308             : 
    1309           0 :                 for (ALL_LIST_ELEMENTS_RO(interface->connected, node,
    1310             :                                           connected)) {
    1311           0 :                         if (source->address.family !=
    1312           0 :                             connected->address->family)
    1313           0 :                                 continue;
    1314           0 :                         if (prefix_same(connected->address, &source->source))
    1315             :                                 return false;
    1316             :                         /*
    1317             :                          * Skip link-local as it is only useful for single hop
    1318             :                          * and in that case no source is specified usually.
    1319             :                          */
    1320           0 :                         if (source->address.family == AF_INET6 &&
    1321           0 :                             IN6_IS_ADDR_LINKLOCAL(
    1322             :                                     &connected->address->u.prefix6))
    1323           0 :                                 continue;
    1324             : 
    1325           0 :                         prefix_copy(&source->source, connected->address);
    1326           0 :                         source->valid = true;
    1327           0 :                         return true;
    1328             :                 }
    1329             :         }
    1330             : 
    1331           0 :         memset(&source->source, 0, sizeof(source->source));
    1332           0 :         source->valid = false;
    1333           0 :         return false;
    1334             : }
    1335             : 
    1336           8 : int bfd_nht_update(const struct prefix *match, const struct zapi_route *route)
    1337             : {
    1338           8 :         struct bfd_source_cache *source;
    1339             : 
    1340           8 :         if (bsglobal.debugging)
    1341           0 :                 zlog_debug("BFD NHT update for %pFX", &route->prefix);
    1342             : 
    1343           8 :         SLIST_FOREACH (source, &bsglobal.source_list, entry) {
    1344           0 :                 if (source->vrf_id != route->vrf_id)
    1345           0 :                         continue;
    1346           0 :                 if (!prefix_same(match, &source->address))
    1347           0 :                         continue;
    1348           0 :                 if (bfd_source_cache_update(source, route))
    1349           0 :                         bfd_source_cache_update_sessions(source);
    1350             :         }
    1351             : 
    1352           8 :         return 0;
    1353             : }

Generated by: LCOV version v1.16-topotato