back to topotato report
topotato coverage report
Current view: top level - pimd - pim_msdp.c (source / functions) Hit Total Coverage
Test: test_pim_bfd.py::PIMBFDTest Lines: 39 646 6.0 %
Date: 2023-02-24 18:39:40 Functions: 3 64 4.7 %

          Line data    Source code
       1             : /*
       2             :  * IP MSDP for Quagga
       3             :  * Copyright (C) 2016 Cumulus Networks, Inc.
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or modify
       6             :  * it under the terms of the GNU General Public License as published by
       7             :  * the Free Software Foundation; either version 2 of the License, or
       8             :  * (at your option) any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful, but
      11             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13             :  * General Public License for 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             : 
      20             : #include <zebra.h>
      21             : 
      22             : #include <lib/hash.h>
      23             : #include <lib/jhash.h>
      24             : #include <lib/log.h>
      25             : #include <lib/prefix.h>
      26             : #include <lib/sockunion.h>
      27             : #include <lib/stream.h>
      28             : #include <lib/thread.h>
      29             : #include <lib/vty.h>
      30             : #include <lib/plist.h>
      31             : #include <lib/lib_errors.h>
      32             : 
      33             : #include "pimd.h"
      34             : #include "pim_memory.h"
      35             : #include "pim_instance.h"
      36             : #include "pim_iface.h"
      37             : #include "pim_rp.h"
      38             : #include "pim_str.h"
      39             : #include "pim_time.h"
      40             : #include "pim_upstream.h"
      41             : #include "pim_oil.h"
      42             : 
      43             : #include "pim_msdp.h"
      44             : #include "pim_msdp_packet.h"
      45             : #include "pim_msdp_socket.h"
      46             : 
      47             : // struct pim_msdp pim_msdp, *msdp = &pim_msdp;
      48             : 
      49             : static void pim_msdp_peer_listen(struct pim_msdp_peer *mp);
      50             : static void pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start);
      51             : static void pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start);
      52             : static void pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp,
      53             :                                            bool start);
      54             : static void pim_msdp_peer_free(struct pim_msdp_peer *mp);
      55             : static void pim_msdp_enable(struct pim_instance *pim);
      56             : static void pim_msdp_sa_adv_timer_setup(struct pim_instance *pim, bool start);
      57             : static void pim_msdp_sa_deref(struct pim_msdp_sa *sa,
      58             :                               enum pim_msdp_sa_flags flags);
      59             : static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2);
      60             : static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr);
      61             : 
      62             : /************************ SA cache management ******************************/
      63           0 : static void pim_msdp_sa_timer_expiry_log(struct pim_msdp_sa *sa,
      64             :                                          const char *timer_str)
      65             : {
      66           0 :         zlog_debug("MSDP SA %s %s timer expired", sa->sg_str, timer_str);
      67           0 : }
      68             : 
      69             : /* RFC-3618:Sec-5.1 - global active source advertisement timer */
      70           0 : static void pim_msdp_sa_adv_timer_cb(struct thread *t)
      71             : {
      72           0 :         struct pim_instance *pim = THREAD_ARG(t);
      73             : 
      74           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
      75           0 :                 zlog_debug("MSDP SA advertisement timer expired");
      76             :         }
      77             : 
      78           0 :         pim_msdp_sa_adv_timer_setup(pim, true /* start */);
      79           0 :         pim_msdp_pkt_sa_tx(pim);
      80           0 : }
      81             : 
      82           4 : static void pim_msdp_sa_adv_timer_setup(struct pim_instance *pim, bool start)
      83             : {
      84           4 :         THREAD_OFF(pim->msdp.sa_adv_timer);
      85           4 :         if (start) {
      86           0 :                 thread_add_timer(pim->msdp.master, pim_msdp_sa_adv_timer_cb,
      87             :                                  pim, PIM_MSDP_SA_ADVERTISMENT_TIME,
      88             :                                  &pim->msdp.sa_adv_timer);
      89             :         }
      90           4 : }
      91             : 
      92             : /* RFC-3618:Sec-5.3 - SA cache state timer */
      93           0 : static void pim_msdp_sa_state_timer_cb(struct thread *t)
      94             : {
      95           0 :         struct pim_msdp_sa *sa;
      96             : 
      97           0 :         sa = THREAD_ARG(t);
      98             : 
      99           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     100           0 :                 pim_msdp_sa_timer_expiry_log(sa, "state");
     101             :         }
     102             : 
     103           0 :         pim_msdp_sa_deref(sa, PIM_MSDP_SAF_PEER);
     104           0 : }
     105             : 
     106           0 : static void pim_msdp_sa_state_timer_setup(struct pim_msdp_sa *sa, bool start)
     107             : {
     108           0 :         THREAD_OFF(sa->sa_state_timer);
     109           0 :         if (start) {
     110           0 :                 thread_add_timer(sa->pim->msdp.master,
     111             :                                  pim_msdp_sa_state_timer_cb, sa,
     112             :                                  PIM_MSDP_SA_HOLD_TIME, &sa->sa_state_timer);
     113             :         }
     114           0 : }
     115             : 
     116           0 : static void pim_msdp_sa_upstream_del(struct pim_msdp_sa *sa)
     117             : {
     118           0 :         struct pim_upstream *up = sa->up;
     119           0 :         if (!up) {
     120             :                 return;
     121             :         }
     122             : 
     123           0 :         sa->up = NULL;
     124           0 :         if (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags)) {
     125           0 :                 PIM_UPSTREAM_FLAG_UNSET_SRC_MSDP(up->flags);
     126           0 :                 sa->flags |= PIM_MSDP_SAF_UP_DEL_IN_PROG;
     127           0 :                 up = pim_upstream_del(sa->pim, up, __func__);
     128             :                 /* re-eval joinDesired; clearing peer-msdp-sa flag can
     129             :                  * cause JD to change
     130             :                  */
     131           0 :                 if (up)
     132           0 :                         pim_upstream_update_join_desired(sa->pim, up);
     133           0 :                 sa->flags &= ~PIM_MSDP_SAF_UP_DEL_IN_PROG;
     134             :         }
     135             : 
     136           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     137           0 :                 zlog_debug("MSDP SA %s de-referenced SPT", sa->sg_str);
     138             :         }
     139             : }
     140             : 
     141           0 : static bool pim_msdp_sa_upstream_add_ok(struct pim_msdp_sa *sa,
     142             :                                         struct pim_upstream *xg_up)
     143             : {
     144           0 :         if (!(sa->flags & PIM_MSDP_SAF_PEER)) {
     145             :                 /* SA should have been rxed from a peer */
     146             :                 return false;
     147             :         }
     148             :         /* check if we are RP */
     149           0 :         if (!I_am_RP(sa->pim, sa->sg.grp)) {
     150             :                 return false;
     151             :         }
     152             : 
     153             :         /* check if we have a (*, G) with a non-empty immediate OIL */
     154           0 :         if (!xg_up) {
     155           0 :                 pim_sgaddr sg;
     156             : 
     157           0 :                 memset(&sg, 0, sizeof(sg));
     158           0 :                 sg.grp = sa->sg.grp;
     159             : 
     160           0 :                 xg_up = pim_upstream_find(sa->pim, &sg);
     161             :         }
     162           0 :         if (!xg_up || (xg_up->join_state != PIM_UPSTREAM_JOINED)) {
     163             :                 /* join desired will be true for such (*, G) entries so we will
     164             :                  * just look at join_state and let the PIM state machine do the
     165             :                  * rest of
     166             :                  * the magic */
     167           0 :                 return false;
     168             :         }
     169             : 
     170             :         return true;
     171             : }
     172             : 
     173             : /* Upstream add evaluation needs to happen everytime -
     174             :  * 1. Peer reference is added or removed.
     175             :  * 2. The RP for a group changes.
     176             :  * 3. joinDesired for the associated (*, G) changes
     177             :  * 4. associated (*, G) is removed - this seems like a bit redundant
     178             :  *    (considering #4); but just in case an entry gets nuked without
     179             :  *    upstream state transition
     180             :  *    */
     181           0 : static void pim_msdp_sa_upstream_update(struct pim_msdp_sa *sa,
     182             :                                         struct pim_upstream *xg_up,
     183             :                                         const char *ctx)
     184             : {
     185           0 :         struct pim_upstream *up;
     186             : 
     187           0 :         if (!pim_msdp_sa_upstream_add_ok(sa, xg_up)) {
     188           0 :                 pim_msdp_sa_upstream_del(sa);
     189           0 :                 return;
     190             :         }
     191             : 
     192           0 :         if (sa->up) {
     193             :                 /* nothing to do */
     194             :                 return;
     195             :         }
     196             : 
     197           0 :         up = pim_upstream_find(sa->pim, &sa->sg);
     198           0 :         if (up && (PIM_UPSTREAM_FLAG_TEST_SRC_MSDP(up->flags))) {
     199             :                 /* somehow we lost track of the upstream ptr? best log it */
     200           0 :                 sa->up = up;
     201           0 :                 if (PIM_DEBUG_MSDP_EVENTS) {
     202           0 :                         zlog_debug("MSDP SA %s SPT reference missing",
     203             :                                    sa->sg_str);
     204             :                 }
     205           0 :                 return;
     206             :         }
     207             : 
     208             :         /* RFC3618: "RP triggers a (S, G) join event towards the data source
     209             :          * as if a JP message was rxed addressed to the RP itself." */
     210           0 :         up = pim_upstream_add(sa->pim, &sa->sg, NULL /* iif */,
     211             :                               PIM_UPSTREAM_FLAG_MASK_SRC_MSDP, __func__, NULL);
     212             : 
     213           0 :         sa->up = up;
     214           0 :         if (up) {
     215             :                 /* update inherited oil */
     216           0 :                 pim_upstream_inherited_olist(sa->pim, up);
     217             :                 /* should we also start the kat in parallel? we will need it
     218             :                  * when the
     219             :                  * SA ages out */
     220           0 :                 if (PIM_DEBUG_MSDP_EVENTS) {
     221           0 :                         zlog_debug("MSDP SA %s referenced SPT", sa->sg_str);
     222             :                 }
     223             :         } else {
     224           0 :                 if (PIM_DEBUG_MSDP_EVENTS) {
     225           0 :                         zlog_debug("MSDP SA %s SPT reference failed",
     226             :                                    sa->sg_str);
     227             :                 }
     228             :         }
     229             : }
     230             : 
     231             : /* release all mem associated with a sa */
     232           0 : static void pim_msdp_sa_free(struct pim_msdp_sa *sa)
     233             : {
     234           0 :         pim_msdp_sa_state_timer_setup(sa, false);
     235             : 
     236           0 :         XFREE(MTYPE_PIM_MSDP_SA, sa);
     237           0 : }
     238             : 
     239           0 : static struct pim_msdp_sa *pim_msdp_sa_new(struct pim_instance *pim,
     240             :                                            pim_sgaddr *sg, struct in_addr rp)
     241             : {
     242           0 :         struct pim_msdp_sa *sa;
     243             : 
     244           0 :         sa = XCALLOC(MTYPE_PIM_MSDP_SA, sizeof(*sa));
     245             : 
     246           0 :         sa->pim = pim;
     247           0 :         sa->sg = *sg;
     248           0 :         snprintfrr(sa->sg_str, sizeof(sa->sg_str), "%pSG", sg);
     249           0 :         sa->rp = rp;
     250           0 :         sa->uptime = pim_time_monotonic_sec();
     251             : 
     252             :         /* insert into misc tables for easy access */
     253           0 :         sa = hash_get(pim->msdp.sa_hash, sa, hash_alloc_intern);
     254           0 :         listnode_add_sort(pim->msdp.sa_list, sa);
     255             : 
     256           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     257           0 :                 zlog_debug("MSDP SA %s created", sa->sg_str);
     258             :         }
     259             : 
     260           0 :         return sa;
     261             : }
     262             : 
     263           0 : static struct pim_msdp_sa *pim_msdp_sa_find(struct pim_instance *pim,
     264             :                                             pim_sgaddr *sg)
     265             : {
     266           0 :         struct pim_msdp_sa lookup;
     267             : 
     268           0 :         lookup.sg = *sg;
     269           0 :         return hash_lookup(pim->msdp.sa_hash, &lookup);
     270             : }
     271             : 
     272           0 : static struct pim_msdp_sa *pim_msdp_sa_add(struct pim_instance *pim,
     273             :                                            pim_sgaddr *sg, struct in_addr rp)
     274             : {
     275           0 :         struct pim_msdp_sa *sa;
     276             : 
     277           0 :         sa = pim_msdp_sa_find(pim, sg);
     278           0 :         if (sa) {
     279             :                 return sa;
     280             :         }
     281             : 
     282           0 :         return pim_msdp_sa_new(pim, sg, rp);
     283             : }
     284             : 
     285           0 : static void pim_msdp_sa_del(struct pim_msdp_sa *sa)
     286             : {
     287             :         /* this is somewhat redundant - still want to be careful not to leave
     288             :          * stale upstream references */
     289           0 :         pim_msdp_sa_upstream_del(sa);
     290             : 
     291             :         /* stop timers */
     292           0 :         pim_msdp_sa_state_timer_setup(sa, false /* start */);
     293             : 
     294             :         /* remove the entry from various tables */
     295           0 :         listnode_delete(sa->pim->msdp.sa_list, sa);
     296           0 :         hash_release(sa->pim->msdp.sa_hash, sa);
     297             : 
     298           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     299           0 :                 zlog_debug("MSDP SA %s deleted", sa->sg_str);
     300             :         }
     301             : 
     302             :         /* free up any associated memory */
     303           0 :         pim_msdp_sa_free(sa);
     304           0 : }
     305             : 
     306           0 : static void pim_msdp_sa_peer_ip_set(struct pim_msdp_sa *sa,
     307             :                                     struct pim_msdp_peer *mp, struct in_addr rp)
     308             : {
     309           0 :         struct pim_msdp_peer *old_mp;
     310             : 
     311             :         /* optimize the "no change" case as it will happen
     312             :          * frequently/periodically */
     313           0 :         if (mp && (sa->peer.s_addr == mp->peer.s_addr)) {
     314             :                 return;
     315             :         }
     316             : 
     317             :         /* any time the peer ip changes also update the rp address */
     318           0 :         if (sa->peer.s_addr != INADDR_ANY) {
     319           0 :                 old_mp = pim_msdp_peer_find(sa->pim, sa->peer);
     320           0 :                 if (old_mp && old_mp->sa_cnt) {
     321           0 :                         --old_mp->sa_cnt;
     322             :                 }
     323             :         }
     324             : 
     325           0 :         if (mp) {
     326           0 :                 ++mp->sa_cnt;
     327           0 :                 sa->peer = mp->peer;
     328             :         } else {
     329           0 :                 sa->peer.s_addr = PIM_NET_INADDR_ANY;
     330             :         }
     331           0 :         sa->rp = rp;
     332             : }
     333             : 
     334             : /* When a local active-source is removed there is no way to withdraw the
     335             :  * source from peers. We will simply remove it from the SA cache so it will
     336             :  * not be sent in supsequent SA updates. Peers will consequently timeout the
     337             :  * SA.
     338             :  * Similarly a "peer-added" SA is never explicitly deleted. It is simply
     339             :  * aged out overtime if not seen in the SA updates from the peers.
     340             :  * XXX: should we provide a knob to drop entries learnt from a peer when the
     341             :  * peer goes down? */
     342           0 : static void pim_msdp_sa_deref(struct pim_msdp_sa *sa,
     343             :                               enum pim_msdp_sa_flags flags)
     344             : {
     345           0 :         bool update_up = false;
     346             : 
     347           0 :         if ((sa->flags & PIM_MSDP_SAF_LOCAL)) {
     348           0 :                 if (flags & PIM_MSDP_SAF_LOCAL) {
     349           0 :                         if (PIM_DEBUG_MSDP_EVENTS) {
     350           0 :                                 zlog_debug("MSDP SA %s local reference removed",
     351             :                                            sa->sg_str);
     352             :                         }
     353           0 :                         if (sa->pim->msdp.local_cnt)
     354           0 :                                 --sa->pim->msdp.local_cnt;
     355             :                 }
     356             :         }
     357             : 
     358           0 :         if ((sa->flags & PIM_MSDP_SAF_PEER)) {
     359           0 :                 if (flags & PIM_MSDP_SAF_PEER) {
     360           0 :                         struct in_addr rp;
     361             : 
     362           0 :                         if (PIM_DEBUG_MSDP_EVENTS) {
     363           0 :                                 zlog_debug("MSDP SA %s peer reference removed",
     364             :                                            sa->sg_str);
     365             :                         }
     366           0 :                         pim_msdp_sa_state_timer_setup(sa, false /* start */);
     367           0 :                         rp.s_addr = INADDR_ANY;
     368           0 :                         pim_msdp_sa_peer_ip_set(sa, NULL /* mp */, rp);
     369             :                         /* if peer ref was removed we need to remove the msdp
     370             :                          * reference on the
     371             :                          * msdp entry */
     372           0 :                         update_up = true;
     373             :                 }
     374             :         }
     375             : 
     376           0 :         sa->flags &= ~flags;
     377           0 :         if (update_up) {
     378           0 :                 pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "sa-deref");
     379             :         }
     380             : 
     381           0 :         if (!(sa->flags & PIM_MSDP_SAF_REF)) {
     382           0 :                 pim_msdp_sa_del(sa);
     383             :         }
     384           0 : }
     385             : 
     386           0 : void pim_msdp_sa_ref(struct pim_instance *pim, struct pim_msdp_peer *mp,
     387             :                      pim_sgaddr *sg, struct in_addr rp)
     388             : {
     389           0 :         struct pim_msdp_sa *sa;
     390             : 
     391           0 :         sa = pim_msdp_sa_add(pim, sg, rp);
     392           0 :         if (!sa) {
     393             :                 return;
     394             :         }
     395             : 
     396             :         /* reference it */
     397           0 :         if (mp) {
     398           0 :                 if (!(sa->flags & PIM_MSDP_SAF_PEER)) {
     399           0 :                         sa->flags |= PIM_MSDP_SAF_PEER;
     400           0 :                         if (PIM_DEBUG_MSDP_EVENTS) {
     401           0 :                                 zlog_debug("MSDP SA %s added by peer",
     402             :                                            sa->sg_str);
     403             :                         }
     404             :                 }
     405           0 :                 pim_msdp_sa_peer_ip_set(sa, mp, rp);
     406             :                 /* start/re-start the state timer to prevent cache expiry */
     407           0 :                 pim_msdp_sa_state_timer_setup(sa, true /* start */);
     408             :                 /* We re-evaluate SA "SPT-trigger" everytime we hear abt it from
     409             :                  * a
     410             :                  * peer. XXX: If this becomes too much of a periodic overhead we
     411             :                  * can make it event based */
     412           0 :                 pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "peer-ref");
     413             :         } else {
     414           0 :                 if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
     415           0 :                         sa->flags |= PIM_MSDP_SAF_LOCAL;
     416           0 :                         ++sa->pim->msdp.local_cnt;
     417           0 :                         if (PIM_DEBUG_MSDP_EVENTS) {
     418           0 :                                 zlog_debug("MSDP SA %s added locally",
     419             :                                            sa->sg_str);
     420             :                         }
     421             :                         /* send an immediate SA update to peers */
     422           0 :                         sa->rp = pim->msdp.originator_id;
     423           0 :                         pim_msdp_pkt_sa_tx_one(sa);
     424             :                 }
     425           0 :                 sa->flags &= ~PIM_MSDP_SAF_STALE;
     426             :         }
     427             : }
     428             : 
     429             : /* The following criteria must be met to originate an SA from the MSDP
     430             :  * speaker -
     431             :  * 1. KAT must be running i.e. source is active.
     432             :  * 2. We must be RP for the group.
     433             :  * 3. Source must be registrable to the RP (this is where the RFC is vague
     434             :  *    and especially ambiguous in CLOS networks; with anycast RP all sources
     435             :  *    are potentially registrable to all RPs in the domain). We assume #3 is
     436             :  *    satisfied if -
     437             :  *    a. We are also the FHR-DR for the source (OR)
     438             :  *    b. We rxed a pim register (null or data encapsulated) within the last
     439             :  *       (3 * (1.5 * register_suppression_timer))).
     440             :  */
     441           0 : static bool pim_msdp_sa_local_add_ok(struct pim_upstream *up)
     442             : {
     443           0 :         struct pim_instance *pim = up->channel_oil->pim;
     444             : 
     445           0 :         if (!(pim->msdp.flags & PIM_MSDPF_ENABLE)) {
     446             :                 return false;
     447             :         }
     448             : 
     449           0 :         if (!pim_upstream_is_kat_running(up))
     450             :                 /* stream is not active */
     451             :                 return false;
     452             : 
     453           0 :         if (!I_am_RP(pim, up->sg.grp)) {
     454             :                 /* we are not RP for the group */
     455             :                 return false;
     456             :         }
     457             : 
     458             :         /* we are the FHR-DR for this stream  or we are RP and have seen
     459             :          * registers
     460             :          * from a FHR for this source */
     461           0 :         if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) || up->t_msdp_reg_timer) {
     462             :                 return true;
     463             :         }
     464             : 
     465             :         return false;
     466             : }
     467             : 
     468           0 : static void pim_msdp_sa_local_add(struct pim_instance *pim, pim_sgaddr *sg)
     469             : {
     470           0 :         struct in_addr rp;
     471           0 :         rp.s_addr = INADDR_ANY;
     472           0 :         pim_msdp_sa_ref(pim, NULL /* mp */, sg, rp);
     473           0 : }
     474             : 
     475           0 : void pim_msdp_sa_local_del(struct pim_instance *pim, pim_sgaddr *sg)
     476             : {
     477           0 :         struct pim_msdp_sa *sa;
     478             : 
     479           0 :         sa = pim_msdp_sa_find(pim, sg);
     480           0 :         if (sa) {
     481           0 :                 pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
     482             :         }
     483           0 : }
     484             : 
     485             : /* we need to be very cautious with this API as SA del too can trigger an
     486             :  * upstream del and we will get stuck in a simple loop */
     487           0 : static void pim_msdp_sa_local_del_on_up_del(struct pim_instance *pim,
     488             :                                             pim_sgaddr *sg)
     489             : {
     490           0 :         struct pim_msdp_sa *sa;
     491             : 
     492           0 :         sa = pim_msdp_sa_find(pim, sg);
     493           0 :         if (sa) {
     494           0 :                 if (PIM_DEBUG_MSDP_INTERNAL) {
     495           0 :                         zlog_debug("MSDP local sa %s del on up del",
     496             :                                    sa->sg_str);
     497             :                 }
     498             : 
     499             :                 /* if there is no local reference escape */
     500           0 :                 if (!(sa->flags & PIM_MSDP_SAF_LOCAL)) {
     501           0 :                         if (PIM_DEBUG_MSDP_INTERNAL) {
     502           0 :                                 zlog_debug("MSDP local sa %s del; no local ref",
     503             :                                            sa->sg_str);
     504             :                         }
     505           0 :                         return;
     506             :                 }
     507             : 
     508           0 :                 if (sa->flags & PIM_MSDP_SAF_UP_DEL_IN_PROG) {
     509             :                         /* MSDP is the one that triggered the upstream del. if
     510             :                          * this happens
     511             :                          * we most certainly have a bug in the PIM upstream
     512             :                          * state machine. We
     513             :                          * will not have a local reference unless the KAT is
     514             :                          * running. And if the
     515             :                          * KAT is running there MUST be an additional
     516             :                          * source-stream reference to
     517             :                          * the flow. Accounting for such cases requires lot of
     518             :                          * changes; perhaps
     519             :                          * address this in the next release? - XXX  */
     520           0 :                         flog_err(
     521             :                                 EC_LIB_DEVELOPMENT,
     522             :                                 "MSDP sa %s SPT teardown is causing the local entry to be removed",
     523             :                                 sa->sg_str);
     524           0 :                         return;
     525             :                 }
     526             : 
     527             :                 /* we are dropping the sa on upstream del we should not have an
     528             :                  * upstream reference */
     529           0 :                 if (sa->up) {
     530           0 :                         if (PIM_DEBUG_MSDP_INTERNAL) {
     531           0 :                                 zlog_debug("MSDP local sa %s del; up non-NULL",
     532             :                                            sa->sg_str);
     533             :                         }
     534           0 :                         sa->up = NULL;
     535             :                 }
     536           0 :                 pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
     537             :         }
     538             : }
     539             : 
     540             : /* Local SA qualification needs to be re-evaluated when -
     541             :  * 1. KAT is started or stopped
     542             :  * 2. on RP changes
     543             :  * 3. Whenever FHR status changes for a (S,G) - XXX - currently there
     544             :  *    is no clear path to transition an entry out of "MASK_FHR" need
     545             :  *    to discuss this with Donald. May result in some strangeness if the
     546             :  *    FHR is also the RP.
     547             :  * 4. When msdp_reg timer is started or stopped
     548             :  */
     549           0 : void pim_msdp_sa_local_update(struct pim_upstream *up)
     550             : {
     551           0 :         struct pim_instance *pim = up->channel_oil->pim;
     552             : 
     553           0 :         if (pim_msdp_sa_local_add_ok(up)) {
     554           0 :                 pim_msdp_sa_local_add(pim, &up->sg);
     555             :         } else {
     556           0 :                 pim_msdp_sa_local_del(pim, &up->sg);
     557             :         }
     558           0 : }
     559             : 
     560           0 : static void pim_msdp_sa_local_setup(struct pim_instance *pim)
     561             : {
     562           0 :         struct pim_upstream *up;
     563             : 
     564           0 :         frr_each (rb_pim_upstream, &pim->upstream_head, up)
     565           0 :                 pim_msdp_sa_local_update(up);
     566           0 : }
     567             : 
     568             : /* whenever the RP changes we need to re-evaluate the "local" SA-cache */
     569             : /* XXX: needs to be tested */
     570           0 : void pim_msdp_i_am_rp_changed(struct pim_instance *pim)
     571             : {
     572           0 :         struct listnode *sanode;
     573           0 :         struct listnode *nextnode;
     574           0 :         struct pim_msdp_sa *sa;
     575             : 
     576           0 :         if (!(pim->msdp.flags & PIM_MSDPF_ENABLE)) {
     577             :                 /* if the feature is not enabled do nothing */
     578             :                 return;
     579             :         }
     580             : 
     581           0 :         if (PIM_DEBUG_MSDP_INTERNAL) {
     582           0 :                 zlog_debug("MSDP i_am_rp changed");
     583             :         }
     584             : 
     585             :         /* mark all local entries as stale */
     586           0 :         for (ALL_LIST_ELEMENTS_RO(pim->msdp.sa_list, sanode, sa)) {
     587           0 :                 if (sa->flags & PIM_MSDP_SAF_LOCAL) {
     588           0 :                         sa->flags |= PIM_MSDP_SAF_STALE;
     589             :                 }
     590             :         }
     591             : 
     592             :         /* re-setup local SA entries */
     593           0 :         pim_msdp_sa_local_setup(pim);
     594             : 
     595           0 :         for (ALL_LIST_ELEMENTS(pim->msdp.sa_list, sanode, nextnode, sa)) {
     596             :                 /* purge stale SA entries */
     597           0 :                 if (sa->flags & PIM_MSDP_SAF_STALE) {
     598             :                         /* clear the stale flag; the entry may be kept even
     599             :                          * after
     600             :                          * "local-deref" */
     601           0 :                         sa->flags &= ~PIM_MSDP_SAF_STALE;
     602             :                         /* sa_deref can end up freeing the sa; so don't access
     603             :                          * contents after */
     604           0 :                         pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
     605             :                 } else {
     606             :                         /* if the souce is still active check if we can
     607             :                          * influence SPT */
     608           0 :                         pim_msdp_sa_upstream_update(sa, NULL /* xg_up */,
     609             :                                                     "rp-change");
     610             :                 }
     611             :         }
     612             : }
     613             : 
     614             : /* We track the join state of (*, G) entries. If G has sources in the SA-cache
     615             :  * we need to setup or teardown SPT when the JoinDesired status changes for
     616             :  * (*, G) */
     617           0 : void pim_msdp_up_join_state_changed(struct pim_instance *pim,
     618             :                                     struct pim_upstream *xg_up)
     619             : {
     620           0 :         struct listnode *sanode;
     621           0 :         struct pim_msdp_sa *sa;
     622             : 
     623           0 :         if (PIM_DEBUG_MSDP_INTERNAL) {
     624           0 :                 zlog_debug("MSDP join state changed for %s", xg_up->sg_str);
     625             :         }
     626             : 
     627             :         /* If this is not really an XG entry just move on */
     628           0 :         if (!pim_addr_is_any(xg_up->sg.src) || pim_addr_is_any(xg_up->sg.grp)) {
     629             :                 return;
     630             :         }
     631             : 
     632             :         /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
     633             :          * walking */
     634           0 :         for (ALL_LIST_ELEMENTS_RO(pim->msdp.sa_list, sanode, sa)) {
     635           0 :                 if (pim_addr_cmp(sa->sg.grp, xg_up->sg.grp)) {
     636           0 :                         continue;
     637             :                 }
     638           0 :                 pim_msdp_sa_upstream_update(sa, xg_up, "up-jp-change");
     639             :         }
     640             : }
     641             : 
     642           0 : static void pim_msdp_up_xg_del(struct pim_instance *pim, pim_sgaddr *sg)
     643             : {
     644           0 :         struct listnode *sanode;
     645           0 :         struct pim_msdp_sa *sa;
     646             : 
     647           0 :         if (PIM_DEBUG_MSDP_INTERNAL) {
     648           0 :                 zlog_debug("MSDP %pSG del", sg);
     649             :         }
     650             : 
     651             :         /* If this is not really an XG entry just move on */
     652           0 :         if (!pim_addr_is_any(sg->src) || pim_addr_is_any(sg->grp)) {
     653             :                 return;
     654             :         }
     655             : 
     656             :         /* XXX: Need to maintain SAs per-group to avoid all this unnecessary
     657             :          * walking */
     658           0 :         for (ALL_LIST_ELEMENTS_RO(pim->msdp.sa_list, sanode, sa)) {
     659           0 :                 if (pim_addr_cmp(sa->sg.grp, sg->grp)) {
     660           0 :                         continue;
     661             :                 }
     662           0 :                 pim_msdp_sa_upstream_update(sa, NULL /* xg */, "up-jp-change");
     663             :         }
     664             : }
     665             : 
     666           0 : void pim_msdp_up_del(struct pim_instance *pim, pim_sgaddr *sg)
     667             : {
     668           0 :         if (PIM_DEBUG_MSDP_INTERNAL) {
     669           0 :                 zlog_debug("MSDP up %pSG del", sg);
     670             :         }
     671           0 :         if (pim_addr_is_any(sg->src)) {
     672           0 :                 pim_msdp_up_xg_del(pim, sg);
     673             :         } else {
     674           0 :                 pim_msdp_sa_local_del_on_up_del(pim, sg);
     675             :         }
     676           0 : }
     677             : 
     678             : /* sa hash and peer list helpers */
     679           0 : static unsigned int pim_msdp_sa_hash_key_make(const void *p)
     680             : {
     681           0 :         const struct pim_msdp_sa *sa = p;
     682             : 
     683           0 :         return pim_sgaddr_hash(sa->sg, 0);
     684             : }
     685             : 
     686           0 : static bool pim_msdp_sa_hash_eq(const void *p1, const void *p2)
     687             : {
     688           0 :         const struct pim_msdp_sa *sa1 = p1;
     689           0 :         const struct pim_msdp_sa *sa2 = p2;
     690             : 
     691           0 :         return !pim_sgaddr_cmp(sa1->sg, sa2->sg);
     692             : }
     693             : 
     694           0 : static int pim_msdp_sa_comp(const void *p1, const void *p2)
     695             : {
     696           0 :         const struct pim_msdp_sa *sa1 = p1;
     697           0 :         const struct pim_msdp_sa *sa2 = p2;
     698             : 
     699           0 :         return pim_sgaddr_cmp(sa1->sg, sa2->sg);
     700             : }
     701             : 
     702             : /* RFC-3618:Sec-10.1.3 - Peer-RPF forwarding */
     703             : /* XXX: this can use a bit of refining and extensions */
     704           0 : bool pim_msdp_peer_rpf_check(struct pim_msdp_peer *mp, struct in_addr rp)
     705             : {
     706           0 :         struct pim_nexthop nexthop = {0};
     707             : 
     708           0 :         if (mp->peer.s_addr == rp.s_addr) {
     709             :                 return true;
     710             :         }
     711             : 
     712             :         /* check if the MSDP peer is the nexthop for the RP */
     713           0 :         if (pim_nexthop_lookup(mp->pim, &nexthop, rp, 0) &&
     714           0 :             nexthop.mrib_nexthop_addr.s_addr == mp->peer.s_addr) {
     715             :                 return true;
     716             :         }
     717             : 
     718             :         return false;
     719             : }
     720             : 
     721             : /************************ Peer session management **************************/
     722           0 : char *pim_msdp_state_dump(enum pim_msdp_peer_state state, char *buf,
     723             :                           int buf_size)
     724             : {
     725           0 :         switch (state) {
     726           0 :         case PIM_MSDP_DISABLED:
     727           0 :                 snprintf(buf, buf_size, "%s", "disabled");
     728           0 :                 break;
     729           0 :         case PIM_MSDP_INACTIVE:
     730           0 :                 snprintf(buf, buf_size, "%s", "inactive");
     731           0 :                 break;
     732           0 :         case PIM_MSDP_LISTEN:
     733           0 :                 snprintf(buf, buf_size, "%s", "listen");
     734           0 :                 break;
     735           0 :         case PIM_MSDP_CONNECTING:
     736           0 :                 snprintf(buf, buf_size, "%s", "connecting");
     737           0 :                 break;
     738           0 :         case PIM_MSDP_ESTABLISHED:
     739           0 :                 snprintf(buf, buf_size, "%s", "established");
     740           0 :                 break;
     741           0 :         default:
     742           0 :                 snprintf(buf, buf_size, "unk-%d", state);
     743             :         }
     744           0 :         return buf;
     745             : }
     746             : 
     747           0 : static void pim_msdp_peer_state_chg_log(struct pim_msdp_peer *mp)
     748             : {
     749           0 :         char state_str[PIM_MSDP_STATE_STRLEN];
     750             : 
     751           0 :         pim_msdp_state_dump(mp->state, state_str, sizeof(state_str));
     752           0 :         zlog_debug("MSDP peer %s state chg to %s", mp->key_str, state_str);
     753           0 : }
     754             : 
     755             : /* MSDP Connection State Machine actions (defined in RFC-3618:Sec-11.2) */
     756             : /* 11.2.A2: active peer - start connect retry timer; when the timer fires
     757             :  * a tcp connection will be made */
     758           0 : static void pim_msdp_peer_connect(struct pim_msdp_peer *mp)
     759             : {
     760           0 :         mp->state = PIM_MSDP_CONNECTING;
     761           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     762           0 :                 pim_msdp_peer_state_chg_log(mp);
     763             :         }
     764             : 
     765           0 :         pim_msdp_peer_cr_timer_setup(mp, true /* start */);
     766           0 : }
     767             : 
     768             : /* 11.2.A3: passive peer - just listen for connections */
     769           0 : static void pim_msdp_peer_listen(struct pim_msdp_peer *mp)
     770             : {
     771           0 :         mp->state = PIM_MSDP_LISTEN;
     772           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     773           0 :                 pim_msdp_peer_state_chg_log(mp);
     774             :         }
     775             : 
     776             :         /* this is interntionally asymmetric i.e. we set up listen-socket when
     777             :         * the
     778             :         * first listening peer is configured; but don't bother tearing it down
     779             :         * when
     780             :         * all the peers go down */
     781           0 :         pim_msdp_sock_listen(mp->pim);
     782           0 : }
     783             : 
     784             : /* 11.2.A4 and 11.2.A5: transition active or passive peer to
     785             :  * established state */
     786           0 : void pim_msdp_peer_established(struct pim_msdp_peer *mp)
     787             : {
     788           0 :         if (mp->state != PIM_MSDP_ESTABLISHED) {
     789           0 :                 ++mp->est_flaps;
     790             :         }
     791             : 
     792           0 :         mp->state = PIM_MSDP_ESTABLISHED;
     793           0 :         mp->uptime = pim_time_monotonic_sec();
     794             : 
     795           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     796           0 :                 pim_msdp_peer_state_chg_log(mp);
     797             :         }
     798             : 
     799             :         /* stop retry timer on active peers */
     800           0 :         pim_msdp_peer_cr_timer_setup(mp, false /* start */);
     801             : 
     802             :         /* send KA; start KA and hold timers */
     803           0 :         pim_msdp_pkt_ka_tx(mp);
     804           0 :         pim_msdp_peer_ka_timer_setup(mp, true /* start */);
     805           0 :         pim_msdp_peer_hold_timer_setup(mp, true /* start */);
     806             : 
     807           0 :         pim_msdp_pkt_sa_tx_to_one_peer(mp);
     808             : 
     809           0 :         PIM_MSDP_PEER_WRITE_ON(mp);
     810           0 :         PIM_MSDP_PEER_READ_ON(mp);
     811           0 : }
     812             : 
     813             : /* 11.2.A6, 11.2.A7 and 11.2.A8: shutdown the peer tcp connection */
     814           0 : void pim_msdp_peer_stop_tcp_conn(struct pim_msdp_peer *mp, bool chg_state)
     815             : {
     816           0 :         if (chg_state) {
     817           0 :                 if (mp->state == PIM_MSDP_ESTABLISHED) {
     818           0 :                         ++mp->est_flaps;
     819             :                 }
     820           0 :                 mp->state = PIM_MSDP_INACTIVE;
     821           0 :                 if (PIM_DEBUG_MSDP_EVENTS) {
     822           0 :                         pim_msdp_peer_state_chg_log(mp);
     823             :                 }
     824             :         }
     825             : 
     826           0 :         if (PIM_DEBUG_MSDP_INTERNAL) {
     827           0 :                 zlog_debug("MSDP peer %s pim_msdp_peer_stop_tcp_conn",
     828             :                            mp->key_str);
     829             :         }
     830             :         /* stop read and write threads */
     831           0 :         PIM_MSDP_PEER_READ_OFF(mp);
     832           0 :         PIM_MSDP_PEER_WRITE_OFF(mp);
     833             : 
     834             :         /* reset buffers */
     835           0 :         mp->packet_size = 0;
     836           0 :         if (mp->ibuf)
     837           0 :                 stream_reset(mp->ibuf);
     838           0 :         if (mp->obuf)
     839           0 :                 stream_fifo_clean(mp->obuf);
     840             : 
     841             :         /* stop all peer timers */
     842           0 :         pim_msdp_peer_ka_timer_setup(mp, false /* start */);
     843           0 :         pim_msdp_peer_cr_timer_setup(mp, false /* start */);
     844           0 :         pim_msdp_peer_hold_timer_setup(mp, false /* start */);
     845             : 
     846             :         /* close connection */
     847           0 :         if (mp->fd >= 0) {
     848           0 :                 close(mp->fd);
     849           0 :                 mp->fd = -1;
     850             :         }
     851           0 : }
     852             : 
     853             : /* RFC-3618:Sec-5.6 - stop the peer tcp connection and startover */
     854           0 : void pim_msdp_peer_reset_tcp_conn(struct pim_msdp_peer *mp, const char *rc_str)
     855             : {
     856           0 :         if (PIM_DEBUG_EVENTS) {
     857           0 :                 zlog_debug("MSDP peer %s tcp reset %s", mp->key_str, rc_str);
     858           0 :                 snprintf(mp->last_reset, sizeof(mp->last_reset), "%s", rc_str);
     859             :         }
     860             : 
     861             :         /* close the connection and transition to listening or connecting */
     862           0 :         pim_msdp_peer_stop_tcp_conn(mp, true /* chg_state */);
     863           0 :         if (PIM_MSDP_PEER_IS_LISTENER(mp)) {
     864           0 :                 pim_msdp_peer_listen(mp);
     865             :         } else {
     866           0 :                 pim_msdp_peer_connect(mp);
     867             :         }
     868           0 : }
     869             : 
     870           0 : static void pim_msdp_peer_timer_expiry_log(struct pim_msdp_peer *mp,
     871             :                                            const char *timer_str)
     872             : {
     873           0 :         zlog_debug("MSDP peer %s %s timer expired", mp->key_str, timer_str);
     874           0 : }
     875             : 
     876             : /* RFC-3618:Sec-5.4 - peer hold timer */
     877           0 : static void pim_msdp_peer_hold_timer_cb(struct thread *t)
     878             : {
     879           0 :         struct pim_msdp_peer *mp;
     880             : 
     881           0 :         mp = THREAD_ARG(t);
     882             : 
     883           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     884           0 :                 pim_msdp_peer_timer_expiry_log(mp, "hold");
     885             :         }
     886             : 
     887           0 :         if (mp->state != PIM_MSDP_ESTABLISHED) {
     888             :                 return;
     889             :         }
     890             : 
     891           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     892           0 :                 pim_msdp_peer_state_chg_log(mp);
     893             :         }
     894           0 :         pim_msdp_peer_reset_tcp_conn(mp, "ht-expired");
     895             : }
     896             : 
     897           0 : static void pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start)
     898             : {
     899           0 :         struct pim_instance *pim = mp->pim;
     900           0 :         THREAD_OFF(mp->hold_timer);
     901           0 :         if (start) {
     902           0 :                 thread_add_timer(pim->msdp.master, pim_msdp_peer_hold_timer_cb,
     903             :                                  mp, pim->msdp.hold_time, &mp->hold_timer);
     904             :         }
     905           0 : }
     906             : 
     907             : 
     908             : /* RFC-3618:Sec-5.5 - peer keepalive timer */
     909           0 : static void pim_msdp_peer_ka_timer_cb(struct thread *t)
     910             : {
     911           0 :         struct pim_msdp_peer *mp;
     912             : 
     913           0 :         mp = THREAD_ARG(t);
     914             : 
     915           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     916           0 :                 pim_msdp_peer_timer_expiry_log(mp, "ka");
     917             :         }
     918             : 
     919           0 :         pim_msdp_pkt_ka_tx(mp);
     920           0 :         pim_msdp_peer_ka_timer_setup(mp, true /* start */);
     921           0 : }
     922             : 
     923           0 : static void pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start)
     924             : {
     925           0 :         THREAD_OFF(mp->ka_timer);
     926           0 :         if (start) {
     927           0 :                 thread_add_timer(mp->pim->msdp.master,
     928             :                                  pim_msdp_peer_ka_timer_cb, mp,
     929             :                                  mp->pim->msdp.keep_alive, &mp->ka_timer);
     930             :         }
     931           0 : }
     932             : 
     933           0 : static void pim_msdp_peer_active_connect(struct pim_msdp_peer *mp)
     934             : {
     935           0 :         int rc;
     936           0 :         ++mp->conn_attempts;
     937           0 :         rc = pim_msdp_sock_connect(mp);
     938             : 
     939           0 :         if (PIM_DEBUG_MSDP_INTERNAL) {
     940           0 :                 zlog_debug("MSDP peer %s pim_msdp_peer_active_connect: %d",
     941             :                            mp->key_str, rc);
     942             :         }
     943             : 
     944           0 :         switch (rc) {
     945           0 :         case connect_error:
     946             :         case -1:
     947             :                 /* connect failed restart the connect-retry timer */
     948           0 :                 pim_msdp_peer_cr_timer_setup(mp, true /* start */);
     949           0 :                 break;
     950             : 
     951           0 :         case connect_success:
     952             :                 /* connect was sucessful move to established */
     953           0 :                 pim_msdp_peer_established(mp);
     954           0 :                 break;
     955             : 
     956           0 :         case connect_in_progress:
     957             :                 /* for NB content we need to wait till sock is readable or
     958             :                  * writeable */
     959           0 :                 PIM_MSDP_PEER_WRITE_ON(mp);
     960           0 :                 PIM_MSDP_PEER_READ_ON(mp);
     961             :                 /* also restart connect-retry timer to reset the socket if
     962             :                  * connect is
     963             :                  * not sucessful */
     964           0 :                 pim_msdp_peer_cr_timer_setup(mp, true /* start */);
     965           0 :                 break;
     966             :         }
     967           0 : }
     968             : 
     969             : /* RFC-3618:Sec-5.6 - connection retry on active peer */
     970           0 : static void pim_msdp_peer_cr_timer_cb(struct thread *t)
     971             : {
     972           0 :         struct pim_msdp_peer *mp;
     973             : 
     974           0 :         mp = THREAD_ARG(t);
     975             : 
     976           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
     977           0 :                 pim_msdp_peer_timer_expiry_log(mp, "connect-retry");
     978             :         }
     979             : 
     980           0 :         if (mp->state != PIM_MSDP_CONNECTING || PIM_MSDP_PEER_IS_LISTENER(mp)) {
     981             :                 return;
     982             :         }
     983             : 
     984           0 :         pim_msdp_peer_active_connect(mp);
     985             : }
     986             : 
     987           0 : static void pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start)
     988             : {
     989           0 :         THREAD_OFF(mp->cr_timer);
     990           0 :         if (start) {
     991           0 :                 thread_add_timer(mp->pim->msdp.master,
     992             :                                  pim_msdp_peer_cr_timer_cb, mp,
     993             :                                  mp->pim->msdp.connection_retry, &mp->cr_timer);
     994             :         }
     995           0 : }
     996             : 
     997             : /* if a valid packet is rxed from the peer we can restart hold timer */
     998           0 : void pim_msdp_peer_pkt_rxed(struct pim_msdp_peer *mp)
     999             : {
    1000           0 :         if (mp->state == PIM_MSDP_ESTABLISHED) {
    1001           0 :                 pim_msdp_peer_hold_timer_setup(mp, true /* start */);
    1002             :         }
    1003           0 : }
    1004             : 
    1005             : /* if a valid packet is txed to the peer we can restart ka timer and avoid
    1006             :  * unnecessary ka noise in the network */
    1007           0 : void pim_msdp_peer_pkt_txed(struct pim_msdp_peer *mp)
    1008             : {
    1009           0 :         if (mp->state == PIM_MSDP_ESTABLISHED) {
    1010           0 :                 pim_msdp_peer_ka_timer_setup(mp, true /* start */);
    1011           0 :                 if (PIM_DEBUG_MSDP_INTERNAL) {
    1012           0 :                         zlog_debug("MSDP ka timer restart on pkt tx to %s",
    1013             :                                    mp->key_str);
    1014             :                 }
    1015             :         }
    1016           0 : }
    1017             : 
    1018           0 : static void pim_msdp_addr2su(union sockunion *su, struct in_addr addr)
    1019             : {
    1020           0 :         sockunion_init(su);
    1021           0 :         su->sin.sin_addr = addr;
    1022           0 :         su->sin.sin_family = AF_INET;
    1023             : #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
    1024             :         su->sin.sin_len = sizeof(struct sockaddr_in);
    1025             : #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
    1026             : }
    1027             : 
    1028             : /* 11.2.A1: create a new peer and transition state to listen or connecting */
    1029           0 : struct pim_msdp_peer *pim_msdp_peer_add(struct pim_instance *pim,
    1030             :                                         const struct in_addr *peer,
    1031             :                                         const struct in_addr *local,
    1032             :                                         const char *mesh_group_name)
    1033             : {
    1034           0 :         struct pim_msdp_peer *mp;
    1035             : 
    1036           0 :         pim_msdp_enable(pim);
    1037             : 
    1038           0 :         mp = XCALLOC(MTYPE_PIM_MSDP_PEER, sizeof(*mp));
    1039             : 
    1040           0 :         mp->pim = pim;
    1041           0 :         mp->peer = *peer;
    1042           0 :         pim_inet4_dump("<peer?>", mp->peer, mp->key_str, sizeof(mp->key_str));
    1043           0 :         pim_msdp_addr2su(&mp->su_peer, mp->peer);
    1044           0 :         mp->local = *local;
    1045             :         /* XXX: originator_id setting needs to move to the mesh group */
    1046           0 :         pim->msdp.originator_id = *local;
    1047           0 :         pim_msdp_addr2su(&mp->su_local, mp->local);
    1048           0 :         if (mesh_group_name)
    1049           0 :                 mp->mesh_group_name =
    1050           0 :                         XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name);
    1051             : 
    1052           0 :         mp->state = PIM_MSDP_INACTIVE;
    1053           0 :         mp->fd = -1;
    1054           0 :         strlcpy(mp->last_reset, "-", sizeof(mp->last_reset));
    1055             :         /* higher IP address is listener */
    1056           0 :         if (ntohl(mp->local.s_addr) > ntohl(mp->peer.s_addr)) {
    1057           0 :                 mp->flags |= PIM_MSDP_PEERF_LISTENER;
    1058             :         }
    1059             : 
    1060             :         /* setup packet buffers */
    1061           0 :         mp->ibuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE);
    1062           0 :         mp->obuf = stream_fifo_new();
    1063             : 
    1064             :         /* insert into misc tables for easy access */
    1065           0 :         mp = hash_get(pim->msdp.peer_hash, mp, hash_alloc_intern);
    1066           0 :         listnode_add_sort(pim->msdp.peer_list, mp);
    1067             : 
    1068           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
    1069           0 :                 zlog_debug("MSDP peer %s created", mp->key_str);
    1070             : 
    1071           0 :                 pim_msdp_peer_state_chg_log(mp);
    1072             :         }
    1073             : 
    1074             :         /* fireup the connect state machine */
    1075           0 :         if (PIM_MSDP_PEER_IS_LISTENER(mp)) {
    1076           0 :                 pim_msdp_peer_listen(mp);
    1077             :         } else {
    1078           0 :                 pim_msdp_peer_connect(mp);
    1079             :         }
    1080           0 :         return mp;
    1081             : }
    1082             : 
    1083           0 : struct pim_msdp_peer *pim_msdp_peer_find(struct pim_instance *pim,
    1084             :                                          struct in_addr peer_addr)
    1085             : {
    1086           0 :         struct pim_msdp_peer lookup;
    1087             : 
    1088           0 :         lookup.peer = peer_addr;
    1089           0 :         return hash_lookup(pim->msdp.peer_hash, &lookup);
    1090             : }
    1091             : 
    1092             : /* release all mem associated with a peer */
    1093           0 : static void pim_msdp_peer_free(struct pim_msdp_peer *mp)
    1094             : {
    1095             :         /*
    1096             :          * Let's make sure we are not running when we delete
    1097             :          * the underlying data structure
    1098             :          */
    1099           0 :         pim_msdp_peer_stop_tcp_conn(mp, false);
    1100             : 
    1101           0 :         if (mp->ibuf) {
    1102           0 :                 stream_free(mp->ibuf);
    1103             :         }
    1104             : 
    1105           0 :         if (mp->obuf) {
    1106           0 :                 stream_fifo_free(mp->obuf);
    1107             :         }
    1108             : 
    1109           0 :         XFREE(MTYPE_PIM_MSDP_MG_NAME, mp->mesh_group_name);
    1110             : 
    1111           0 :         mp->pim = NULL;
    1112           0 :         XFREE(MTYPE_PIM_MSDP_PEER, mp);
    1113           0 : }
    1114             : 
    1115             : /* delete the peer config */
    1116           0 : void pim_msdp_peer_del(struct pim_msdp_peer **mp)
    1117             : {
    1118           0 :         if (*mp == NULL)
    1119             :                 return;
    1120             : 
    1121             :         /* stop the tcp connection and shutdown all timers */
    1122           0 :         pim_msdp_peer_stop_tcp_conn(*mp, true /* chg_state */);
    1123             : 
    1124             :         /* remove the session from various tables */
    1125           0 :         listnode_delete((*mp)->pim->msdp.peer_list, *mp);
    1126           0 :         hash_release((*mp)->pim->msdp.peer_hash, *mp);
    1127             : 
    1128           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
    1129           0 :                 zlog_debug("MSDP peer %s deleted", (*mp)->key_str);
    1130             :         }
    1131             : 
    1132             :         /* free up any associated memory */
    1133           0 :         pim_msdp_peer_free(*mp);
    1134           0 :         *mp = NULL;
    1135             : }
    1136             : 
    1137           0 : void pim_msdp_peer_change_source(struct pim_msdp_peer *mp,
    1138             :                                  const struct in_addr *addr)
    1139             : {
    1140           0 :         pim_msdp_peer_stop_tcp_conn(mp, true);
    1141             : 
    1142           0 :         mp->local = *addr;
    1143             : 
    1144           0 :         if (PIM_MSDP_PEER_IS_LISTENER(mp))
    1145           0 :                 pim_msdp_peer_listen(mp);
    1146             :         else
    1147           0 :                 pim_msdp_peer_connect(mp);
    1148           0 : }
    1149             : 
    1150             : /* peer hash and peer list helpers */
    1151           0 : static unsigned int pim_msdp_peer_hash_key_make(const void *p)
    1152             : {
    1153           0 :         const struct pim_msdp_peer *mp = p;
    1154           0 :         return (jhash_1word(mp->peer.s_addr, 0));
    1155             : }
    1156             : 
    1157           0 : static bool pim_msdp_peer_hash_eq(const void *p1, const void *p2)
    1158             : {
    1159           0 :         const struct pim_msdp_peer *mp1 = p1;
    1160           0 :         const struct pim_msdp_peer *mp2 = p2;
    1161             : 
    1162           0 :         return (mp1->peer.s_addr == mp2->peer.s_addr);
    1163             : }
    1164             : 
    1165           0 : static int pim_msdp_peer_comp(const void *p1, const void *p2)
    1166             : {
    1167           0 :         const struct pim_msdp_peer *mp1 = p1;
    1168           0 :         const struct pim_msdp_peer *mp2 = p2;
    1169             : 
    1170           0 :         if (ntohl(mp1->peer.s_addr) < ntohl(mp2->peer.s_addr))
    1171             :                 return -1;
    1172             : 
    1173           0 :         if (ntohl(mp1->peer.s_addr) > ntohl(mp2->peer.s_addr))
    1174           0 :                 return 1;
    1175             : 
    1176             :         return 0;
    1177             : }
    1178             : 
    1179             : /************************** Mesh group management **************************/
    1180           0 : void pim_msdp_mg_free(struct pim_instance *pim, struct pim_msdp_mg **mgp)
    1181             : {
    1182           0 :         struct pim_msdp_mg_mbr *mbr;
    1183           0 :         struct listnode *n, *nn;
    1184             : 
    1185           0 :         if (*mgp == NULL)
    1186             :                 return;
    1187             : 
    1188             :         /* SIP is being removed - tear down all active peer sessions */
    1189           0 :         for (ALL_LIST_ELEMENTS((*mgp)->mbr_list, n, nn, mbr))
    1190           0 :                 pim_msdp_mg_mbr_del((*mgp), mbr);
    1191             : 
    1192           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
    1193           0 :                 zlog_debug("MSDP mesh-group %s deleted",
    1194             :                            (*mgp)->mesh_group_name);
    1195             :         }
    1196             : 
    1197           0 :         XFREE(MTYPE_PIM_MSDP_MG_NAME, (*mgp)->mesh_group_name);
    1198             : 
    1199           0 :         if ((*mgp)->mbr_list)
    1200           0 :                 list_delete(&(*mgp)->mbr_list);
    1201             : 
    1202           0 :         SLIST_REMOVE(&pim->msdp.mglist, (*mgp), pim_msdp_mg, mg_entry);
    1203           0 :         XFREE(MTYPE_PIM_MSDP_MG, (*mgp));
    1204             : }
    1205             : 
    1206           0 : struct pim_msdp_mg *pim_msdp_mg_new(struct pim_instance *pim,
    1207             :                                     const char *mesh_group_name)
    1208             : {
    1209           0 :         struct pim_msdp_mg *mg;
    1210             : 
    1211           0 :         mg = XCALLOC(MTYPE_PIM_MSDP_MG, sizeof(*mg));
    1212             : 
    1213           0 :         mg->mesh_group_name = XSTRDUP(MTYPE_PIM_MSDP_MG_NAME, mesh_group_name);
    1214           0 :         mg->mbr_list = list_new();
    1215           0 :         mg->mbr_list->del = (void (*)(void *))pim_msdp_mg_mbr_free;
    1216           0 :         mg->mbr_list->cmp = (int (*)(void *, void *))pim_msdp_mg_mbr_comp;
    1217             : 
    1218           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
    1219           0 :                 zlog_debug("MSDP mesh-group %s created", mg->mesh_group_name);
    1220             :         }
    1221             : 
    1222           0 :         SLIST_INSERT_HEAD(&pim->msdp.mglist, mg, mg_entry);
    1223             : 
    1224           0 :         return mg;
    1225             : }
    1226             : 
    1227           0 : static int pim_msdp_mg_mbr_comp(const void *p1, const void *p2)
    1228             : {
    1229           0 :         const struct pim_msdp_mg_mbr *mbr1 = p1;
    1230           0 :         const struct pim_msdp_mg_mbr *mbr2 = p2;
    1231             : 
    1232           0 :         if (ntohl(mbr1->mbr_ip.s_addr) < ntohl(mbr2->mbr_ip.s_addr))
    1233             :                 return -1;
    1234             : 
    1235           0 :         if (ntohl(mbr1->mbr_ip.s_addr) > ntohl(mbr2->mbr_ip.s_addr))
    1236           0 :                 return 1;
    1237             : 
    1238             :         return 0;
    1239             : }
    1240             : 
    1241           0 : static void pim_msdp_mg_mbr_free(struct pim_msdp_mg_mbr *mbr)
    1242             : {
    1243           0 :         XFREE(MTYPE_PIM_MSDP_MG_MBR, mbr);
    1244           0 : }
    1245             : 
    1246           0 : void pim_msdp_mg_mbr_del(struct pim_msdp_mg *mg, struct pim_msdp_mg_mbr *mbr)
    1247             : {
    1248             :         /* Delete active peer session if any */
    1249           0 :         if (mbr->mp) {
    1250           0 :                 pim_msdp_peer_del(&mbr->mp);
    1251             :         }
    1252             : 
    1253           0 :         listnode_delete(mg->mbr_list, mbr);
    1254           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
    1255           0 :                 char ip_str[INET_ADDRSTRLEN];
    1256           0 :                 pim_inet4_dump("<mbr?>", mbr->mbr_ip, ip_str, sizeof(ip_str));
    1257           0 :                 zlog_debug("MSDP mesh-group %s mbr %s deleted",
    1258             :                            mg->mesh_group_name, ip_str);
    1259             :         }
    1260           0 :         pim_msdp_mg_mbr_free(mbr);
    1261           0 :         if (mg->mbr_cnt) {
    1262           0 :                 --mg->mbr_cnt;
    1263             :         }
    1264           0 : }
    1265             : 
    1266           0 : static void pim_msdp_src_del(struct pim_msdp_mg *mg)
    1267             : {
    1268           0 :         struct pim_msdp_mg_mbr *mbr;
    1269           0 :         struct listnode *mbr_node;
    1270             : 
    1271             :         /* SIP is being removed - tear down all active peer sessions */
    1272           0 :         for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr)) {
    1273           0 :                 if (mbr->mp)
    1274           0 :                         pim_msdp_peer_del(&mbr->mp);
    1275             :         }
    1276           0 :         if (PIM_DEBUG_MSDP_EVENTS) {
    1277           0 :                 zlog_debug("MSDP mesh-group %s src cleared",
    1278             :                            mg->mesh_group_name);
    1279             :         }
    1280           0 : }
    1281             : 
    1282             : /*********************** MSDP feature APIs *********************************/
    1283           0 : int pim_msdp_config_write(struct pim_instance *pim, struct vty *vty,
    1284             :                           const char *spaces)
    1285             : {
    1286           0 :         struct pim_msdp_mg *mg;
    1287           0 :         struct listnode *mbrnode;
    1288           0 :         struct pim_msdp_mg_mbr *mbr;
    1289           0 :         char src_str[INET_ADDRSTRLEN];
    1290           0 :         int count = 0;
    1291             : 
    1292           0 :         if (SLIST_EMPTY(&pim->msdp.mglist))
    1293             :                 return count;
    1294             : 
    1295           0 :         SLIST_FOREACH (mg, &pim->msdp.mglist, mg_entry) {
    1296           0 :                 if (mg->src_ip.s_addr != INADDR_ANY) {
    1297           0 :                         pim_inet4_dump("<src?>", mg->src_ip, src_str,
    1298             :                                        sizeof(src_str));
    1299           0 :                         vty_out(vty, "%sip msdp mesh-group %s source %s\n",
    1300             :                                 spaces, mg->mesh_group_name, src_str);
    1301           0 :                         ++count;
    1302             :                 }
    1303             : 
    1304           0 :                 for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbrnode, mbr)) {
    1305           0 :                         vty_out(vty, "%sip msdp mesh-group %s member %pI4\n",
    1306             :                                 spaces, mg->mesh_group_name, &mbr->mbr_ip);
    1307           0 :                         ++count;
    1308             :                 }
    1309             :         }
    1310             : 
    1311             :         return count;
    1312             : }
    1313             : 
    1314           0 : bool pim_msdp_peer_config_write(struct vty *vty, struct pim_instance *pim,
    1315             :                                 const char *spaces)
    1316             : {
    1317           0 :         struct pim_msdp_peer *mp;
    1318           0 :         struct listnode *node;
    1319           0 :         bool written = false;
    1320             : 
    1321           0 :         for (ALL_LIST_ELEMENTS_RO(pim->msdp.peer_list, node, mp)) {
    1322             :                 /* Skip meshed group peers. */
    1323           0 :                 if (mp->flags & PIM_MSDP_PEERF_IN_GROUP)
    1324           0 :                         continue;
    1325             : 
    1326           0 :                 vty_out(vty, "%sip msdp peer %pI4 source %pI4\n", spaces,
    1327             :                         &mp->peer, &mp->local);
    1328           0 :                 written = true;
    1329             :         }
    1330             : 
    1331           0 :         return written;
    1332             : }
    1333             : 
    1334             : /* Enable feature including active/periodic timers etc. on the first peer
    1335             :  * config. Till then MSDP should just stay quiet. */
    1336           0 : static void pim_msdp_enable(struct pim_instance *pim)
    1337             : {
    1338           0 :         if (pim->msdp.flags & PIM_MSDPF_ENABLE) {
    1339             :                 /* feature is already enabled */
    1340             :                 return;
    1341             :         }
    1342           0 :         pim->msdp.flags |= PIM_MSDPF_ENABLE;
    1343           0 :         pim->msdp.work_obuf = stream_new(PIM_MSDP_MAX_PACKET_SIZE);
    1344           0 :         pim_msdp_sa_adv_timer_setup(pim, true /* start */);
    1345             :         /* setup sa cache based on local sources */
    1346           0 :         pim_msdp_sa_local_setup(pim);
    1347             : }
    1348             : 
    1349             : /* MSDP init */
    1350           4 : void pim_msdp_init(struct pim_instance *pim, struct thread_master *master)
    1351             : {
    1352           4 :         pim->msdp.master = master;
    1353           4 :         char hash_name[64];
    1354             : 
    1355           4 :         snprintf(hash_name, sizeof(hash_name), "PIM %s MSDP Peer Hash",
    1356           4 :                  pim->vrf->name);
    1357           4 :         pim->msdp.peer_hash = hash_create(pim_msdp_peer_hash_key_make,
    1358             :                                           pim_msdp_peer_hash_eq, hash_name);
    1359           4 :         pim->msdp.peer_list = list_new();
    1360           4 :         pim->msdp.peer_list->del = (void (*)(void *))pim_msdp_peer_free;
    1361           4 :         pim->msdp.peer_list->cmp = (int (*)(void *, void *))pim_msdp_peer_comp;
    1362             : 
    1363           4 :         snprintf(hash_name, sizeof(hash_name), "PIM %s MSDP SA Hash",
    1364           4 :                  pim->vrf->name);
    1365           4 :         pim->msdp.sa_hash = hash_create(pim_msdp_sa_hash_key_make,
    1366             :                                         pim_msdp_sa_hash_eq, hash_name);
    1367           4 :         pim->msdp.sa_list = list_new();
    1368           4 :         pim->msdp.sa_list->del = (void (*)(void *))pim_msdp_sa_free;
    1369           4 :         pim->msdp.sa_list->cmp = (int (*)(void *, void *))pim_msdp_sa_comp;
    1370           4 : }
    1371             : 
    1372             : /* counterpart to MSDP init; XXX: unused currently */
    1373           4 : void pim_msdp_exit(struct pim_instance *pim)
    1374             : {
    1375           4 :         struct pim_msdp_mg *mg;
    1376             : 
    1377           4 :         pim_msdp_sa_adv_timer_setup(pim, false);
    1378             : 
    1379             :         /* Stop listener and delete all peer sessions */
    1380           4 :         while ((mg = SLIST_FIRST(&pim->msdp.mglist)) != NULL)
    1381           0 :                 pim_msdp_mg_free(pim, &mg);
    1382             : 
    1383           4 :         if (pim->msdp.peer_hash) {
    1384           4 :                 hash_clean(pim->msdp.peer_hash, NULL);
    1385           4 :                 hash_free(pim->msdp.peer_hash);
    1386           4 :                 pim->msdp.peer_hash = NULL;
    1387             :         }
    1388             : 
    1389           4 :         if (pim->msdp.peer_list) {
    1390           4 :                 list_delete(&pim->msdp.peer_list);
    1391             :         }
    1392             : 
    1393           4 :         if (pim->msdp.sa_hash) {
    1394           4 :                 hash_clean(pim->msdp.sa_hash, NULL);
    1395           4 :                 hash_free(pim->msdp.sa_hash);
    1396           4 :                 pim->msdp.sa_hash = NULL;
    1397             :         }
    1398             : 
    1399           4 :         if (pim->msdp.sa_list) {
    1400           4 :                 list_delete(&pim->msdp.sa_list);
    1401             :         }
    1402             : 
    1403           4 :         if (pim->msdp.work_obuf)
    1404           0 :                 stream_free(pim->msdp.work_obuf);
    1405           4 :         pim->msdp.work_obuf = NULL;
    1406           4 : }
    1407             : 
    1408           0 : void pim_msdp_mg_src_add(struct pim_instance *pim, struct pim_msdp_mg *mg,
    1409             :                          struct in_addr *ai)
    1410             : {
    1411           0 :         struct pim_msdp_mg_mbr *mbr;
    1412           0 :         struct listnode *mbr_node;
    1413             : 
    1414             :         /* Stop all connections and remove data structures. */
    1415           0 :         pim_msdp_src_del(mg);
    1416             : 
    1417             :         /* Set new address. */
    1418           0 :         mg->src_ip = *ai;
    1419             : 
    1420             :         /* No new address, disable everyone. */
    1421           0 :         if (ai->s_addr == INADDR_ANY) {
    1422           0 :                 if (PIM_DEBUG_MSDP_EVENTS)
    1423           0 :                         zlog_debug("MSDP mesh-group %s src unset",
    1424             :                                    mg->mesh_group_name);
    1425           0 :                 return;
    1426             :         }
    1427             : 
    1428             :         /* Create data structures and start TCP connection. */
    1429           0 :         for (ALL_LIST_ELEMENTS_RO(mg->mbr_list, mbr_node, mbr))
    1430           0 :                 mbr->mp = pim_msdp_peer_add(pim, &mbr->mbr_ip, &mg->src_ip,
    1431           0 :                                             mg->mesh_group_name);
    1432             : 
    1433           0 :         if (PIM_DEBUG_MSDP_EVENTS)
    1434           0 :                 zlog_debug("MSDP mesh-group %s src %pI4 set",
    1435             :                            mg->mesh_group_name, &mg->src_ip);
    1436             : }
    1437             : 
    1438           0 : struct pim_msdp_mg_mbr *pim_msdp_mg_mbr_add(struct pim_instance *pim,
    1439             :                                             struct pim_msdp_mg *mg,
    1440             :                                             struct in_addr *ia)
    1441             : {
    1442           0 :         struct pim_msdp_mg_mbr *mbr;
    1443             : 
    1444           0 :         mbr = XCALLOC(MTYPE_PIM_MSDP_MG_MBR, sizeof(*mbr));
    1445           0 :         mbr->mbr_ip = *ia;
    1446           0 :         listnode_add_sort(mg->mbr_list, mbr);
    1447             : 
    1448             :         /* if valid SIP has been configured add peer session */
    1449           0 :         if (mg->src_ip.s_addr != INADDR_ANY)
    1450           0 :                 mbr->mp = pim_msdp_peer_add(pim, &mbr->mbr_ip, &mg->src_ip,
    1451           0 :                                             mg->mesh_group_name);
    1452             : 
    1453           0 :         if (PIM_DEBUG_MSDP_EVENTS)
    1454           0 :                 zlog_debug("MSDP mesh-group %s mbr %pI4 created",
    1455             :                            mg->mesh_group_name, &mbr->mbr_ip);
    1456             : 
    1457           0 :         ++mg->mbr_cnt;
    1458             : 
    1459           0 :         return mbr;
    1460             : }

Generated by: LCOV version v1.16-topotato