back to topotato report
topotato coverage report
Current view: top level - bfdd - bfd.c (source / functions) Hit Total Coverage
Test: aggregated run ( view descriptions ) Lines: 514 926 55.5 %
Date: 2023-02-24 19:38:44 Functions: 64 91 70.3 %

          Line data    Source code
       1             : /*********************************************************************
       2             :  * Copyright 2013 Cumulus Networks, LLC.  All rights reserved.
       3             :  * Copyright 2014,2015,2016,2017 Cumulus Networks, Inc.  All rights reserved.
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or modify it
       6             :  * under the terms of the GNU General Public License as published by the Free
       7             :  * Software Foundation; either version 2 of the License, or (at your option)
       8             :  * any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful, but WITHOUT
      11             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12             :  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      13             :  * more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License along
      16             :  * with this program; see the file COPYING; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      18             :  *
      19             :  * bfd.c: implements the BFD protocol.
      20             :  *
      21             :  * Authors
      22             :  * -------
      23             :  * Shrijeet Mukherjee [shm@cumulusnetworks.com]
      24             :  * Kanna Rajagopal [kanna@cumulusnetworks.com]
      25             :  * Radhika Mahankali [Radhika@cumulusnetworks.com]
      26             :  */
      27             : 
      28             : #include <zebra.h>
      29             : 
      30             : #include "lib/jhash.h"
      31             : #include "lib/network.h"
      32             : 
      33             : #include "bfd.h"
      34             : 
      35          24 : DEFINE_MTYPE_STATIC(BFDD, BFDD_CONFIG, "long-lived configuration memory");
      36          24 : DEFINE_MTYPE_STATIC(BFDD, BFDD_PROFILE, "long-lived profile memory");
      37          24 : DEFINE_MTYPE_STATIC(BFDD, BFDD_SESSION_OBSERVER, "Session observer");
      38          24 : DEFINE_MTYPE_STATIC(BFDD, BFDD_VRF, "BFD VRF");
      39             : 
      40             : /*
      41             :  * Prototypes
      42             :  */
      43             : static uint32_t ptm_bfd_gen_ID(void);
      44             : static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd);
      45             : static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
      46             :                                          uint32_t ldisc);
      47             : static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc);
      48             : static const char *get_diag_str(int diag);
      49             : 
      50             : static void bs_admin_down_handler(struct bfd_session *bs, int nstate);
      51             : static void bs_down_handler(struct bfd_session *bs, int nstate);
      52             : static void bs_init_handler(struct bfd_session *bs, int nstate);
      53             : static void bs_up_handler(struct bfd_session *bs, int nstate);
      54             : 
      55             : /**
      56             :  * Remove BFD profile from all BFD sessions so we don't leave dangling
      57             :  * pointers.
      58             :  */
      59             : static void bfd_profile_detach(struct bfd_profile *bp);
      60             : 
      61             : /* Zeroed array with the size of an IPv6 address. */
      62             : struct in6_addr zero_addr;
      63             : 
      64             : /** BFD profiles list. */
      65             : struct bfdproflist bplist;
      66             : 
      67             : /*
      68             :  * Functions
      69             :  */
      70           4 : struct bfd_profile *bfd_profile_lookup(const char *name)
      71             : {
      72           4 :         struct bfd_profile *bp;
      73             : 
      74           4 :         TAILQ_FOREACH (bp, &bplist, entry) {
      75           2 :                 if (strcmp(name, bp->name))
      76           0 :                         continue;
      77             : 
      78             :                 return bp;
      79             :         }
      80             : 
      81             :         return NULL;
      82             : }
      83             : 
      84          18 : static void bfd_profile_set_default(struct bfd_profile *bp)
      85             : {
      86          18 :         bp->admin_shutdown = false;
      87          18 :         bp->detection_multiplier = BFD_DEFDETECTMULT;
      88          18 :         bp->echo_mode = false;
      89          18 :         bp->passive = false;
      90          18 :         bp->minimum_ttl = BFD_DEF_MHOP_TTL;
      91          18 :         bp->min_echo_rx = BFD_DEF_REQ_MIN_ECHO_RX;
      92          18 :         bp->min_echo_tx = BFD_DEF_DES_MIN_ECHO_TX;
      93          18 :         bp->min_rx = BFD_DEFREQUIREDMINRX;
      94          18 :         bp->min_tx = BFD_DEFDESIREDMINTX;
      95             : }
      96             : 
      97           2 : struct bfd_profile *bfd_profile_new(const char *name)
      98             : {
      99           2 :         struct bfd_profile *bp;
     100             : 
     101             :         /* Search for duplicates. */
     102           2 :         if (bfd_profile_lookup(name) != NULL)
     103             :                 return NULL;
     104             : 
     105             :         /* Allocate, name it and put into list. */
     106           2 :         bp = XCALLOC(MTYPE_BFDD_PROFILE, sizeof(*bp));
     107           2 :         strlcpy(bp->name, name, sizeof(bp->name));
     108           2 :         TAILQ_INSERT_TAIL(&bplist, bp, entry);
     109             : 
     110             :         /* Set default values. */
     111           2 :         bfd_profile_set_default(bp);
     112             : 
     113           2 :         return bp;
     114             : }
     115             : 
     116           2 : void bfd_profile_free(struct bfd_profile *bp)
     117             : {
     118             :         /* Detach from any session. */
     119           2 :         if (bglobal.bg_shutdown == false)
     120           2 :                 bfd_profile_detach(bp);
     121             : 
     122             :         /* Remove from global list. */
     123           2 :         TAILQ_REMOVE(&bplist, bp, entry);
     124             : 
     125           2 :         XFREE(MTYPE_BFDD_PROFILE, bp);
     126           2 : }
     127             : 
     128           2 : void bfd_profile_apply(const char *profname, struct bfd_session *bs)
     129             : {
     130           2 :         struct bfd_profile *bp;
     131             : 
     132             :         /* Remove previous profile if any. */
     133           2 :         if (bs->profile_name) {
     134             :                 /* We are changing profiles. */
     135           0 :                 if (strcmp(bs->profile_name, profname)) {
     136           0 :                         XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
     137           0 :                         bs->profile_name =
     138           0 :                                 XSTRDUP(MTYPE_BFDD_PROFILE, profname);
     139             :                 }
     140             :         } else /* Save the current profile name (in case it doesn't exist). */
     141           2 :                 bs->profile_name = XSTRDUP(MTYPE_BFDD_PROFILE, profname);
     142             : 
     143             :         /* Look up new profile to apply. */
     144           2 :         bp = bfd_profile_lookup(profname);
     145             : 
     146             :         /* Point to profile if it exists. */
     147           2 :         bs->profile = bp;
     148             : 
     149             :         /* Apply configuration. */
     150           2 :         bfd_session_apply(bs);
     151           2 : }
     152             : 
     153           2 : void bfd_session_apply(struct bfd_session *bs)
     154             : {
     155           2 :         struct bfd_profile *bp;
     156           2 :         uint32_t min_tx = bs->timers.desired_min_tx;
     157           2 :         uint32_t min_rx = bs->timers.required_min_rx;
     158             : 
     159             :         /* Pick the source of configuration. */
     160           2 :         bp = bs->profile ? bs->profile : &bs->peer_profile;
     161             : 
     162             :         /* Set multiplier if not the default. */
     163           2 :         if (bs->peer_profile.detection_multiplier == BFD_DEFDETECTMULT)
     164           2 :                 bs->detect_mult = bp->detection_multiplier;
     165             :         else
     166           0 :                 bs->detect_mult = bs->peer_profile.detection_multiplier;
     167             : 
     168             :         /* Set timers if not the default. */
     169           2 :         if (bs->peer_profile.min_tx == BFD_DEFDESIREDMINTX)
     170           2 :                 bs->timers.desired_min_tx = bp->min_tx;
     171             :         else
     172           0 :                 bs->timers.desired_min_tx = bs->peer_profile.min_tx;
     173             : 
     174           2 :         if (bs->peer_profile.min_rx == BFD_DEFREQUIREDMINRX)
     175           2 :                 bs->timers.required_min_rx = bp->min_rx;
     176             :         else
     177           0 :                 bs->timers.required_min_rx = bs->peer_profile.min_rx;
     178             : 
     179             :         /* We can only apply echo options on single hop sessions. */
     180           2 :         if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) {
     181             :                 /* Configure echo timers if they were default. */
     182           2 :                 if (bs->peer_profile.min_echo_rx == BFD_DEF_REQ_MIN_ECHO_RX)
     183           2 :                         bs->timers.required_min_echo_rx = bp->min_echo_rx;
     184             :                 else
     185           0 :                         bs->timers.required_min_echo_rx =
     186             :                                 bs->peer_profile.min_echo_rx;
     187             : 
     188           2 :                 if (bs->peer_profile.min_echo_tx == BFD_DEF_DES_MIN_ECHO_TX)
     189           2 :                         bs->timers.desired_min_echo_tx = bp->min_echo_tx;
     190             :                 else
     191           0 :                         bs->timers.desired_min_echo_tx =
     192             :                                 bs->peer_profile.min_echo_tx;
     193             : 
     194             :                 /* Toggle echo if default value. */
     195           2 :                 if (bs->peer_profile.echo_mode == false)
     196           2 :                         bfd_set_echo(bs, bp->echo_mode);
     197             :                 else
     198           0 :                         bfd_set_echo(bs, bs->peer_profile.echo_mode);
     199             :         } else {
     200             :                 /* Configure the TTL packet filter. */
     201           0 :                 if (bs->peer_profile.minimum_ttl == BFD_DEF_MHOP_TTL)
     202           0 :                         bs->mh_ttl = bp->minimum_ttl;
     203             :                 else
     204           0 :                         bs->mh_ttl = bs->peer_profile.minimum_ttl;
     205             :         }
     206             : 
     207             :         /* Toggle 'passive-mode' if default value. */
     208           2 :         if (bs->peer_profile.passive == false)
     209           2 :                 bfd_set_passive_mode(bs, bp->passive);
     210             :         else
     211           0 :                 bfd_set_passive_mode(bs, bs->peer_profile.passive);
     212             : 
     213             :         /* Toggle 'no shutdown' if default value. */
     214           2 :         if (bs->peer_profile.admin_shutdown == false)
     215           2 :                 bfd_set_shutdown(bs, bp->admin_shutdown);
     216             :         else
     217           0 :                 bfd_set_shutdown(bs, bs->peer_profile.admin_shutdown);
     218             : 
     219             :         /* If session interval changed negotiate new timers. */
     220           2 :         if (bs->ses_state == PTM_BFD_UP
     221           0 :             && (bs->timers.desired_min_tx != min_tx
     222           0 :                 || bs->timers.required_min_rx != min_rx))
     223           2 :                 bfd_set_polling(bs);
     224             : 
     225             :         /* Send updated information to data plane. */
     226           2 :         bfd_dplane_update_session(bs);
     227           2 : }
     228             : 
     229           0 : void bfd_profile_remove(struct bfd_session *bs)
     230             : {
     231             :         /* Remove any previous set profile name. */
     232           0 :         XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
     233           0 :         bs->profile = NULL;
     234             : 
     235           0 :         bfd_session_apply(bs);
     236           0 : }
     237             : 
     238          52 : void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer,
     239             :                  struct sockaddr_any *local, bool mhop, const char *ifname,
     240             :                  const char *vrfname)
     241             : {
     242          52 :         memset(key, 0, sizeof(*key));
     243             : 
     244          52 :         switch (peer->sa_sin.sin_family) {
     245          52 :         case AF_INET:
     246          52 :                 key->family = AF_INET;
     247          52 :                 memcpy(&key->peer, &peer->sa_sin.sin_addr,
     248             :                        sizeof(peer->sa_sin.sin_addr));
     249          52 :                 memcpy(&key->local, &local->sa_sin.sin_addr,
     250             :                        sizeof(local->sa_sin.sin_addr));
     251          52 :                 break;
     252           0 :         case AF_INET6:
     253           0 :                 key->family = AF_INET6;
     254           0 :                 memcpy(&key->peer, &peer->sa_sin6.sin6_addr,
     255             :                        sizeof(peer->sa_sin6.sin6_addr));
     256           0 :                 memcpy(&key->local, &local->sa_sin6.sin6_addr,
     257             :                        sizeof(local->sa_sin6.sin6_addr));
     258           0 :                 break;
     259             :         }
     260             : 
     261          52 :         key->mhop = mhop;
     262          52 :         if (ifname && ifname[0])
     263          52 :                 strlcpy(key->ifname, ifname, sizeof(key->ifname));
     264          52 :         if (vrfname && vrfname[0])
     265          52 :                 strlcpy(key->vrfname, vrfname, sizeof(key->vrfname));
     266             :         else
     267           0 :                 strlcpy(key->vrfname, VRF_DEFAULT_NAME, sizeof(key->vrfname));
     268          52 : }
     269             : 
     270          42 : struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
     271             : {
     272          42 :         struct bfd_session *bs;
     273          42 :         struct peer_label *pl;
     274          42 :         struct bfd_key key;
     275             : 
     276             :         /* Try to find label first. */
     277          42 :         if (bpc->bpc_has_label) {
     278           0 :                 pl = pl_find(bpc->bpc_label);
     279           0 :                 if (pl != NULL) {
     280           0 :                         bs = pl->pl_bs;
     281           0 :                         return bs;
     282             :                 }
     283             :         }
     284             : 
     285             :         /* Otherwise fallback to peer/local hash lookup. */
     286          42 :         gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop,
     287          42 :                     bpc->bpc_localif, bpc->bpc_vrfname);
     288             : 
     289          42 :         return bfd_key_lookup(key);
     290             : }
     291             : 
     292             : /*
     293             :  * Starts a disabled BFD session.
     294             :  *
     295             :  * A session is disabled when the specified interface/VRF doesn't exist
     296             :  * yet. It might happen on FRR boot or with virtual interfaces.
     297             :  */
     298          16 : int bfd_session_enable(struct bfd_session *bs)
     299             : {
     300          16 :         struct interface *ifp = NULL;
     301          16 :         struct vrf *vrf = NULL;
     302          16 :         int psock;
     303             : 
     304             :         /* We are using data plane, we don't need software. */
     305          16 :         if (bs->bdc)
     306             :                 return 0;
     307             : 
     308             :         /*
     309             :          * If the interface or VRF doesn't exist, then we must register
     310             :          * the session but delay its start.
     311             :          */
     312          16 :         if (bs->key.vrfname[0]) {
     313          16 :                 vrf = vrf_lookup_by_name(bs->key.vrfname);
     314          16 :                 if (vrf == NULL) {
     315           0 :                         zlog_err(
     316             :                                 "session-enable: specified VRF %s doesn't exists.",
     317             :                                 bs->key.vrfname);
     318           0 :                         return 0;
     319             :                 }
     320             :         } else {
     321           0 :                 vrf = vrf_lookup_by_id(VRF_DEFAULT);
     322             :         }
     323             : 
     324          16 :         assert(vrf);
     325             : 
     326          16 :         if (bs->key.ifname[0]) {
     327          16 :                 ifp = if_lookup_by_name(bs->key.ifname, vrf->vrf_id);
     328          16 :                 if (ifp == NULL) {
     329           0 :                         zlog_err(
     330             :                                 "session-enable: specified interface %s (VRF %s) doesn't exist.",
     331             :                                 bs->key.ifname, vrf->name);
     332           0 :                         return 0;
     333             :                 }
     334             :         }
     335             : 
     336             :         /* Assign interface/VRF pointers. */
     337          16 :         bs->vrf = vrf;
     338             : 
     339             :         /* Assign interface pointer (if any). */
     340          16 :         bs->ifp = ifp;
     341             : 
     342             :         /* Attempt to use data plane. */
     343          16 :         if (bglobal.bg_use_dplane && bfd_dplane_add_session(bs) == 0) {
     344           0 :                 control_notify_config(BCM_NOTIFY_CONFIG_ADD, bs);
     345           0 :                 return 0;
     346             :         }
     347             : 
     348             :         /* Sanity check: don't leak open sockets. */
     349          16 :         if (bs->sock != -1) {
     350           0 :                 if (bglobal.debug_peer_event)
     351           0 :                         zlog_debug("%s: previous socket open", __func__);
     352             : 
     353           0 :                 close(bs->sock);
     354           0 :                 bs->sock = -1;
     355             :         }
     356             : 
     357             :         /*
     358             :          * Get socket for transmitting control packets.  Note that if we
     359             :          * could use the destination port (3784) for the source
     360             :          * port we wouldn't need a socket per session.
     361             :          */
     362          16 :         if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_IPV6) == 0) {
     363          16 :                 psock = bp_peer_socket(bs);
     364          16 :                 if (psock == -1)
     365             :                         return 0;
     366             :         } else {
     367           0 :                 psock = bp_peer_socketv6(bs);
     368           0 :                 if (psock == -1)
     369             :                         return 0;
     370             :         }
     371             : 
     372             :         /*
     373             :          * We've got a valid socket, lets start the timers and the
     374             :          * protocol.
     375             :          */
     376          16 :         bs->sock = psock;
     377             : 
     378             :         /* Only start timers if we are using active mode. */
     379          16 :         if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE) == 0) {
     380          16 :                 bfd_recvtimer_update(bs);
     381          16 :                 ptm_bfd_start_xmt_timer(bs, false);
     382             :         }
     383             : 
     384             :         /* initialize RTT */
     385          32 :         bfd_rtt_init(bs);
     386             : 
     387             :         return 0;
     388             : }
     389             : 
     390             : /*
     391             :  * Disabled a running BFD session.
     392             :  *
     393             :  * A session is disabled when the specified interface/VRF gets removed
     394             :  * (e.g. virtual interfaces).
     395             :  */
     396          16 : void bfd_session_disable(struct bfd_session *bs)
     397             : {
     398             :         /* We are using data plane, we don't need software. */
     399          16 :         if (bs->bdc)
     400             :                 return;
     401             : 
     402             :         /* Free up socket resources. */
     403          16 :         if (bs->sock != -1) {
     404          16 :                 close(bs->sock);
     405          16 :                 bs->sock = -1;
     406             :         }
     407             : 
     408             :         /* Disable all timers. */
     409          16 :         bfd_recvtimer_delete(bs);
     410          16 :         bfd_xmttimer_delete(bs);
     411          16 :         ptm_bfd_echo_stop(bs);
     412             : 
     413             :         /* Set session down so it doesn't report UP and disabled. */
     414          16 :         ptm_bfd_sess_dn(bs, BD_PATH_DOWN);
     415             : }
     416             : 
     417          16 : static uint32_t ptm_bfd_gen_ID(void)
     418             : {
     419          16 :         uint32_t session_id;
     420             : 
     421             :         /*
     422             :          * RFC 5880, Section 6.8.1. recommends that we should generate
     423             :          * random session identification numbers.
     424             :          */
     425          16 :         do {
     426          16 :                 session_id = ((frr_weak_random() << 16) & 0xFFFF0000)
     427          16 :                              | (frr_weak_random() & 0x0000FFFF);
     428          16 :         } while (session_id == 0 || bfd_id_lookup(session_id) != NULL);
     429             : 
     430          16 :         return session_id;
     431             : }
     432             : 
     433          47 : void ptm_bfd_start_xmt_timer(struct bfd_session *bfd, bool is_echo)
     434             : {
     435          47 :         uint64_t jitter, xmt_TO;
     436          47 :         int maxpercent;
     437             : 
     438          47 :         xmt_TO = is_echo ? bfd->echo_xmt_TO : bfd->xmt_TO;
     439             : 
     440             :         /*
     441             :          * From section 6.5.2: trasmit interval should be randomly jittered
     442             :          * between
     443             :          * 75% and 100% of nominal value, unless detect_mult is 1, then should
     444             :          * be
     445             :          * between 75% and 90%.
     446             :          */
     447          47 :         maxpercent = (bfd->detect_mult == 1) ? 16 : 26;
     448          47 :         jitter = (xmt_TO * (75 + (frr_weak_random() % maxpercent))) / 100;
     449             :         /* XXX remove that division above */
     450             : 
     451          47 :         if (is_echo)
     452           0 :                 bfd_echo_xmttimer_update(bfd, jitter);
     453             :         else
     454          47 :                 bfd_xmttimer_update(bfd, jitter);
     455          47 : }
     456             : 
     457           0 : static void ptm_bfd_echo_xmt_TO(struct bfd_session *bfd)
     458             : {
     459             :         /* Send the scheduled echo  packet */
     460             :         /* if ipv4 use the new echo implementation that causes
     461             :          * the packet to be looped in forwarding plane of peer
     462             :          */
     463           0 :         if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6) == 0)
     464             : #ifdef BFD_LINUX
     465           0 :                 ptm_bfd_echo_fp_snd(bfd);
     466             : #else
     467             :                 ptm_bfd_echo_snd(bfd);
     468             : #endif
     469             :         else
     470           0 :                 ptm_bfd_echo_snd(bfd);
     471             : 
     472             :         /* Restart the timer for next time */
     473           0 :         ptm_bfd_start_xmt_timer(bfd, true);
     474           0 : }
     475             : 
     476          19 : void ptm_bfd_xmt_TO(struct bfd_session *bfd, int fbit)
     477             : {
     478             :         /* Send the scheduled control packet */
     479          19 :         ptm_bfd_snd(bfd, fbit);
     480             : 
     481             :         /* Restart the timer for next time */
     482          19 :         ptm_bfd_start_xmt_timer(bfd, false);
     483          19 : }
     484             : 
     485          16 : void ptm_bfd_echo_stop(struct bfd_session *bfd)
     486             : {
     487          16 :         bfd->echo_xmt_TO = 0;
     488          16 :         bfd->echo_detect_TO = 0;
     489          16 :         UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE);
     490             : 
     491          16 :         bfd_echo_xmttimer_delete(bfd);
     492          16 :         bfd_echo_recvtimer_delete(bfd);
     493          16 : }
     494             : 
     495           0 : void ptm_bfd_echo_start(struct bfd_session *bfd)
     496             : {
     497           0 :         bfd->echo_detect_TO = (bfd->remote_detect_mult * bfd->echo_xmt_TO);
     498           0 :         if (bfd->echo_detect_TO > 0) {
     499           0 :                 bfd_echo_recvtimer_update(bfd);
     500           0 :                 ptm_bfd_echo_xmt_TO(bfd);
     501             :         }
     502           0 : }
     503             : 
     504           6 : void ptm_bfd_sess_up(struct bfd_session *bfd)
     505             : {
     506           6 :         int old_state = bfd->ses_state;
     507             : 
     508           6 :         bfd->local_diag = 0;
     509           6 :         bfd->ses_state = PTM_BFD_UP;
     510           6 :         monotime(&bfd->uptime);
     511             : 
     512             :         /* Connection is up, lets negotiate timers. */
     513           6 :         bfd_set_polling(bfd);
     514             : 
     515             :         /* Start sending control packets with poll bit immediately. */
     516           6 :         ptm_bfd_snd(bfd, 0);
     517             : 
     518           6 :         control_notify(bfd, bfd->ses_state);
     519             : 
     520           6 :         if (old_state != bfd->ses_state) {
     521           6 :                 bfd->stats.session_up++;
     522           6 :                 if (bglobal.debug_peer_event)
     523           0 :                         zlog_debug("state-change: [%s] %s -> %s",
     524             :                                    bs_to_string(bfd), state_list[old_state].str,
     525             :                                    state_list[bfd->ses_state].str);
     526             :         }
     527           6 : }
     528             : 
     529          19 : void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag)
     530             : {
     531          19 :         int old_state = bfd->ses_state;
     532             : 
     533          19 :         bfd->local_diag = diag;
     534          19 :         bfd->discrs.remote_discr = 0;
     535          19 :         bfd->ses_state = PTM_BFD_DOWN;
     536          19 :         bfd->polling = 0;
     537          19 :         bfd->demand_mode = 0;
     538          19 :         monotime(&bfd->downtime);
     539             : 
     540             :         /*
     541             :          * Only attempt to send if we have a valid socket:
     542             :          * this function might be called by session disablers and in
     543             :          * this case we won't have a valid socket (i.e. interface was
     544             :          * removed or VRF doesn't exist anymore).
     545             :          */
     546          19 :         if (bfd->sock != -1)
     547           3 :                 ptm_bfd_snd(bfd, 0);
     548             : 
     549             :         /* Slow down the control packets, the connection is down. */
     550          19 :         bs_set_slow_timers(bfd);
     551             : 
     552             :         /* only signal clients when going from up->down state */
     553          19 :         if (old_state == PTM_BFD_UP)
     554           3 :                 control_notify(bfd, PTM_BFD_DOWN);
     555             : 
     556             :         /* Stop echo packet transmission if they are active */
     557          19 :         if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
     558           0 :                 ptm_bfd_echo_stop(bfd);
     559             : 
     560             :         /* Stop attempting to transmit or expect control packets if passive. */
     561          19 :         if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_PASSIVE)) {
     562           0 :                 bfd_recvtimer_delete(bfd);
     563           0 :                 bfd_xmttimer_delete(bfd);
     564             :         }
     565             : 
     566          19 :         if (old_state != bfd->ses_state) {
     567           3 :                 bfd->stats.session_down++;
     568           3 :                 if (bglobal.debug_peer_event)
     569           0 :                         zlog_debug("state-change: [%s] %s -> %s reason:%s",
     570             :                                    bs_to_string(bfd), state_list[old_state].str,
     571             :                                    state_list[bfd->ses_state].str,
     572             :                                    get_diag_str(bfd->local_diag));
     573             :         }
     574             : 
     575             :         /* clear peer's mac address */
     576          19 :         UNSET_FLAG(bfd->flags, BFD_SESS_FLAG_MAC_SET);
     577          19 :         memset(bfd->peer_hw_addr, 0, sizeof(bfd->peer_hw_addr));
     578             :         /* reset local address ,it might has been be changed after bfd is up*/
     579          19 :         memset(&bfd->local_address, 0, sizeof(bfd->local_address));
     580             : 
     581             :         /* reset RTT */
     582          38 :         bfd_rtt_init(bfd);
     583          19 : }
     584             : 
     585          28 : static struct bfd_session *bfd_find_disc(struct sockaddr_any *sa,
     586             :                                          uint32_t ldisc)
     587             : {
     588          28 :         struct bfd_session *bs;
     589             : 
     590          28 :         bs = bfd_id_lookup(ldisc);
     591          28 :         if (bs == NULL)
     592             :                 return NULL;
     593             : 
     594          25 :         switch (bs->key.family) {
     595          25 :         case AF_INET:
     596          25 :                 if (memcmp(&sa->sa_sin.sin_addr, &bs->key.peer,
     597             :                            sizeof(sa->sa_sin.sin_addr)))
     598           0 :                         return NULL;
     599             :                 break;
     600           0 :         case AF_INET6:
     601           0 :                 if (memcmp(&sa->sa_sin6.sin6_addr, &bs->key.peer,
     602             :                            sizeof(sa->sa_sin6.sin6_addr)))
     603           0 :                         return NULL;
     604             :                 break;
     605             :         }
     606             : 
     607             :         return bs;
     608             : }
     609             : 
     610          38 : struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
     611             :                                       struct sockaddr_any *peer,
     612             :                                       struct sockaddr_any *local,
     613             :                                       struct interface *ifp,
     614             :                                       vrf_id_t vrfid,
     615             :                                       bool is_mhop)
     616             : {
     617          38 :         struct vrf *vrf;
     618          38 :         struct bfd_key key;
     619             : 
     620             :         /* Find our session using the ID signaled by the remote end. */
     621          38 :         if (cp->discrs.remote_discr)
     622          28 :                 return bfd_find_disc(peer, ntohl(cp->discrs.remote_discr));
     623             : 
     624             :         /* Search for session without using discriminator. */
     625          10 :         vrf = vrf_lookup_by_id(vrfid);
     626             : 
     627          10 :         gen_bfd_key(&key, peer, local, is_mhop, ifp ? ifp->name : NULL,
     628           0 :                     vrf ? vrf->name : VRF_DEFAULT_NAME);
     629             : 
     630             :         /* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
     631          10 :         return bfd_key_lookup(key);
     632             : }
     633             : 
     634          19 : void bfd_xmt_cb(struct thread *t)
     635             : {
     636          19 :         struct bfd_session *bs = THREAD_ARG(t);
     637             : 
     638          19 :         ptm_bfd_xmt_TO(bs, 0);
     639          19 : }
     640             : 
     641           0 : void bfd_echo_xmt_cb(struct thread *t)
     642             : {
     643           0 :         struct bfd_session *bs = THREAD_ARG(t);
     644             : 
     645           0 :         if (bs->echo_xmt_TO > 0)
     646           0 :                 ptm_bfd_echo_xmt_TO(bs);
     647           0 : }
     648             : 
     649             : /* Was ptm_bfd_detect_TO() */
     650           1 : void bfd_recvtimer_cb(struct thread *t)
     651             : {
     652           1 :         struct bfd_session *bs = THREAD_ARG(t);
     653             : 
     654           1 :         switch (bs->ses_state) {
     655           1 :         case PTM_BFD_INIT:
     656             :         case PTM_BFD_UP:
     657           1 :                 ptm_bfd_sess_dn(bs, BD_CONTROL_EXPIRED);
     658           1 :                 break;
     659             :         }
     660           1 : }
     661             : 
     662             : /* Was ptm_bfd_echo_detect_TO() */
     663           0 : void bfd_echo_recvtimer_cb(struct thread *t)
     664             : {
     665           0 :         struct bfd_session *bs = THREAD_ARG(t);
     666             : 
     667           0 :         switch (bs->ses_state) {
     668           0 :         case PTM_BFD_INIT:
     669             :         case PTM_BFD_UP:
     670           0 :                 ptm_bfd_sess_dn(bs, BD_ECHO_FAILED);
     671           0 :                 break;
     672             :         }
     673           0 : }
     674             : 
     675          16 : struct bfd_session *bfd_session_new(void)
     676             : {
     677          16 :         struct bfd_session *bs;
     678             : 
     679          16 :         bs = XCALLOC(MTYPE_BFDD_CONFIG, sizeof(*bs));
     680             : 
     681             :         /* Set peer session defaults. */
     682          16 :         bfd_profile_set_default(&bs->peer_profile);
     683             : 
     684          16 :         bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX;
     685          16 :         bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX;
     686          16 :         bs->timers.required_min_echo_rx = BFD_DEF_REQ_MIN_ECHO_RX;
     687          16 :         bs->timers.desired_min_echo_tx = BFD_DEF_DES_MIN_ECHO_TX;
     688          16 :         bs->detect_mult = BFD_DEFDETECTMULT;
     689          16 :         bs->mh_ttl = BFD_DEF_MHOP_TTL;
     690          16 :         bs->ses_state = PTM_BFD_DOWN;
     691             : 
     692             :         /* Initiate connection with slow timers. */
     693          16 :         bs_set_slow_timers(bs);
     694             : 
     695             :         /* Initiate remote settings as well. */
     696          16 :         bs->remote_timers = bs->cur_timers;
     697          16 :         bs->remote_detect_mult = BFD_DEFDETECTMULT;
     698             : 
     699          16 :         bs->sock = -1;
     700          16 :         monotime(&bs->uptime);
     701          16 :         bs->downtime = bs->uptime;
     702             : 
     703          16 :         return bs;
     704             : }
     705             : 
     706           0 : int bfd_session_update_label(struct bfd_session *bs, const char *nlabel)
     707             : {
     708             :         /* New label treatment:
     709             :          * - Check if the label is taken;
     710             :          * - Try to allocate the memory for it and register;
     711             :          */
     712           0 :         if (bs->pl == NULL) {
     713           0 :                 if (pl_find(nlabel) != NULL) {
     714             :                         /* Someone is already using it. */
     715             :                         return -1;
     716             :                 }
     717             : 
     718           0 :                 pl_new(nlabel, bs);
     719             : 
     720           0 :                 return 0;
     721             :         }
     722             : 
     723             :         /*
     724             :          * Test label change consistency:
     725             :          * - Do nothing if it's the same label;
     726             :          * - Check if the future label is already taken;
     727             :          * - Change label;
     728             :          */
     729           0 :         if (strcmp(nlabel, bs->pl->pl_label) == 0)
     730             :                 return -1;
     731           0 :         if (pl_find(nlabel) != NULL)
     732             :                 return -1;
     733             : 
     734           0 :         strlcpy(bs->pl->pl_label, nlabel, sizeof(bs->pl->pl_label));
     735           0 :         return 0;
     736             : }
     737             : 
     738          16 : static void _bfd_session_update(struct bfd_session *bs,
     739             :                                 struct bfd_peer_cfg *bpc)
     740             : {
     741          16 :         if (bpc->bpc_has_txinterval) {
     742           0 :                 bs->timers.desired_min_tx = bpc->bpc_txinterval * 1000;
     743           0 :                 bs->peer_profile.min_tx = bs->timers.desired_min_tx;
     744             :         }
     745             : 
     746          16 :         if (bpc->bpc_has_recvinterval) {
     747           0 :                 bs->timers.required_min_rx = bpc->bpc_recvinterval * 1000;
     748           0 :                 bs->peer_profile.min_rx = bs->timers.required_min_rx;
     749             :         }
     750             : 
     751          16 :         if (bpc->bpc_has_detectmultiplier) {
     752           0 :                 bs->detect_mult = bpc->bpc_detectmultiplier;
     753           0 :                 bs->peer_profile.detection_multiplier = bs->detect_mult;
     754             :         }
     755             : 
     756          16 :         if (bpc->bpc_has_echorecvinterval) {
     757           0 :                 bs->timers.required_min_echo_rx = bpc->bpc_echorecvinterval * 1000;
     758           0 :                 bs->peer_profile.min_echo_rx = bs->timers.required_min_echo_rx;
     759             :         }
     760             : 
     761          16 :         if (bpc->bpc_has_echotxinterval) {
     762           0 :                 bs->timers.desired_min_echo_tx = bpc->bpc_echotxinterval * 1000;
     763           0 :                 bs->peer_profile.min_echo_tx = bs->timers.desired_min_echo_tx;
     764             :         }
     765             : 
     766          16 :         if (bpc->bpc_has_label)
     767           0 :                 bfd_session_update_label(bs, bpc->bpc_label);
     768             : 
     769          16 :         if (bpc->bpc_cbit)
     770           0 :                 SET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
     771             :         else
     772          16 :                 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT);
     773             : 
     774          16 :         if (bpc->bpc_has_minimum_ttl) {
     775          16 :                 bs->mh_ttl = bpc->bpc_minimum_ttl;
     776          16 :                 bs->peer_profile.minimum_ttl = bpc->bpc_minimum_ttl;
     777             :         }
     778             : 
     779          16 :         bs->peer_profile.echo_mode = bpc->bpc_echo;
     780          16 :         bfd_set_echo(bs, bpc->bpc_echo);
     781             : 
     782             :         /*
     783             :          * Shutdown needs to be the last in order to avoid timers enable when
     784             :          * the session is disabled.
     785             :          */
     786          16 :         bs->peer_profile.admin_shutdown = bpc->bpc_shutdown;
     787          16 :         bfd_set_passive_mode(bs, bpc->bpc_passive);
     788          16 :         bfd_set_shutdown(bs, bpc->bpc_shutdown);
     789             : 
     790             :         /*
     791             :          * Apply profile last: it also calls `bfd_set_shutdown`.
     792             :          *
     793             :          * There is no problem calling `shutdown` twice if the value doesn't
     794             :          * change or if it is overridden by peer specific configuration.
     795             :          */
     796          16 :         if (bpc->bpc_has_profile)
     797           2 :                 bfd_profile_apply(bpc->bpc_profile, bs);
     798          16 : }
     799             : 
     800           0 : static int bfd_session_update(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
     801             : {
     802             :         /* User didn't want to update, return failure. */
     803           0 :         if (bpc->bpc_createonly)
     804             :                 return -1;
     805             : 
     806           0 :         _bfd_session_update(bs, bpc);
     807             : 
     808           0 :         control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
     809             : 
     810           0 :         return 0;
     811             : }
     812             : 
     813          16 : void bfd_session_free(struct bfd_session *bs)
     814             : {
     815          16 :         struct bfd_session_observer *bso;
     816             : 
     817          16 :         bfd_session_disable(bs);
     818             : 
     819             :         /* Remove session from data plane if any. */
     820          16 :         bfd_dplane_delete_session(bs);
     821             : 
     822          16 :         bfd_key_delete(bs->key);
     823          16 :         bfd_id_delete(bs->discrs.my_discr);
     824             : 
     825             :         /* Remove observer if any. */
     826          21 :         TAILQ_FOREACH(bso, &bglobal.bg_obslist, bso_entry) {
     827          21 :                 if (bso->bso_bs != bs)
     828           5 :                         continue;
     829             : 
     830             :                 break;
     831             :         }
     832          16 :         if (bso != NULL)
     833          16 :                 bs_observer_del(bso);
     834             : 
     835          16 :         pl_free(bs->pl);
     836             : 
     837          16 :         XFREE(MTYPE_BFDD_PROFILE, bs->profile_name);
     838          16 :         XFREE(MTYPE_BFDD_CONFIG, bs);
     839          16 : }
     840             : 
     841          16 : struct bfd_session *ptm_bfd_sess_new(struct bfd_peer_cfg *bpc)
     842             : {
     843          16 :         struct bfd_session *bfd, *l_bfd;
     844             : 
     845             :         /* check to see if this needs a new session */
     846          16 :         l_bfd = bs_peer_find(bpc);
     847          16 :         if (l_bfd) {
     848             :                 /* Requesting a duplicated peer means update configuration. */
     849           0 :                 if (bfd_session_update(l_bfd, bpc) == 0)
     850             :                         return l_bfd;
     851             :                 else
     852             :                         return NULL;
     853             :         }
     854             : 
     855             :         /* Get BFD session storage with its defaults. */
     856          16 :         bfd = bfd_session_new();
     857             : 
     858             :         /*
     859             :          * Store interface/VRF name in case we need to delay session
     860             :          * start. See `bfd_session_enable` for more information.
     861             :          */
     862          16 :         if (bpc->bpc_has_localif)
     863          16 :                 strlcpy(bfd->key.ifname, bpc->bpc_localif,
     864             :                         sizeof(bfd->key.ifname));
     865             : 
     866          16 :         if (bpc->bpc_has_vrfname)
     867          16 :                 strlcpy(bfd->key.vrfname, bpc->bpc_vrfname,
     868             :                         sizeof(bfd->key.vrfname));
     869             :         else
     870           0 :                 strlcpy(bfd->key.vrfname, VRF_DEFAULT_NAME,
     871             :                         sizeof(bfd->key.vrfname));
     872             : 
     873             :         /* Copy remaining data. */
     874          16 :         if (bpc->bpc_ipv4 == false)
     875           0 :                 SET_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6);
     876             : 
     877          16 :         bfd->key.family = (bpc->bpc_ipv4) ? AF_INET : AF_INET6;
     878          16 :         switch (bfd->key.family) {
     879          16 :         case AF_INET:
     880          16 :                 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin.sin_addr,
     881             :                        sizeof(bpc->bpc_peer.sa_sin.sin_addr));
     882          16 :                 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin.sin_addr,
     883             :                        sizeof(bpc->bpc_local.sa_sin.sin_addr));
     884          16 :                 break;
     885             : 
     886           0 :         case AF_INET6:
     887           0 :                 memcpy(&bfd->key.peer, &bpc->bpc_peer.sa_sin6.sin6_addr,
     888             :                        sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
     889           0 :                 memcpy(&bfd->key.local, &bpc->bpc_local.sa_sin6.sin6_addr,
     890             :                        sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
     891           0 :                 break;
     892             : 
     893             :         default:
     894             :                 assert(1);
     895             :                 break;
     896             :         }
     897             : 
     898          16 :         if (bpc->bpc_mhop)
     899           0 :                 SET_FLAG(bfd->flags, BFD_SESS_FLAG_MH);
     900             : 
     901          16 :         bfd->key.mhop = bpc->bpc_mhop;
     902             : 
     903          16 :         if (bs_registrate(bfd) == NULL)
     904             :                 return NULL;
     905             : 
     906             :         /* Apply other configurations. */
     907          16 :         _bfd_session_update(bfd, bpc);
     908             : 
     909          16 :         return bfd;
     910             : }
     911             : 
     912          16 : struct bfd_session *bs_registrate(struct bfd_session *bfd)
     913             : {
     914             :         /* Registrate session into data structures. */
     915          16 :         bfd_key_insert(bfd);
     916          16 :         bfd->discrs.my_discr = ptm_bfd_gen_ID();
     917          16 :         bfd_id_insert(bfd);
     918             : 
     919             :         /* Try to enable session and schedule for packet receive/send. */
     920          16 :         if (bfd_session_enable(bfd) == -1) {
     921             :                 /* Unrecoverable failure, remove the session/peer. */
     922           0 :                 bfd_session_free(bfd);
     923           0 :                 return NULL;
     924             :         }
     925             : 
     926             :         /* Add observer if we have moving parts. */
     927          16 :         if (bfd->key.ifname[0] || bfd->key.vrfname[0] || bfd->sock == -1)
     928          16 :                 bs_observer_add(bfd);
     929             : 
     930          16 :         if (bglobal.debug_peer_event)
     931           0 :                 zlog_debug("session-new: %s", bs_to_string(bfd));
     932             : 
     933          16 :         control_notify_config(BCM_NOTIFY_CONFIG_ADD, bfd);
     934             : 
     935          16 :         return bfd;
     936             : }
     937             : 
     938           0 : int ptm_bfd_sess_del(struct bfd_peer_cfg *bpc)
     939             : {
     940           0 :         struct bfd_session *bs;
     941             : 
     942             :         /* Find session and call free(). */
     943           0 :         bs = bs_peer_find(bpc);
     944           0 :         if (bs == NULL)
     945             :                 return -1;
     946             : 
     947             :         /* This pointer is being referenced, don't let it be deleted. */
     948           0 :         if (bs->refcount > 0) {
     949           0 :                 zlog_err("session-delete: refcount failure: %" PRIu64" references",
     950             :                          bs->refcount);
     951           0 :                 return -1;
     952             :         }
     953             : 
     954           0 :         if (bglobal.debug_peer_event)
     955           0 :                 zlog_debug("%s: %s", __func__, bs_to_string(bs));
     956             : 
     957           0 :         control_notify_config(BCM_NOTIFY_CONFIG_DELETE, bs);
     958             : 
     959           0 :         bfd_session_free(bs);
     960             : 
     961           0 :         return 0;
     962             : }
     963             : 
     964           6 : void bfd_set_polling(struct bfd_session *bs)
     965             : {
     966             :         /*
     967             :          * Start polling procedure: the only timers that require polling
     968             :          * to change value without losing connection are:
     969             :          *
     970             :          *   - Desired minimum transmission interval;
     971             :          *   - Required minimum receive interval;
     972             :          *
     973             :          * RFC 5880, Section 6.8.3.
     974             :          */
     975           6 :         bs->polling = 1;
     976           0 : }
     977             : 
     978             : /*
     979             :  * bs_<state>_handler() functions implement the BFD state machine
     980             :  * transition mechanism. `<state>` is the current session state and
     981             :  * the parameter `nstate` is the peer new state.
     982             :  */
     983             : static void bs_admin_down_handler(struct bfd_session *bs
     984             :                                   __attribute__((__unused__)),
     985             :                                   int nstate __attribute__((__unused__)))
     986             : {
     987             :         /*
     988             :          * We are administratively down, there is no state machine
     989             :          * handling.
     990             :          */
     991             : }
     992             : 
     993           8 : static void bs_down_handler(struct bfd_session *bs, int nstate)
     994             : {
     995           8 :         switch (nstate) {
     996             :         case PTM_BFD_ADM_DOWN:
     997             :                 /*
     998             :                  * Remote peer doesn't want to talk, so lets keep the
     999             :                  * connection down.
    1000             :                  */
    1001             :         case PTM_BFD_UP:
    1002             :                 /* Peer can't be up yet, wait it go to 'init' or 'down'. */
    1003             :                 break;
    1004             : 
    1005           5 :         case PTM_BFD_DOWN:
    1006             :                 /*
    1007             :                  * Remote peer agreed that the path is down, lets try to
    1008             :                  * bring it up.
    1009             :                  */
    1010           5 :                 bs->ses_state = PTM_BFD_INIT;
    1011             : 
    1012             :                 /*
    1013             :                  * RFC 5880, Section 6.1.
    1014             :                  * A system taking the Passive role MUST NOT begin
    1015             :                  * sending BFD packets for a particular session until
    1016             :                  * it has received a BFD packet for that session, and thus
    1017             :                  * has learned the remote system's discriminator value.
    1018             :                  *
    1019             :                  * Now we can start transmission timer in passive mode.
    1020             :                  */
    1021           5 :                 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE))
    1022           0 :                         ptm_bfd_xmt_TO(bs, 0);
    1023             : 
    1024             :                 break;
    1025             : 
    1026           3 :         case PTM_BFD_INIT:
    1027             :                 /*
    1028             :                  * Remote peer told us his path is up, lets turn
    1029             :                  * activate the session.
    1030             :                  */
    1031           3 :                 ptm_bfd_sess_up(bs);
    1032           3 :                 break;
    1033             : 
    1034           0 :         default:
    1035           0 :                 if (bglobal.debug_peer_event)
    1036           0 :                         zlog_debug("state-change: unhandled neighbor state: %d",
    1037             :                                    nstate);
    1038             :                 break;
    1039             :         }
    1040           8 : }
    1041             : 
    1042           3 : static void bs_init_handler(struct bfd_session *bs, int nstate)
    1043             : {
    1044           3 :         switch (nstate) {
    1045           0 :         case PTM_BFD_ADM_DOWN:
    1046             :                 /*
    1047             :                  * Remote peer doesn't want to talk, so lets make the
    1048             :                  * connection down.
    1049             :                  */
    1050           0 :                 ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
    1051           0 :                 break;
    1052             : 
    1053             :         case PTM_BFD_DOWN:
    1054             :                 /* Remote peer hasn't moved to first stage yet. */
    1055             :                 break;
    1056             : 
    1057           3 :         case PTM_BFD_INIT:
    1058             :         case PTM_BFD_UP:
    1059             :                 /* We agreed on the settings and the path is up. */
    1060           3 :                 ptm_bfd_sess_up(bs);
    1061           3 :                 break;
    1062             : 
    1063           0 :         default:
    1064           0 :                 if (bglobal.debug_peer_event)
    1065           0 :                         zlog_debug("state-change: unhandled neighbor state: %d",
    1066             :                                    nstate);
    1067             :                 break;
    1068             :         }
    1069           3 : }
    1070             : 
    1071          19 : static void bs_up_handler(struct bfd_session *bs, int nstate)
    1072             : {
    1073          19 :         switch (nstate) {
    1074           2 :         case PTM_BFD_ADM_DOWN:
    1075             :         case PTM_BFD_DOWN:
    1076             :                 /* Peer lost or asked to shutdown connection. */
    1077           2 :                 ptm_bfd_sess_dn(bs, BD_NEIGHBOR_DOWN);
    1078           2 :                 break;
    1079             : 
    1080             :         case PTM_BFD_INIT:
    1081             :         case PTM_BFD_UP:
    1082             :                 /* Path is up and working. */
    1083             :                 break;
    1084             : 
    1085           0 :         default:
    1086           0 :                 if (bglobal.debug_peer_event)
    1087           0 :                         zlog_debug("state-change: unhandled neighbor state: %d",
    1088             :                                    nstate);
    1089             :                 break;
    1090             :         }
    1091          19 : }
    1092             : 
    1093          30 : void bs_state_handler(struct bfd_session *bs, int nstate)
    1094             : {
    1095          30 :         switch (bs->ses_state) {
    1096             :         case PTM_BFD_ADM_DOWN:
    1097             :                 bs_admin_down_handler(bs, nstate);
    1098             :                 break;
    1099           8 :         case PTM_BFD_DOWN:
    1100           8 :                 bs_down_handler(bs, nstate);
    1101           8 :                 break;
    1102           3 :         case PTM_BFD_INIT:
    1103           3 :                 bs_init_handler(bs, nstate);
    1104           3 :                 break;
    1105          19 :         case PTM_BFD_UP:
    1106          19 :                 bs_up_handler(bs, nstate);
    1107          19 :                 break;
    1108             : 
    1109           0 :         default:
    1110           0 :                 if (bglobal.debug_peer_event)
    1111           0 :                         zlog_debug("state-change: [%s] is in invalid state: %d",
    1112             :                                    bs_to_string(bs), nstate);
    1113             :                 break;
    1114             :         }
    1115          30 : }
    1116             : 
    1117             : /*
    1118             :  * Handles echo timer manipulation after updating timer.
    1119             :  */
    1120          30 : void bs_echo_timer_handler(struct bfd_session *bs)
    1121             : {
    1122          30 :         uint32_t old_timer;
    1123             : 
    1124             :         /*
    1125             :          * Before doing any echo handling, check if it is possible to
    1126             :          * use it.
    1127             :          *
    1128             :          *   - Check for `echo-mode` configuration.
    1129             :          *   - Check that we are not using multi hop (RFC 5883,
    1130             :          *     Section 3).
    1131             :          *   - Check that we are already at the up state.
    1132             :          */
    1133          30 :         if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO) == 0
    1134          30 :             || CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)
    1135           0 :             || bs->ses_state != PTM_BFD_UP)
    1136             :                 return;
    1137             : 
    1138             :         /* Remote peer asked to stop echo. */
    1139           0 :         if (bs->remote_timers.required_min_echo == 0) {
    1140           0 :                 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE))
    1141           0 :                         ptm_bfd_echo_stop(bs);
    1142             : 
    1143           0 :                 return;
    1144             :         }
    1145             : 
    1146             :         /*
    1147             :          * Calculate the echo transmission timer: we must not send
    1148             :          * echo packets faster than the minimum required time
    1149             :          * announced by the remote system.
    1150             :          *
    1151             :          * RFC 5880, Section 6.8.9.
    1152             :          */
    1153           0 :         old_timer = bs->echo_xmt_TO;
    1154           0 :         if (bs->remote_timers.required_min_echo > bs->timers.desired_min_echo_tx)
    1155           0 :                 bs->echo_xmt_TO = bs->remote_timers.required_min_echo;
    1156             :         else
    1157           0 :                 bs->echo_xmt_TO = bs->timers.desired_min_echo_tx;
    1158             : 
    1159           0 :         if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0
    1160           0 :             || old_timer != bs->echo_xmt_TO)
    1161           0 :                 ptm_bfd_echo_start(bs);
    1162             : }
    1163             : 
    1164             : /*
    1165             :  * RFC 5880 Section 6.5.
    1166             :  *
    1167             :  * When a BFD control packet with the final bit is received, we must
    1168             :  * update the session parameters.
    1169             :  */
    1170          12 : void bs_final_handler(struct bfd_session *bs)
    1171             : {
    1172             :         /* Start using our new timers. */
    1173          12 :         bs->cur_timers.desired_min_tx = bs->timers.desired_min_tx;
    1174          12 :         bs->cur_timers.required_min_rx = bs->timers.required_min_rx;
    1175             : 
    1176             :         /*
    1177             :          * TODO: demand mode. See RFC 5880 Section 6.1.
    1178             :          *
    1179             :          * When using demand mode we must disable the detection timer
    1180             :          * for lost control packets.
    1181             :          */
    1182          12 :         if (bs->demand_mode) {
    1183             :                 /* Notify watchers about changed timers. */
    1184           0 :                 control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
    1185           0 :                 return;
    1186             :         }
    1187             : 
    1188             :         /*
    1189             :          * Calculate transmission time based on new timers.
    1190             :          *
    1191             :          * Transmission calculation:
    1192             :          * Unless specified by exceptions at the end of Section 6.8.7, the
    1193             :          * transmission time will be determined by the system with the
    1194             :          * slowest rate.
    1195             :          *
    1196             :          * RFC 5880, Section 6.8.7.
    1197             :          */
    1198          12 :         if (bs->timers.desired_min_tx > bs->remote_timers.required_min_rx)
    1199           2 :                 bs->xmt_TO = bs->timers.desired_min_tx;
    1200             :         else
    1201          10 :                 bs->xmt_TO = bs->remote_timers.required_min_rx;
    1202             : 
    1203             :         /* Apply new transmission timer immediately. */
    1204          12 :         ptm_bfd_start_xmt_timer(bs, false);
    1205             : 
    1206             :         /* Notify watchers about changed timers. */
    1207          12 :         control_notify_config(BCM_NOTIFY_CONFIG_UPDATE, bs);
    1208             : }
    1209             : 
    1210          35 : void bs_set_slow_timers(struct bfd_session *bs)
    1211             : {
    1212             :         /*
    1213             :          * BFD connection must use slow timers before going up or after
    1214             :          * losing connectivity to avoid wasting bandwidth.
    1215             :          *
    1216             :          * RFC 5880, Section 6.8.3.
    1217             :          */
    1218          35 :         bs->cur_timers.desired_min_tx = BFD_DEF_SLOWTX;
    1219          35 :         bs->cur_timers.required_min_rx = BFD_DEF_SLOWTX;
    1220          35 :         bs->cur_timers.required_min_echo = 0;
    1221             : 
    1222             :         /* Set the appropriated timeouts for slow connection. */
    1223          35 :         bs->detect_TO = (BFD_DEFDETECTMULT * BFD_DEF_SLOWTX);
    1224          35 :         bs->xmt_TO = BFD_DEF_SLOWTX;
    1225           0 : }
    1226             : 
    1227          18 : void bfd_set_echo(struct bfd_session *bs, bool echo)
    1228             : {
    1229          18 :         if (echo) {
    1230             :                 /* Check if echo mode is already active. */
    1231           0 :                 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
    1232             :                         return;
    1233             : 
    1234           0 :                 SET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
    1235             : 
    1236             :                 /* Activate/update echo receive timeout timer. */
    1237           0 :                 if (bs->bdc == NULL)
    1238           0 :                         bs_echo_timer_handler(bs);
    1239             :         } else {
    1240             :                 /* Check if echo mode is already disabled. */
    1241          18 :                 if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO))
    1242             :                         return;
    1243             : 
    1244           0 :                 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_ECHO);
    1245             : 
    1246             :                 /* Deactivate timeout timer. */
    1247           0 :                 if (bs->bdc == NULL)
    1248           0 :                         ptm_bfd_echo_stop(bs);
    1249             :         }
    1250             : }
    1251             : 
    1252          18 : void bfd_set_shutdown(struct bfd_session *bs, bool shutdown)
    1253             : {
    1254          18 :         bool is_shutdown;
    1255             : 
    1256             :         /*
    1257             :          * Special case: we are batching changes and the previous state was
    1258             :          * not shutdown. Instead of potentially disconnect a running peer,
    1259             :          * we'll get the current status to validate we were really down.
    1260             :          */
    1261          18 :         if (bs->ses_state == PTM_BFD_UP)
    1262             :                 is_shutdown = false;
    1263             :         else
    1264          18 :                 is_shutdown = CHECK_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
    1265             : 
    1266          18 :         if (shutdown) {
    1267             :                 /* Already shutdown. */
    1268           0 :                 if (is_shutdown)
    1269             :                         return;
    1270             : 
    1271           0 :                 SET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
    1272             : 
    1273             :                 /* Handle data plane shutdown case. */
    1274           0 :                 if (bs->bdc) {
    1275           0 :                         bs->ses_state = PTM_BFD_ADM_DOWN;
    1276           0 :                         bfd_dplane_update_session(bs);
    1277           0 :                         control_notify(bs, bs->ses_state);
    1278           0 :                         return;
    1279             :                 }
    1280             : 
    1281             :                 /* Disable all events. */
    1282           0 :                 bfd_recvtimer_delete(bs);
    1283           0 :                 bfd_echo_recvtimer_delete(bs);
    1284           0 :                 bfd_xmttimer_delete(bs);
    1285           0 :                 bfd_echo_xmttimer_delete(bs);
    1286             : 
    1287             :                 /* Change and notify state change. */
    1288           0 :                 bs->ses_state = PTM_BFD_ADM_DOWN;
    1289           0 :                 control_notify(bs, bs->ses_state);
    1290             : 
    1291             :                 /* Don't try to send packets with a disabled session. */
    1292           0 :                 if (bs->sock != -1)
    1293           0 :                         ptm_bfd_snd(bs, 0);
    1294             :         } else {
    1295             :                 /* Already working. */
    1296          18 :                 if (!is_shutdown)
    1297             :                         return;
    1298             : 
    1299           0 :                 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_SHUTDOWN);
    1300             : 
    1301             :                 /* Handle data plane shutdown case. */
    1302           0 :                 if (bs->bdc) {
    1303           0 :                         bs->ses_state = PTM_BFD_DOWN;
    1304           0 :                         bfd_dplane_update_session(bs);
    1305           0 :                         control_notify(bs, bs->ses_state);
    1306           0 :                         return;
    1307             :                 }
    1308             : 
    1309             :                 /* Change and notify state change. */
    1310           0 :                 bs->ses_state = PTM_BFD_DOWN;
    1311           0 :                 control_notify(bs, bs->ses_state);
    1312             : 
    1313             :                 /* Enable timers if non passive, otherwise stop them. */
    1314           0 :                 if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) {
    1315           0 :                         bfd_recvtimer_delete(bs);
    1316           0 :                         bfd_xmttimer_delete(bs);
    1317             :                 } else {
    1318           0 :                         bfd_recvtimer_update(bs);
    1319           0 :                         bfd_xmttimer_update(bs, bs->xmt_TO);
    1320             :                 }
    1321             :         }
    1322             : }
    1323             : 
    1324          18 : void bfd_set_passive_mode(struct bfd_session *bs, bool passive)
    1325             : {
    1326          18 :         if (passive) {
    1327           0 :                 SET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE);
    1328             : 
    1329             :                 /* Session is already up and running, nothing to do now. */
    1330           0 :                 if (bs->ses_state != PTM_BFD_DOWN)
    1331             :                         return;
    1332             : 
    1333             :                 /* Lets disable the timers since we are now passive. */
    1334           0 :                 bfd_recvtimer_delete(bs);
    1335           0 :                 bfd_xmttimer_delete(bs);
    1336             :         } else {
    1337          18 :                 UNSET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE);
    1338             : 
    1339             :                 /* Session is already up and running, nothing to do now. */
    1340          18 :                 if (bs->ses_state != PTM_BFD_DOWN)
    1341             :                         return;
    1342             : 
    1343             :                 /* Session is down, let it attempt to start the connection. */
    1344          18 :                 bfd_xmttimer_update(bs, bs->xmt_TO);
    1345          18 :                 bfd_recvtimer_update(bs);
    1346             :         }
    1347             : }
    1348             : 
    1349             : /*
    1350             :  * Helper functions.
    1351             :  */
    1352           0 : static const char *get_diag_str(int diag)
    1353             : {
    1354           0 :         for (int i = 0; diag_list[i].str; i++) {
    1355           0 :                 if (diag_list[i].type == diag)
    1356             :                         return diag_list[i].str;
    1357             :         }
    1358             :         return "N/A";
    1359             : }
    1360             : 
    1361           0 : const char *satostr(const struct sockaddr_any *sa)
    1362             : {
    1363             : #define INETSTR_BUFCOUNT 8
    1364           0 :         static char buf[INETSTR_BUFCOUNT][INET6_ADDRSTRLEN];
    1365           0 :         static int bufidx;
    1366           0 :         const struct sockaddr_in *sin = &sa->sa_sin;
    1367           0 :         const struct sockaddr_in6 *sin6 = &sa->sa_sin6;
    1368             : 
    1369           0 :         bufidx += (bufidx + 1) % INETSTR_BUFCOUNT;
    1370           0 :         buf[bufidx][0] = 0;
    1371             : 
    1372           0 :         switch (sin->sin_family) {
    1373           0 :         case AF_INET:
    1374           0 :                 inet_ntop(AF_INET, &sin->sin_addr, buf[bufidx],
    1375             :                           sizeof(buf[bufidx]));
    1376           0 :                 break;
    1377           0 :         case AF_INET6:
    1378           0 :                 inet_ntop(AF_INET6, &sin6->sin6_addr, buf[bufidx],
    1379             :                           sizeof(buf[bufidx]));
    1380           0 :                 break;
    1381             : 
    1382           0 :         default:
    1383           0 :                 strlcpy(buf[bufidx], "unknown", sizeof(buf[bufidx]));
    1384           0 :                 break;
    1385             :         }
    1386             : 
    1387           0 :         return buf[bufidx];
    1388             : }
    1389             : 
    1390          52 : const char *diag2str(uint8_t diag)
    1391             : {
    1392          52 :         switch (diag) {
    1393             :         case 0:
    1394             :                 return "ok";
    1395           0 :         case 1:
    1396           0 :                 return "control detection time expired";
    1397           0 :         case 2:
    1398           0 :                 return "echo function failed";
    1399           0 :         case 3:
    1400           0 :                 return "neighbor signaled session down";
    1401           0 :         case 4:
    1402           0 :                 return "forwarding plane reset";
    1403           0 :         case 5:
    1404           0 :                 return "path down";
    1405           0 :         case 6:
    1406           0 :                 return "concatenated path down";
    1407           0 :         case 7:
    1408           0 :                 return "administratively down";
    1409           0 :         case 8:
    1410           0 :                 return "reverse concatenated path down";
    1411           0 :         default:
    1412           0 :                 return "unknown";
    1413             :         }
    1414             : }
    1415             : 
    1416           0 : int strtosa(const char *addr, struct sockaddr_any *sa)
    1417             : {
    1418           0 :         memset(sa, 0, sizeof(*sa));
    1419             : 
    1420           0 :         if (inet_pton(AF_INET, addr, &sa->sa_sin.sin_addr) == 1) {
    1421           0 :                 sa->sa_sin.sin_family = AF_INET;
    1422             : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
    1423             :                 sa->sa_sin.sin_len = sizeof(sa->sa_sin);
    1424             : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
    1425           0 :                 return 0;
    1426             :         }
    1427             : 
    1428           0 :         if (inet_pton(AF_INET6, addr, &sa->sa_sin6.sin6_addr) == 1) {
    1429           0 :                 sa->sa_sin6.sin6_family = AF_INET6;
    1430             : #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
    1431             :                 sa->sa_sin6.sin6_len = sizeof(sa->sa_sin6);
    1432             : #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
    1433           0 :                 return 0;
    1434             :         }
    1435             : 
    1436             :         return -1;
    1437             : }
    1438             : 
    1439           0 : void integer2timestr(uint64_t time, char *buf, size_t buflen)
    1440             : {
    1441           0 :         uint64_t year, month, day, hour, minute, second;
    1442           0 :         int rv;
    1443             : 
    1444             : #define MINUTES (60)
    1445             : #define HOURS (60 * MINUTES)
    1446             : #define DAYS (24 * HOURS)
    1447             : #define MONTHS (30 * DAYS)
    1448             : #define YEARS (12 * MONTHS)
    1449           0 :         if (time >= YEARS) {
    1450           0 :                 year = time / YEARS;
    1451           0 :                 time -= year * YEARS;
    1452             : 
    1453           0 :                 rv = snprintfrr(buf, buflen, "%" PRIu64 " year(s), ", year);
    1454           0 :                 buf += rv;
    1455           0 :                 buflen -= rv;
    1456             :         }
    1457           0 :         if (time >= MONTHS) {
    1458           0 :                 month = time / MONTHS;
    1459           0 :                 time -= month * MONTHS;
    1460             : 
    1461           0 :                 rv = snprintfrr(buf, buflen, "%" PRIu64 " month(s), ", month);
    1462           0 :                 buf += rv;
    1463           0 :                 buflen -= rv;
    1464             :         }
    1465           0 :         if (time >= DAYS) {
    1466           0 :                 day = time / DAYS;
    1467           0 :                 time -= day * DAYS;
    1468             : 
    1469           0 :                 rv = snprintfrr(buf, buflen, "%" PRIu64 " day(s), ", day);
    1470           0 :                 buf += rv;
    1471           0 :                 buflen -= rv;
    1472             :         }
    1473           0 :         if (time >= HOURS) {
    1474           0 :                 hour = time / HOURS;
    1475           0 :                 time -= hour * HOURS;
    1476             : 
    1477           0 :                 rv = snprintfrr(buf, buflen, "%" PRIu64 " hour(s), ", hour);
    1478           0 :                 buf += rv;
    1479           0 :                 buflen -= rv;
    1480             :         }
    1481           0 :         if (time >= MINUTES) {
    1482           0 :                 minute = time / MINUTES;
    1483           0 :                 time -= minute * MINUTES;
    1484             : 
    1485           0 :                 rv = snprintfrr(buf, buflen, "%" PRIu64 " minute(s), ", minute);
    1486           0 :                 buf += rv;
    1487           0 :                 buflen -= rv;
    1488             :         }
    1489           0 :         second = time % MINUTES;
    1490           0 :         snprintfrr(buf, buflen, "%" PRIu64 " second(s)", second);
    1491           0 : }
    1492             : 
    1493           0 : const char *bs_to_string(const struct bfd_session *bs)
    1494             : {
    1495           0 :         static char buf[256];
    1496           0 :         char addr_buf[INET6_ADDRSTRLEN];
    1497           0 :         int pos;
    1498           0 :         bool is_mhop = CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH);
    1499             : 
    1500           0 :         pos = snprintf(buf, sizeof(buf), "mhop:%s", is_mhop ? "yes" : "no");
    1501           0 :         pos += snprintf(buf + pos, sizeof(buf) - pos, " peer:%s",
    1502           0 :                         inet_ntop(bs->key.family, &bs->key.peer, addr_buf,
    1503             :                                   sizeof(addr_buf)));
    1504           0 :         pos += snprintf(buf + pos, sizeof(buf) - pos, " local:%s",
    1505           0 :                         inet_ntop(bs->key.family, &bs->key.local, addr_buf,
    1506             :                                   sizeof(addr_buf)));
    1507           0 :         if (bs->key.vrfname[0])
    1508           0 :                 pos += snprintf(buf + pos, sizeof(buf) - pos, " vrf:%s",
    1509           0 :                                 bs->key.vrfname);
    1510           0 :         if (bs->key.ifname[0])
    1511           0 :                 pos += snprintf(buf + pos, sizeof(buf) - pos, " ifname:%s",
    1512           0 :                                 bs->key.ifname);
    1513             : 
    1514           0 :         (void)pos;
    1515             : 
    1516           0 :         return buf;
    1517             : }
    1518             : 
    1519          16 : int bs_observer_add(struct bfd_session *bs)
    1520             : {
    1521          16 :         struct bfd_session_observer *bso;
    1522             : 
    1523          16 :         bso = XCALLOC(MTYPE_BFDD_SESSION_OBSERVER, sizeof(*bso));
    1524          16 :         bso->bso_bs = bs;
    1525          16 :         bso->bso_addr.family = bs->key.family;
    1526          16 :         memcpy(&bso->bso_addr.u.prefix, &bs->key.local,
    1527             :                sizeof(bs->key.local));
    1528             : 
    1529          16 :         TAILQ_INSERT_TAIL(&bglobal.bg_obslist, bso, bso_entry);
    1530             : 
    1531          16 :         return 0;
    1532             : }
    1533             : 
    1534          16 : void bs_observer_del(struct bfd_session_observer *bso)
    1535             : {
    1536          16 :         TAILQ_REMOVE(&bglobal.bg_obslist, bso, bso_entry);
    1537          16 :         XFREE(MTYPE_BFDD_SESSION_OBSERVER, bso);
    1538          16 : }
    1539             : 
    1540           0 : void bs_to_bpc(struct bfd_session *bs, struct bfd_peer_cfg *bpc)
    1541             : {
    1542           0 :         memset(bpc, 0, sizeof(*bpc));
    1543             : 
    1544           0 :         bpc->bpc_ipv4 = (bs->key.family == AF_INET);
    1545           0 :         bpc->bpc_mhop = bs->key.mhop;
    1546             : 
    1547           0 :         switch (bs->key.family) {
    1548           0 :         case AF_INET:
    1549           0 :                 bpc->bpc_peer.sa_sin.sin_family = AF_INET;
    1550           0 :                 memcpy(&bpc->bpc_peer.sa_sin.sin_addr, &bs->key.peer,
    1551             :                        sizeof(bpc->bpc_peer.sa_sin.sin_addr));
    1552             : 
    1553           0 :                 if (memcmp(&bs->key.local, &zero_addr, sizeof(bs->key.local))) {
    1554           0 :                         bpc->bpc_local.sa_sin.sin_family = AF_INET6;
    1555           0 :                         memcpy(&bpc->bpc_local.sa_sin.sin_addr, &bs->key.local,
    1556             :                                sizeof(bpc->bpc_local.sa_sin.sin_addr));
    1557             :                 }
    1558             :                 break;
    1559             : 
    1560           0 :         case AF_INET6:
    1561           0 :                 bpc->bpc_peer.sa_sin.sin_family = AF_INET6;
    1562           0 :                 memcpy(&bpc->bpc_peer.sa_sin6.sin6_addr, &bs->key.peer,
    1563             :                        sizeof(bpc->bpc_peer.sa_sin6.sin6_addr));
    1564             : 
    1565           0 :                 bpc->bpc_local.sa_sin6.sin6_family = AF_INET6;
    1566           0 :                 memcpy(&bpc->bpc_local.sa_sin6.sin6_addr, &bs->key.local,
    1567             :                        sizeof(bpc->bpc_local.sa_sin6.sin6_addr));
    1568           0 :                 break;
    1569             :         }
    1570             : 
    1571           0 :         if (bs->key.ifname[0]) {
    1572           0 :                 bpc->bpc_has_localif = true;
    1573           0 :                 strlcpy(bpc->bpc_localif, bs->key.ifname,
    1574             :                         sizeof(bpc->bpc_localif));
    1575             :         }
    1576             : 
    1577           0 :         if (bs->key.vrfname[0]) {
    1578           0 :                 bpc->bpc_has_vrfname = true;
    1579           0 :                 strlcpy(bpc->bpc_vrfname, bs->key.vrfname,
    1580             :                         sizeof(bpc->bpc_vrfname));
    1581             :         }
    1582           0 : }
    1583             : 
    1584             : 
    1585             : /*
    1586             :  * BFD hash data structures to find sessions.
    1587             :  */
    1588             : static struct hash *bfd_id_hash;
    1589             : static struct hash *bfd_key_hash;
    1590             : 
    1591             : static unsigned int bfd_id_hash_do(const void *p);
    1592             : static unsigned int bfd_key_hash_do(const void *p);
    1593             : 
    1594             : static void _bfd_free(struct hash_bucket *hb,
    1595             :                       void *arg __attribute__((__unused__)));
    1596             : 
    1597             : /* BFD hash for our discriminator. */
    1598          64 : static unsigned int bfd_id_hash_do(const void *p)
    1599             : {
    1600          64 :         const struct bfd_session *bs = p;
    1601             : 
    1602          64 :         return jhash_1word(bs->discrs.my_discr, 0);
    1603             : }
    1604             : 
    1605          41 : static bool bfd_id_hash_cmp(const void *n1, const void *n2)
    1606             : {
    1607          41 :         const struct bfd_session *bs1 = n1, *bs2 = n2;
    1608             : 
    1609          41 :         return bs1->discrs.my_discr == bs2->discrs.my_discr;
    1610             : }
    1611             : 
    1612             : /* BFD hash for single hop. */
    1613          61 : static unsigned int bfd_key_hash_do(const void *p)
    1614             : {
    1615          61 :         const struct bfd_session *bs = p;
    1616          61 :         struct bfd_key key = bs->key;
    1617             : 
    1618             :         /*
    1619             :          * Local address and interface name are optional and
    1620             :          * can be filled any time after session creation.
    1621             :          * Hash key should not depend on these fields.
    1622             :          */
    1623          61 :         memset(&key.local, 0, sizeof(key.local));
    1624          61 :         memset(key.ifname, 0, sizeof(key.ifname));
    1625             : 
    1626          61 :         return jhash(&key, sizeof(key), 0);
    1627             : }
    1628             : 
    1629          31 : static bool bfd_key_hash_cmp(const void *n1, const void *n2)
    1630             : {
    1631          31 :         const struct bfd_session *bs1 = n1, *bs2 = n2;
    1632             : 
    1633          31 :         if (bs1->key.family != bs2->key.family)
    1634             :                 return false;
    1635          31 :         if (bs1->key.mhop != bs2->key.mhop)
    1636             :                 return false;
    1637          31 :         if (memcmp(&bs1->key.peer, &bs2->key.peer, sizeof(bs1->key.peer)))
    1638             :                 return false;
    1639          31 :         if (memcmp(bs1->key.vrfname, bs2->key.vrfname,
    1640             :                    sizeof(bs1->key.vrfname)))
    1641             :                 return false;
    1642             : 
    1643             :         /*
    1644             :          * Local address is optional and can be empty.
    1645             :          * If both addresses are not empty and different,
    1646             :          * then the keys are different.
    1647             :          */
    1648          31 :         if (memcmp(&bs1->key.local, &zero_addr, sizeof(bs1->key.local))
    1649           0 :             && memcmp(&bs2->key.local, &zero_addr, sizeof(bs2->key.local))
    1650           0 :             && memcmp(&bs1->key.local, &bs2->key.local, sizeof(bs1->key.local)))
    1651             :                 return false;
    1652             : 
    1653             :         /*
    1654             :          * Interface name is optional and can be empty.
    1655             :          * If both names are not empty and different,
    1656             :          * then the keys are different.
    1657             :          */
    1658          31 :         if (bs1->key.ifname[0] && bs2->key.ifname[0]
    1659          31 :             && memcmp(bs1->key.ifname, bs2->key.ifname,
    1660             :                       sizeof(bs1->key.ifname)))
    1661           0 :                 return false;
    1662             : 
    1663             :         return true;
    1664             : }
    1665             : 
    1666             : 
    1667             : /*
    1668             :  * Hash public interface / exported functions.
    1669             :  */
    1670             : 
    1671             : /* Lookup functions. */
    1672          44 : struct bfd_session *bfd_id_lookup(uint32_t id)
    1673             : {
    1674          44 :         struct bfd_session bs;
    1675             : 
    1676          44 :         bs.discrs.my_discr = id;
    1677             : 
    1678          44 :         return hash_lookup(bfd_id_hash, &bs);
    1679             : }
    1680             : 
    1681          52 : struct bfd_session *bfd_key_lookup(struct bfd_key key)
    1682             : {
    1683          52 :         struct bfd_session bs;
    1684             : 
    1685          52 :         bs.key = key;
    1686             : 
    1687          52 :         return hash_lookup(bfd_key_hash, &bs);
    1688             : }
    1689             : 
    1690             : /*
    1691             :  * Delete functions.
    1692             :  *
    1693             :  * Delete functions searches and remove the item from the hash and
    1694             :  * returns a pointer to the removed item data. If the item was not found
    1695             :  * then it returns NULL.
    1696             :  *
    1697             :  * The data stored inside the hash is not free()ed, so you must do it
    1698             :  * manually after getting the pointer back.
    1699             :  */
    1700          16 : struct bfd_session *bfd_id_delete(uint32_t id)
    1701             : {
    1702          16 :         struct bfd_session bs;
    1703             : 
    1704          16 :         bs.discrs.my_discr = id;
    1705             : 
    1706          16 :         return hash_release(bfd_id_hash, &bs);
    1707             : }
    1708             : 
    1709          16 : struct bfd_session *bfd_key_delete(struct bfd_key key)
    1710             : {
    1711          16 :         struct bfd_session bs;
    1712             : 
    1713          16 :         bs.key = key;
    1714             : 
    1715          16 :         return hash_release(bfd_key_hash, &bs);
    1716             : }
    1717             : 
    1718             : /* Iteration functions. */
    1719          22 : void bfd_id_iterate(hash_iter_func hif, void *arg)
    1720             : {
    1721          14 :         hash_iterate(bfd_id_hash, hif, arg);
    1722          14 : }
    1723             : 
    1724           0 : void bfd_key_iterate(hash_iter_func hif, void *arg)
    1725             : {
    1726           0 :         hash_iterate(bfd_key_hash, hif, arg);
    1727           0 : }
    1728             : 
    1729             : /*
    1730             :  * Insert functions.
    1731             :  *
    1732             :  * Inserts session into hash and returns `true` on success, otherwise
    1733             :  * `false`.
    1734             :  */
    1735          16 : bool bfd_id_insert(struct bfd_session *bs)
    1736             : {
    1737          16 :         return (hash_get(bfd_id_hash, bs, hash_alloc_intern) == bs);
    1738             : }
    1739             : 
    1740          16 : bool bfd_key_insert(struct bfd_session *bs)
    1741             : {
    1742          16 :         return (hash_get(bfd_key_hash, bs, hash_alloc_intern) == bs);
    1743             : }
    1744             : 
    1745           8 : void bfd_initialize(void)
    1746             : {
    1747           8 :         bfd_id_hash = hash_create(bfd_id_hash_do, bfd_id_hash_cmp,
    1748             :                                   "BFD session discriminator hash");
    1749           8 :         bfd_key_hash = hash_create(bfd_key_hash_do, bfd_key_hash_cmp,
    1750             :                                    "BFD session hash");
    1751           8 :         TAILQ_INIT(&bplist);
    1752           8 : }
    1753             : 
    1754           0 : static void _bfd_free(struct hash_bucket *hb,
    1755             :                       void *arg __attribute__((__unused__)))
    1756             : {
    1757           0 :         struct bfd_session *bs = hb->data;
    1758             : 
    1759           0 :         bfd_session_free(bs);
    1760           0 : }
    1761             : 
    1762           8 : void bfd_shutdown(void)
    1763             : {
    1764           8 :         struct bfd_profile *bp;
    1765             : 
    1766             :         /*
    1767             :          * Close and free all BFD sessions.
    1768             :          *
    1769             :          * _bfd_free() will call bfd_session_free() which will take care
    1770             :          * of removing the session from all hashes, so we just run an
    1771             :          * assert() here to make sure it really happened.
    1772             :          */
    1773           8 :         bfd_id_iterate(_bfd_free, NULL);
    1774           8 :         assert(bfd_key_hash->count == 0);
    1775             : 
    1776             :         /* Now free the hashes themselves. */
    1777           8 :         hash_free(bfd_id_hash);
    1778           8 :         hash_free(bfd_key_hash);
    1779             : 
    1780             :         /* Free all profile allocations. */
    1781          10 :         while ((bp = TAILQ_FIRST(&bplist)) != NULL)
    1782           2 :                 bfd_profile_free(bp);
    1783           8 : }
    1784             : 
    1785             : struct bfd_session_iterator {
    1786             :         int bsi_stop;
    1787             :         bool bsi_mhop;
    1788             :         const struct bfd_session *bsi_bs;
    1789             : };
    1790             : 
    1791           0 : static int _bfd_session_next(struct hash_bucket *hb, void *arg)
    1792             : {
    1793           0 :         struct bfd_session_iterator *bsi = arg;
    1794           0 :         struct bfd_session *bs = hb->data;
    1795             : 
    1796             :         /* Previous entry signaled stop. */
    1797           0 :         if (bsi->bsi_stop == 1) {
    1798             :                 /* Match the single/multi hop sessions. */
    1799           0 :                 if (bs->key.mhop != bsi->bsi_mhop)
    1800             :                         return HASHWALK_CONTINUE;
    1801             : 
    1802           0 :                 bsi->bsi_bs = bs;
    1803           0 :                 return HASHWALK_ABORT;
    1804             :         }
    1805             : 
    1806             :         /* We found the current item, stop in the next one. */
    1807           0 :         if (bsi->bsi_bs == hb->data) {
    1808           0 :                 bsi->bsi_stop = 1;
    1809             :                 /* Set entry to NULL to signal end of list. */
    1810           0 :                 bsi->bsi_bs = NULL;
    1811           0 :         } else if (bsi->bsi_bs == NULL && bsi->bsi_mhop == bs->key.mhop) {
    1812             :                 /* We want the first list item. */
    1813           0 :                 bsi->bsi_stop = 1;
    1814           0 :                 bsi->bsi_bs = hb->data;
    1815           0 :                 return HASHWALK_ABORT;
    1816             :         }
    1817             : 
    1818             :         return HASHWALK_CONTINUE;
    1819             : }
    1820             : 
    1821             : /*
    1822             :  * bfd_session_next: uses the current session to find the next.
    1823             :  *
    1824             :  * `bs` might point to NULL to get the first item of the data structure.
    1825             :  */
    1826           0 : const struct bfd_session *bfd_session_next(const struct bfd_session *bs,
    1827             :                                            bool mhop)
    1828             : {
    1829           0 :         struct bfd_session_iterator bsi;
    1830             : 
    1831           0 :         bsi.bsi_stop = 0;
    1832           0 :         bsi.bsi_bs = bs;
    1833           0 :         bsi.bsi_mhop = mhop;
    1834           0 :         hash_walk(bfd_key_hash, _bfd_session_next, &bsi);
    1835           0 :         if (bsi.bsi_stop == 0)
    1836             :                 return NULL;
    1837             : 
    1838           0 :         return bsi.bsi_bs;
    1839             : }
    1840             : 
    1841           0 : static void _bfd_session_remove_manual(struct hash_bucket *hb,
    1842             :                                        void *arg __attribute__((__unused__)))
    1843             : {
    1844           0 :         struct bfd_session *bs = hb->data;
    1845             : 
    1846             :         /* Delete only manually configured sessions. */
    1847           0 :         if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG) == 0)
    1848             :                 return;
    1849             : 
    1850           0 :         bs->refcount--;
    1851           0 :         UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CONFIG);
    1852             : 
    1853             :         /* Don't delete sessions still in use. */
    1854           0 :         if (bs->refcount != 0)
    1855             :                 return;
    1856             : 
    1857           0 :         bfd_session_free(bs);
    1858             : }
    1859             : 
    1860             : /*
    1861             :  * bfd_sessions_remove_manual: remove all manually configured sessions.
    1862             :  *
    1863             :  * NOTE: this function doesn't remove automatically created sessions.
    1864             :  */
    1865           0 : void bfd_sessions_remove_manual(void)
    1866             : {
    1867           0 :         hash_iterate(bfd_key_hash, _bfd_session_remove_manual, NULL);
    1868           0 : }
    1869             : 
    1870           0 : void bfd_profiles_remove(void)
    1871             : {
    1872           0 :         struct bfd_profile *bp;
    1873             : 
    1874           0 :         while ((bp = TAILQ_FIRST(&bplist)) != NULL)
    1875           0 :                 bfd_profile_free(bp);
    1876           0 : }
    1877             : 
    1878             : /*
    1879             :  * Profile related hash functions.
    1880             :  */
    1881           0 : static void _bfd_profile_update(struct hash_bucket *hb, void *arg)
    1882             : {
    1883           0 :         struct bfd_profile *bp = arg;
    1884           0 :         struct bfd_session *bs = hb->data;
    1885             : 
    1886             :         /* This session is not using the profile. */
    1887           0 :         if (bs->profile_name == NULL || strcmp(bs->profile_name, bp->name) != 0)
    1888             :                 return;
    1889             : 
    1890           0 :         bfd_profile_apply(bp->name, bs);
    1891             : }
    1892             : 
    1893           4 : void bfd_profile_update(struct bfd_profile *bp)
    1894             : {
    1895           4 :         hash_iterate(bfd_key_hash, _bfd_profile_update, bp);
    1896           4 : }
    1897             : 
    1898           0 : static void _bfd_profile_detach(struct hash_bucket *hb, void *arg)
    1899             : {
    1900           0 :         struct bfd_profile *bp = arg;
    1901           0 :         struct bfd_session *bs = hb->data;
    1902             : 
    1903             :         /* This session is not using the profile. */
    1904           0 :         if (bs->profile_name == NULL || strcmp(bs->profile_name, bp->name) != 0)
    1905             :                 return;
    1906             : 
    1907           0 :         bfd_profile_remove(bs);
    1908             : }
    1909             : 
    1910           0 : static void bfd_profile_detach(struct bfd_profile *bp)
    1911             : {
    1912           0 :         hash_iterate(bfd_key_hash, _bfd_profile_detach, bp);
    1913           0 : }
    1914             : 
    1915             : /*
    1916             :  * VRF related functions.
    1917             :  */
    1918           8 : static int bfd_vrf_new(struct vrf *vrf)
    1919             : {
    1920           8 :         if (bglobal.debug_zebra)
    1921           0 :                 zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id);
    1922             : 
    1923           8 :         return 0;
    1924             : }
    1925             : 
    1926           8 : static int bfd_vrf_delete(struct vrf *vrf)
    1927             : {
    1928           8 :         if (bglobal.debug_zebra)
    1929           0 :                 zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id);
    1930             : 
    1931           8 :         return 0;
    1932             : }
    1933             : 
    1934           8 : static int bfd_vrf_enable(struct vrf *vrf)
    1935             : {
    1936           8 :         struct bfd_vrf_global *bvrf;
    1937             : 
    1938             :         /* a different name */
    1939           8 :         if (!vrf->info) {
    1940           8 :                 bvrf = XCALLOC(MTYPE_BFDD_VRF, sizeof(struct bfd_vrf_global));
    1941           8 :                 bvrf->vrf = vrf;
    1942           8 :                 vrf->info = (void *)bvrf;
    1943             : 
    1944             :                 /* Disable sockets if using data plane. */
    1945           8 :                 if (bglobal.bg_use_dplane) {
    1946           0 :                         bvrf->bg_shop = -1;
    1947           0 :                         bvrf->bg_mhop = -1;
    1948           0 :                         bvrf->bg_shop6 = -1;
    1949           0 :                         bvrf->bg_mhop6 = -1;
    1950           0 :                         bvrf->bg_echo = -1;
    1951           0 :                         bvrf->bg_echov6 = -1;
    1952             :                 }
    1953             :         } else
    1954             :                 bvrf = vrf->info;
    1955             : 
    1956           8 :         if (bglobal.debug_zebra)
    1957           0 :                 zlog_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id);
    1958             : 
    1959           8 :         if (!bvrf->bg_shop)
    1960           8 :                 bvrf->bg_shop = bp_udp_shop(vrf);
    1961           8 :         if (!bvrf->bg_mhop)
    1962           8 :                 bvrf->bg_mhop = bp_udp_mhop(vrf);
    1963           8 :         if (!bvrf->bg_shop6)
    1964           8 :                 bvrf->bg_shop6 = bp_udp6_shop(vrf);
    1965           8 :         if (!bvrf->bg_mhop6)
    1966           8 :                 bvrf->bg_mhop6 = bp_udp6_mhop(vrf);
    1967           8 :         if (!bvrf->bg_echo)
    1968           8 :                 bvrf->bg_echo = bp_echo_socket(vrf);
    1969           8 :         if (!bvrf->bg_echov6)
    1970           8 :                 bvrf->bg_echov6 = bp_echov6_socket(vrf);
    1971             : 
    1972           8 :         if (!bvrf->bg_ev[0] && bvrf->bg_shop != -1)
    1973           8 :                 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop,
    1974             :                                 &bvrf->bg_ev[0]);
    1975           8 :         if (!bvrf->bg_ev[1] && bvrf->bg_mhop != -1)
    1976           8 :                 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop,
    1977             :                                 &bvrf->bg_ev[1]);
    1978           8 :         if (!bvrf->bg_ev[2] && bvrf->bg_shop6 != -1)
    1979           8 :                 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6,
    1980             :                                 &bvrf->bg_ev[2]);
    1981           8 :         if (!bvrf->bg_ev[3] && bvrf->bg_mhop6 != -1)
    1982           8 :                 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6,
    1983             :                                 &bvrf->bg_ev[3]);
    1984           8 :         if (!bvrf->bg_ev[4] && bvrf->bg_echo != -1)
    1985           8 :                 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo,
    1986             :                                 &bvrf->bg_ev[4]);
    1987           8 :         if (!bvrf->bg_ev[5] && bvrf->bg_echov6 != -1)
    1988           8 :                 thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6,
    1989             :                                 &bvrf->bg_ev[5]);
    1990             : 
    1991           8 :         if (vrf->vrf_id != VRF_DEFAULT) {
    1992           0 :                 bfdd_zclient_register(vrf->vrf_id);
    1993           0 :                 bfdd_sessions_enable_vrf(vrf);
    1994             :         }
    1995           8 :         return 0;
    1996             : }
    1997             : 
    1998           8 : static int bfd_vrf_disable(struct vrf *vrf)
    1999             : {
    2000           8 :         struct bfd_vrf_global *bvrf;
    2001             : 
    2002           8 :         if (!vrf->info)
    2003             :                 return 0;
    2004           8 :         bvrf = vrf->info;
    2005             : 
    2006           8 :         if (vrf->vrf_id != VRF_DEFAULT) {
    2007           0 :                 bfdd_sessions_disable_vrf(vrf);
    2008           0 :                 bfdd_zclient_unregister(vrf->vrf_id);
    2009             :         }
    2010             : 
    2011           8 :         if (bglobal.debug_zebra)
    2012           0 :                 zlog_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id);
    2013             : 
    2014             :         /* Disable read/write poll triggering. */
    2015           8 :         THREAD_OFF(bvrf->bg_ev[0]);
    2016           8 :         THREAD_OFF(bvrf->bg_ev[1]);
    2017           8 :         THREAD_OFF(bvrf->bg_ev[2]);
    2018           8 :         THREAD_OFF(bvrf->bg_ev[3]);
    2019           8 :         THREAD_OFF(bvrf->bg_ev[4]);
    2020           8 :         THREAD_OFF(bvrf->bg_ev[5]);
    2021             : 
    2022             :         /* Close all descriptors. */
    2023           8 :         socket_close(&bvrf->bg_echo);
    2024           8 :         socket_close(&bvrf->bg_shop);
    2025           8 :         socket_close(&bvrf->bg_mhop);
    2026           8 :         if (bvrf->bg_shop6 != -1)
    2027           8 :                 socket_close(&bvrf->bg_shop6);
    2028           8 :         if (bvrf->bg_mhop6 != -1)
    2029           8 :                 socket_close(&bvrf->bg_mhop6);
    2030           8 :         socket_close(&bvrf->bg_echo);
    2031           8 :         if (bvrf->bg_echov6 != -1)
    2032           8 :                 socket_close(&bvrf->bg_echov6);
    2033             : 
    2034             :         /* free context */
    2035           8 :         XFREE(MTYPE_BFDD_VRF, bvrf);
    2036           8 :         vrf->info = NULL;
    2037             : 
    2038           8 :         return 0;
    2039             : }
    2040             : 
    2041           8 : void bfd_vrf_init(void)
    2042             : {
    2043           8 :         vrf_init(bfd_vrf_new, bfd_vrf_enable, bfd_vrf_disable, bfd_vrf_delete);
    2044           8 : }
    2045             : 
    2046           8 : void bfd_vrf_terminate(void)
    2047             : {
    2048           8 :         vrf_terminate();
    2049           8 : }
    2050             : 
    2051           0 : struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd)
    2052             : {
    2053           0 :         struct vrf *vrf;
    2054             : 
    2055           0 :         if (!vrf_is_backend_netns()) {
    2056           0 :                 vrf = vrf_lookup_by_id(VRF_DEFAULT);
    2057           0 :                 if (vrf)
    2058           0 :                         return (struct bfd_vrf_global *)vrf->info;
    2059             :                 return NULL;
    2060             :         }
    2061           0 :         if (!bfd)
    2062             :                 return NULL;
    2063           0 :         if (!bfd->vrf)
    2064             :                 return NULL;
    2065           0 :         return bfd->vrf->info;
    2066             : }
    2067             : 
    2068           0 : unsigned long bfd_get_session_count(void)
    2069             : {
    2070           0 :         return bfd_key_hash->count;
    2071             : }
    2072             : 
    2073          35 : void bfd_rtt_init(struct bfd_session *bfd)
    2074             : {
    2075          35 :         uint8_t i;
    2076             : 
    2077             :         /* initialize RTT */
    2078          35 :         bfd->rtt_valid = 0;
    2079          35 :         bfd->rtt_index = 0;
    2080         315 :         for (i = 0; i < BFD_RTT_SAMPLE; i++)
    2081         280 :                 bfd->rtt[i] = 0;
    2082           0 : }

Generated by: LCOV version v1.16-topotato