back to topotato report
topotato coverage report
Current view: top level - pimd - pim_vxlan.c (source / functions) Hit Total Coverage
Test: aggregated run ( view descriptions ) Lines: 60 499 12.0 %
Date: 2023-02-24 19:38:44 Functions: 9 51 17.6 %

          Line data    Source code
       1             : /* PIM support for VxLAN BUM flooding
       2             :  *
       3             :  * Copyright (C) 2019 Cumulus Networks, Inc.
       4             :  *
       5             :  * This file is part of FRR.
       6             :  *
       7             :  * FRR is free software; you can redistribute it and/or modify it
       8             :  * under the terms of the GNU General Public License as published by the
       9             :  * Free Software Foundation; either version 2, or (at your option) any
      10             :  * later version.
      11             :  *
      12             :  * FRR is distributed in the hope that it will be useful, but
      13             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :  * General Public License for more details.
      16             :  * This program is free software; you can redistribute it and/or modify
      17             :  * it under the terms of the GNU General Public License as published by
      18             :  * the Free Software Foundation; either version 2 of the License, or
      19             :  * (at your option) any later version.
      20             :  */
      21             : 
      22             : #include <zebra.h>
      23             : 
      24             : #include <hash.h>
      25             : #include <jhash.h>
      26             : #include <log.h>
      27             : #include <prefix.h>
      28             : #include <vrf.h>
      29             : 
      30             : #include "pimd.h"
      31             : #include "pim_iface.h"
      32             : #include "pim_memory.h"
      33             : #include "pim_oil.h"
      34             : #include "pim_register.h"
      35             : #include "pim_str.h"
      36             : #include "pim_upstream.h"
      37             : #include "pim_ifchannel.h"
      38             : #include "pim_nht.h"
      39             : #include "pim_zebra.h"
      40             : #include "pim_vxlan.h"
      41             : #include "pim_mlag.h"
      42             : 
      43             : /* pim-vxlan global info */
      44             : struct pim_vxlan vxlan_info, *pim_vxlan_p = &vxlan_info;
      45             : 
      46             : static void pim_vxlan_work_timer_setup(bool start);
      47             : static void pim_vxlan_set_peerlink_rif(struct pim_instance *pim,
      48             :                         struct interface *ifp);
      49             : 
      50             : /*************************** vxlan work list **********************************
      51             :  * A work list is maintained for staggered generation of pim null register
      52             :  * messages for vxlan SG entries that are in a reg_join state.
      53             :  *
      54             :  * A max of 500 NULL registers are generated at one shot. If paused reg
      55             :  * generation continues on the next second and so on till all register
      56             :  * messages have been sent out. And the process is restarted every 60s.
      57             :  *
      58             :  * purpose of this null register generation is to setup the SPT and maintain
      59             :  * independent of the presence of overlay BUM traffic.
      60             :  ****************************************************************************/
      61           0 : static void pim_vxlan_do_reg_work(void)
      62             : {
      63           0 :         struct listnode *listnode;
      64           0 :         int work_cnt = 0;
      65           0 :         struct pim_vxlan_sg *vxlan_sg;
      66           0 :         static int sec_count;
      67             : 
      68           0 :         ++sec_count;
      69             : 
      70           0 :         if (sec_count > PIM_VXLAN_NULL_REG_INTERVAL) {
      71           0 :                 sec_count = 0;
      72           0 :                 listnode = vxlan_info.next_work ?
      73           0 :                                         vxlan_info.next_work :
      74           0 :                                         vxlan_info.work_list->head;
      75           0 :                 if (PIM_DEBUG_VXLAN && listnode)
      76           0 :                         zlog_debug("vxlan SG work %s",
      77             :                                 vxlan_info.next_work ? "continues" : "starts");
      78             :         } else {
      79           0 :                 listnode = vxlan_info.next_work;
      80             :         }
      81             : 
      82           0 :         for (; listnode; listnode = listnode->next) {
      83           0 :                 vxlan_sg = (struct pim_vxlan_sg *)listnode->data;
      84           0 :                 if (vxlan_sg->up && (vxlan_sg->up->reg_state == PIM_REG_JOIN)) {
      85           0 :                         if (PIM_DEBUG_VXLAN)
      86           0 :                                 zlog_debug("vxlan SG %s periodic NULL register",
      87             :                                                 vxlan_sg->sg_str);
      88             : 
      89             :                         /*
      90             :                          * If we are on the work queue *and* the rpf
      91             :                          * has been lost on the vxlan_sg->up let's
      92             :                          * make sure that we don't send it.
      93             :                          */
      94           0 :                         if (vxlan_sg->up->rpf.source_nexthop.interface) {
      95           0 :                                 pim_null_register_send(vxlan_sg->up);
      96           0 :                                 ++work_cnt;
      97             :                         }
      98             :                 }
      99             : 
     100           0 :                 if (work_cnt > vxlan_info.max_work_cnt) {
     101           0 :                         vxlan_info.next_work = listnode->next;
     102           0 :                         if (PIM_DEBUG_VXLAN)
     103           0 :                                 zlog_debug("vxlan SG %d work items proc and pause",
     104             :                                         work_cnt);
     105           0 :                         return;
     106             :                 }
     107             :         }
     108             : 
     109           0 :         if (work_cnt) {
     110           0 :                 if (PIM_DEBUG_VXLAN)
     111           0 :                         zlog_debug("vxlan SG %d work items proc", work_cnt);
     112             :         }
     113           0 :         vxlan_info.next_work = NULL;
     114             : }
     115             : 
     116             : /* Staggered work related info is initialized when the first work comes
     117             :  * along
     118             :  */
     119           0 : static void pim_vxlan_init_work(void)
     120             : {
     121           0 :         if (vxlan_info.flags & PIM_VXLANF_WORK_INITED)
     122             :                 return;
     123             : 
     124           0 :         vxlan_info.max_work_cnt = PIM_VXLAN_WORK_MAX;
     125           0 :         vxlan_info.flags |= PIM_VXLANF_WORK_INITED;
     126           0 :         vxlan_info.work_list = list_new();
     127           0 :         pim_vxlan_work_timer_setup(true/* start */);
     128             : }
     129             : 
     130           0 : static void pim_vxlan_add_work(struct pim_vxlan_sg *vxlan_sg)
     131             : {
     132           0 :         if (vxlan_sg->flags & PIM_VXLAN_SGF_DEL_IN_PROG) {
     133           0 :                 if (PIM_DEBUG_VXLAN)
     134           0 :                         zlog_debug("vxlan SG %s skip work list; del-in-prog",
     135             :                                         vxlan_sg->sg_str);
     136           0 :                 return;
     137             :         }
     138             : 
     139           0 :         pim_vxlan_init_work();
     140             : 
     141             :         /* already a part of the work list */
     142           0 :         if (vxlan_sg->work_node)
     143             :                 return;
     144             : 
     145           0 :         if (PIM_DEBUG_VXLAN)
     146           0 :                 zlog_debug("vxlan SG %s work list add",
     147             :                                 vxlan_sg->sg_str);
     148           0 :         vxlan_sg->work_node = listnode_add(vxlan_info.work_list, vxlan_sg);
     149             :         /* XXX: adjust max_work_cnt if needed */
     150             : }
     151             : 
     152           0 : static void pim_vxlan_del_work(struct pim_vxlan_sg *vxlan_sg)
     153             : {
     154           0 :         if (!vxlan_sg->work_node)
     155             :                 return;
     156             : 
     157           0 :         if (PIM_DEBUG_VXLAN)
     158           0 :                 zlog_debug("vxlan SG %s work list del",
     159             :                                 vxlan_sg->sg_str);
     160             : 
     161           0 :         if (vxlan_sg->work_node == vxlan_info.next_work)
     162           0 :                 vxlan_info.next_work = vxlan_sg->work_node->next;
     163             : 
     164           0 :         list_delete_node(vxlan_info.work_list, vxlan_sg->work_node);
     165           0 :         vxlan_sg->work_node = NULL;
     166             : }
     167             : 
     168           6 : void pim_vxlan_update_sg_reg_state(struct pim_instance *pim,
     169             :                 struct pim_upstream *up, bool reg_join)
     170             : {
     171           6 :         struct pim_vxlan_sg *vxlan_sg;
     172             : 
     173          12 :         vxlan_sg = pim_vxlan_sg_find(pim, &up->sg);
     174           6 :         if (!vxlan_sg)
     175             :                 return;
     176             : 
     177             :         /* add the vxlan sg entry to a work list for periodic reg joins.
     178             :          * the entry will stay in the list as long as the register state is
     179             :          * PIM_REG_JOIN
     180             :          */
     181           0 :         if (reg_join)
     182           0 :                 pim_vxlan_add_work(vxlan_sg);
     183             :         else
     184           0 :                 pim_vxlan_del_work(vxlan_sg);
     185             : }
     186             : 
     187           0 : static void pim_vxlan_work_timer_cb(struct thread *t)
     188             : {
     189           0 :         pim_vxlan_do_reg_work();
     190           0 :         pim_vxlan_work_timer_setup(true /* start */);
     191           0 : }
     192             : 
     193             : /* global 1second timer used for periodic processing */
     194          18 : static void pim_vxlan_work_timer_setup(bool start)
     195             : {
     196          18 :         THREAD_OFF(vxlan_info.work_timer);
     197          18 :         if (start)
     198           0 :                 thread_add_timer(router->master, pim_vxlan_work_timer_cb, NULL,
     199             :                         PIM_VXLAN_WORK_TIME, &vxlan_info.work_timer);
     200          18 : }
     201             : 
     202             : /**************************** vxlan origination mroutes ***********************
     203             :  * For every (local-vtep-ip, bum-mcast-grp) registered by evpn an origination
     204             :  * mroute is setup by pimd. The purpose of this mroute is to forward vxlan
     205             :  * encapsulated BUM (broadcast, unknown-unicast and unknown-multicast packets
     206             :  * over the underlay.)
     207             :  *
     208             :  * Sample mroute (single VTEP):
     209             :  * (27.0.0.7, 239.1.1.100)     Iif: lo      Oifs: uplink-1
     210             :  *
     211             :  * Sample mroute (anycast VTEP):
     212             :  * (36.0.0.9, 239.1.1.100)          Iif: peerlink-3.4094\
     213             :  *                                       Oifs: peerlink-3.4094 uplink-1
     214             :  ***************************************************************************/
     215           0 : static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg *vxlan_sg)
     216             : {
     217           0 :         struct pim_upstream *up = vxlan_sg->up;
     218             : 
     219           0 :         if (!up)
     220             :                 return;
     221             : 
     222           0 :         if (PIM_DEBUG_VXLAN)
     223           0 :                 zlog_debug("vxlan SG %s orig mroute-up del",
     224             :                         vxlan_sg->sg_str);
     225             : 
     226           0 :         vxlan_sg->up = NULL;
     227             : 
     228           0 :         if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG) {
     229             :                 /* clear out all the vxlan properties */
     230           0 :                 up->flags &= ~(PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG |
     231             :                         PIM_UPSTREAM_FLAG_MASK_STATIC_IIF |
     232             :                         PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY |
     233             :                         PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG |
     234             :                         PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA |
     235             :                         PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL);
     236             : 
     237             :                 /* We bring things to a grinding halt by force expirying
     238             :                  * the kat. Doing this will also remove the reference we
     239             :                  * created as a "vxlan" source and delete the upstream entry
     240             :                  * if there are no other references.
     241             :                  */
     242           0 :                 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
     243           0 :                         THREAD_OFF(up->t_ka_timer);
     244           0 :                         up = pim_upstream_keep_alive_timer_proc(up);
     245             :                 } else {
     246             :                         /* this is really unexpected as we force vxlan
     247             :                          * origination mroutes active sources but just in
     248             :                          * case
     249             :                          */
     250           0 :                         up = pim_upstream_del(vxlan_sg->pim, up, __func__);
     251             :                 }
     252             :                 /* if there are other references register the source
     253             :                  * for nht
     254             :                  */
     255           0 :                 if (up) {
     256           0 :                         enum pim_rpf_result r;
     257             : 
     258           0 :                         r = pim_rpf_update(vxlan_sg->pim, up, NULL, __func__);
     259           0 :                         if (r == PIM_RPF_FAILURE) {
     260           0 :                                 if (PIM_DEBUG_VXLAN)
     261           0 :                                         zlog_debug(
     262             :                                                 "vxlan SG %s rpf_update failure",
     263             :                                                 vxlan_sg->sg_str);
     264             :                         }
     265             :                 }
     266             :         }
     267             : }
     268             : 
     269           0 : static void pim_vxlan_orig_mr_up_iif_update(struct pim_vxlan_sg *vxlan_sg)
     270             : {
     271             :         /* update MFC with the new IIF */
     272           0 :         pim_upstream_fill_static_iif(vxlan_sg->up, vxlan_sg->iif);
     273           0 :         pim_upstream_mroute_iif_update(vxlan_sg->up->channel_oil, __func__);
     274             : 
     275           0 :         if (PIM_DEBUG_VXLAN)
     276           0 :                 zlog_debug("vxlan SG %s orig mroute-up updated with iif %s",
     277             :                         vxlan_sg->sg_str,
     278             :                         vxlan_sg->iif?vxlan_sg->iif->name:"-");
     279             : 
     280           0 : }
     281             : 
     282             : /* For every VxLAN BUM multicast group we setup a SG-up that has the following
     283             :  * "forced properties" -
     284             :  * 1. Directly connected on a DR interface i.e. we must act as an FHR
     285             :  * 2. We prime the pump i.e. no multicast data is needed to register this
     286             :  *    source with the FHR. To do that we send periodic null registers if
     287             :  *    the SG entry is in a register-join state. We also prevent expiry of
     288             :  *    KAT.
     289             :  * 3. As this SG is setup without data there is no need to register encapsulate
     290             :  *    data traffic. This encapsulation is explicitly skipped for the following
     291             :  *    reasons -
     292             :  *    a) Many levels of encapsulation are needed creating MTU disc challenges.
     293             :  *       Overlay BUM is encapsulated in a vxlan/UDP/IP header and then
     294             :  *       encapsulated again in a pim-register header.
     295             :  *    b) On a vxlan-aa setup both switches rx a copy of each BUM packet. if
     296             :  *       they both reg encapsulated traffic the RP will accept the duplicates
     297             :  *       as there are no RPF checks for this encapsulated data.
     298             :  *    a), b) can be workarounded if needed, but there is really no need because
     299             :  *    of (2) i.e. the pump is primed without data.
     300             :  */
     301           0 : static void pim_vxlan_orig_mr_up_add(struct pim_vxlan_sg *vxlan_sg)
     302             : {
     303           0 :         struct pim_upstream *up;
     304           0 :         struct pim_interface *term_ifp;
     305           0 :         int flags = 0;
     306           0 :         struct pim_instance *pim = vxlan_sg->pim;
     307             : 
     308           0 :         if (vxlan_sg->up) {
     309             :                 /* nothing to do */
     310             :                 return;
     311             :         }
     312             : 
     313           0 :         if (PIM_DEBUG_VXLAN)
     314           0 :                 zlog_debug("vxlan SG %s orig mroute-up add with iif %s",
     315             :                         vxlan_sg->sg_str,
     316             :                         vxlan_sg->iif?vxlan_sg->iif->name:"-");
     317             : 
     318           0 :         PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_ORIG(flags);
     319             :         /* pin the IIF to lo or peerlink-subinterface and disable NHT */
     320           0 :         PIM_UPSTREAM_FLAG_SET_STATIC_IIF(flags);
     321             :         /* Fake traffic by setting SRC_STREAM and starting KAT */
     322             :         /* We intentionally skip updating ref count for SRC_STREAM/FHR.
     323             :          * Setting SRC_VXLAN should have already created a reference
     324             :          * preventing the entry from being deleted
     325             :          */
     326           0 :         PIM_UPSTREAM_FLAG_SET_FHR(flags);
     327           0 :         PIM_UPSTREAM_FLAG_SET_SRC_STREAM(flags);
     328             :         /* Force pimreg even if non-DR. This is needed on a MLAG setup for
     329             :          * VxLAN AA
     330             :          */
     331           0 :         PIM_UPSTREAM_FLAG_SET_FORCE_PIMREG(flags);
     332             :         /* prevent KAT expiry. we want the MDT setup even if there is no BUM
     333             :          * traffic
     334             :          */
     335           0 :         PIM_UPSTREAM_FLAG_SET_DISABLE_KAT_EXPIRY(flags);
     336             :         /* SPT for vxlan BUM groups is primed and maintained via NULL
     337             :          * registers so there is no need to reg-encapsulate
     338             :          * vxlan-encapsulated overlay data traffic
     339             :          */
     340           0 :         PIM_UPSTREAM_FLAG_SET_NO_PIMREG_DATA(flags);
     341             :         /* On a MLAG setup we force a copy to the MLAG peer while also
     342             :          * accepting traffic from the peer. To do this we set peerlink-rif as
     343             :          * the IIF and also add it to the OIL
     344             :          */
     345           0 :         PIM_UPSTREAM_FLAG_SET_ALLOW_IIF_IN_OIL(flags);
     346             : 
     347             :         /* XXX: todo: defer pim_upstream add if pim is not enabled on the iif */
     348           0 :         up = pim_upstream_find(vxlan_sg->pim, &vxlan_sg->sg);
     349           0 :         if (up) {
     350             :                 /* if the iif is set to something other than the vxlan_sg->iif
     351             :                  * we must dereg the old nexthop and force to new "static"
     352             :                  * iif
     353             :                  */
     354           0 :                 if (!PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags)) {
     355           0 :                         pim_delete_tracked_nexthop(vxlan_sg->pim,
     356             :                                                    up->upstream_addr, up, NULL);
     357             :                 }
     358             :                 /* We are acting FHR; clear out use_rpt setting if any */
     359           0 :                 pim_upstream_update_use_rpt(up, false /*update_mroute*/);
     360           0 :                 pim_upstream_ref(up, flags, __func__);
     361           0 :                 vxlan_sg->up = up;
     362           0 :                 term_ifp = pim_vxlan_get_term_ifp(pim);
     363             :                 /* mute termination device on origination mroutes */
     364           0 :                 if (term_ifp)
     365           0 :                         pim_channel_update_oif_mute(up->channel_oil,
     366             :                                         term_ifp);
     367           0 :                 pim_vxlan_orig_mr_up_iif_update(vxlan_sg);
     368             :                 /* mute pimreg on origination mroutes */
     369           0 :                 if (pim->regiface)
     370           0 :                         pim_channel_update_oif_mute(up->channel_oil,
     371           0 :                                         pim->regiface->info);
     372             :         } else {
     373           0 :                 up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg,
     374             :                                       vxlan_sg->iif, flags, __func__, NULL);
     375           0 :                 vxlan_sg->up = up;
     376             :         }
     377             : 
     378           0 :         if (!up) {
     379           0 :                 if (PIM_DEBUG_VXLAN)
     380           0 :                         zlog_debug("vxlan SG %s orig mroute-up add failed",
     381             :                                         vxlan_sg->sg_str);
     382           0 :                 return;
     383             :         }
     384             : 
     385           0 :         pim_upstream_keep_alive_timer_start(up, vxlan_sg->pim->keep_alive_time);
     386             : 
     387             :         /* register the source with the RP */
     388           0 :         switch (up->reg_state) {
     389             : 
     390           0 :         case PIM_REG_NOINFO:
     391           0 :                 pim_register_join(up);
     392           0 :                 pim_null_register_send(up);
     393           0 :                 break;
     394             : 
     395           0 :         case PIM_REG_JOIN:
     396             :                 /* if the pim upstream entry is already in reg-join state
     397             :                  * send null_register right away and add to the register
     398             :                  * worklist
     399             :                  */
     400           0 :                 pim_null_register_send(up);
     401           0 :                 pim_vxlan_update_sg_reg_state(pim, up, true);
     402           0 :                 break;
     403             : 
     404             :         case PIM_REG_JOIN_PENDING:
     405             :         case PIM_REG_PRUNE:
     406             :                 break;
     407             :         }
     408             : 
     409             :         /* update the inherited OIL */
     410           0 :         pim_upstream_inherited_olist(vxlan_sg->pim, up);
     411           0 :         if (!up->channel_oil->installed)
     412           0 :                 pim_upstream_mroute_add(up->channel_oil, __func__);
     413             : }
     414             : 
     415           0 : static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg)
     416             : {
     417           0 :         if (!vxlan_sg->up || !vxlan_sg->orig_oif)
     418             :                 return;
     419             : 
     420           0 :         if (PIM_DEBUG_VXLAN)
     421           0 :                 zlog_debug("vxlan SG %s oif %s add",
     422             :                         vxlan_sg->sg_str, vxlan_sg->orig_oif->name);
     423             : 
     424           0 :         vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED;
     425           0 :         pim_channel_add_oif(vxlan_sg->up->channel_oil,
     426             :                 vxlan_sg->orig_oif, PIM_OIF_FLAG_PROTO_VXLAN,
     427             :                 __func__);
     428             : }
     429             : 
     430           0 : static void pim_vxlan_orig_mr_oif_del(struct pim_vxlan_sg *vxlan_sg)
     431             : {
     432           0 :         struct interface *orig_oif;
     433             : 
     434           0 :         orig_oif = vxlan_sg->orig_oif;
     435           0 :         vxlan_sg->orig_oif = NULL;
     436             : 
     437           0 :         if (!(vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED))
     438             :                 return;
     439             : 
     440           0 :         if (PIM_DEBUG_VXLAN)
     441           0 :                 zlog_debug("vxlan SG %s oif %s del",
     442             :                         vxlan_sg->sg_str, orig_oif->name);
     443             : 
     444           0 :         vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED;
     445           0 :         pim_channel_del_oif(vxlan_sg->up->channel_oil,
     446             :                         orig_oif, PIM_OIF_FLAG_PROTO_VXLAN, __func__);
     447             : }
     448             : 
     449           0 : static inline struct interface *pim_vxlan_orig_mr_oif_get(
     450             :                 struct pim_instance *pim)
     451             : {
     452           0 :         return (vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) ?
     453           0 :                 pim->vxlan.peerlink_rif : NULL;
     454             : }
     455             : 
     456             : /* Single VTEPs: IIF for the vxlan-origination-mroutes is lo or vrf-dev (if
     457             :  * the mroute is in a non-default vrf).
     458             :  * Anycast VTEPs: IIF is the MLAG ISL/peerlink.
     459             :  */
     460          44 : static inline struct interface *pim_vxlan_orig_mr_iif_get(
     461             :                 struct pim_instance *pim)
     462             : {
     463           0 :         return ((vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) &&
     464           0 :                         pim->vxlan.peerlink_rif) ?
     465          22 :                 pim->vxlan.peerlink_rif : pim->vxlan.default_iif;
     466             : }
     467             : 
     468           0 : static bool pim_vxlan_orig_mr_add_is_ok(struct pim_vxlan_sg *vxlan_sg)
     469             : {
     470           0 :         struct pim_interface *pim_ifp;
     471             : 
     472           0 :         vxlan_sg->iif = pim_vxlan_orig_mr_iif_get(vxlan_sg->pim);
     473           0 :         if (!vxlan_sg->iif)
     474             :                 return false;
     475             : 
     476           0 :         pim_ifp = (struct pim_interface *)vxlan_sg->iif->info;
     477           0 :         if (!pim_ifp || (pim_ifp->mroute_vif_index < 0))
     478           0 :                 return false;
     479             : 
     480             :         return true;
     481             : }
     482             : 
     483           0 : static void pim_vxlan_orig_mr_install(struct pim_vxlan_sg *vxlan_sg)
     484             : {
     485           0 :         pim_vxlan_orig_mr_up_add(vxlan_sg);
     486             : 
     487           0 :         vxlan_sg->orig_oif = pim_vxlan_orig_mr_oif_get(vxlan_sg->pim);
     488           0 :         pim_vxlan_orig_mr_oif_add(vxlan_sg);
     489           0 : }
     490             : 
     491           0 : static void pim_vxlan_orig_mr_add(struct pim_vxlan_sg *vxlan_sg)
     492             : {
     493           0 :         if (!pim_vxlan_orig_mr_add_is_ok(vxlan_sg))
     494             :                 return;
     495             : 
     496           0 :         if (PIM_DEBUG_VXLAN)
     497           0 :                 zlog_debug("vxlan SG %s orig-mr add", vxlan_sg->sg_str);
     498             : 
     499           0 :         pim_vxlan_orig_mr_install(vxlan_sg);
     500             : }
     501             : 
     502           0 : static void pim_vxlan_orig_mr_del(struct pim_vxlan_sg *vxlan_sg)
     503             : {
     504           0 :         if (PIM_DEBUG_VXLAN)
     505           0 :                 zlog_debug("vxlan SG %s orig-mr del", vxlan_sg->sg_str);
     506             : 
     507           0 :         pim_vxlan_orig_mr_oif_del(vxlan_sg);
     508           0 :         pim_vxlan_orig_mr_up_del(vxlan_sg);
     509           0 : }
     510             : 
     511           0 : static void pim_vxlan_orig_mr_iif_update(struct hash_bucket *bucket, void *arg)
     512             : {
     513           0 :         struct interface *ifp;
     514           0 :         struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)bucket->data;
     515           0 :         struct interface *old_iif = vxlan_sg->iif;
     516             : 
     517           0 :         if (!pim_vxlan_is_orig_mroute(vxlan_sg))
     518             :                 return;
     519             : 
     520           0 :         ifp = pim_vxlan_orig_mr_iif_get(vxlan_sg->pim);
     521           0 :         if (PIM_DEBUG_VXLAN)
     522           0 :                 zlog_debug("vxlan SG %s iif changed from %s to %s",
     523             :                                 vxlan_sg->sg_str,
     524             :                                 old_iif ? old_iif->name : "-",
     525             :                                 ifp ? ifp->name : "-");
     526             : 
     527           0 :         if (pim_vxlan_orig_mr_add_is_ok(vxlan_sg)) {
     528           0 :                 if (vxlan_sg->up) {
     529             :                         /* upstream exists but iif changed */
     530           0 :                         pim_vxlan_orig_mr_up_iif_update(vxlan_sg);
     531             :                 } else {
     532             :                         /* install mroute */
     533           0 :                         pim_vxlan_orig_mr_install(vxlan_sg);
     534             :                 }
     535             :         } else {
     536           0 :                 pim_vxlan_orig_mr_del(vxlan_sg);
     537             :         }
     538             : }
     539             : 
     540             : /**************************** vxlan termination mroutes ***********************
     541             :  * For every bum-mcast-grp registered by evpn a *G termination
     542             :  * mroute is setup by pimd. The purpose of this mroute is to pull down vxlan
     543             :  * packets with the bum-mcast-grp dip from the underlay and terminate the
     544             :  * tunnel. This is done by including the vxlan termination device (ipmr-lo) in
     545             :  * its OIL. The vxlan de-capsulated packets are subject to subsequent overlay
     546             :  * bridging.
     547             :  *
     548             :  * Sample mroute:
     549             :  * (0.0.0.0, 239.1.1.100)     Iif: uplink-1      Oifs: ipmr-lo, uplink-1
     550             :  *****************************************************************************/
     551          16 : struct pim_interface *pim_vxlan_get_term_ifp(struct pim_instance *pim)
     552             : {
     553          16 :         return pim->vxlan.term_if ?
     554          16 :                 (struct pim_interface *)pim->vxlan.term_if->info : NULL;
     555             : }
     556             : 
     557           0 : static void pim_vxlan_term_mr_oif_add(struct pim_vxlan_sg *vxlan_sg)
     558             : {
     559           0 :         if (vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED)
     560             :                 return;
     561             : 
     562           0 :         if (PIM_DEBUG_VXLAN)
     563           0 :                 zlog_debug("vxlan SG %s term-oif %s add",
     564             :                         vxlan_sg->sg_str, vxlan_sg->term_oif->name);
     565             : 
     566           0 :         if (pim_ifchannel_local_membership_add(vxlan_sg->term_oif,
     567             :                                 &vxlan_sg->sg, true /*is_vxlan */)) {
     568           0 :                 vxlan_sg->flags |= PIM_VXLAN_SGF_OIF_INSTALLED;
     569             :                 /* update the inherited OIL */
     570             :                 /* XXX - I don't see the inherited OIL updated when a local
     571             :                  * member is added. And that probably needs to be fixed. Till
     572             :                  * that happens we do a force update on the inherited OIL
     573             :                  * here.
     574             :                  */
     575           0 :                 pim_upstream_inherited_olist(vxlan_sg->pim, vxlan_sg->up);
     576             :         } else {
     577           0 :                 zlog_warn("vxlan SG %s term-oif %s add failed",
     578             :                         vxlan_sg->sg_str, vxlan_sg->term_oif->name);
     579             :         }
     580             : }
     581             : 
     582           0 : static void pim_vxlan_term_mr_oif_del(struct pim_vxlan_sg *vxlan_sg)
     583             : {
     584           0 :         if (!(vxlan_sg->flags & PIM_VXLAN_SGF_OIF_INSTALLED))
     585             :                 return;
     586             : 
     587           0 :         if (PIM_DEBUG_VXLAN)
     588           0 :                 zlog_debug("vxlan SG %s oif %s del",
     589             :                         vxlan_sg->sg_str, vxlan_sg->term_oif->name);
     590             : 
     591           0 :         vxlan_sg->flags &= ~PIM_VXLAN_SGF_OIF_INSTALLED;
     592           0 :         pim_ifchannel_local_membership_del(vxlan_sg->term_oif, &vxlan_sg->sg);
     593             :         /* update the inherited OIL */
     594             :         /* XXX - I don't see the inherited OIL updated when a local member
     595             :          * is deleted. And that probably needs to be fixed. Till that happens
     596             :          * we do a force update on the inherited OIL here.
     597             :          */
     598           0 :         pim_upstream_inherited_olist(vxlan_sg->pim, vxlan_sg->up);
     599             : }
     600             : 
     601           0 : static void pim_vxlan_update_sg_entry_mlag(struct pim_instance *pim,
     602             :                 struct pim_upstream *up, bool inherit)
     603             : {
     604           0 :         bool is_df = true;
     605             : 
     606           0 :         if (inherit && up->parent &&
     607           0 :                         PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->parent->flags) &&
     608             :                         PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(up->parent->flags))
     609           0 :                 is_df = false;
     610             : 
     611           0 :         pim_mlag_up_df_role_update(pim, up, is_df, "inherit_xg_df");
     612           0 : }
     613             : 
     614             : /* We run MLAG DF election only on mroutes that have the termination
     615             :  * device ipmr-lo in the immediate OIL. This is only (*, G) entries at the
     616             :  * moment. For (S, G) entries that (with ipmr-lo in the inherited OIL) we
     617             :  * inherit the DF role from the (*, G) entry.
     618             :  */
     619           0 : void pim_vxlan_inherit_mlag_flags(struct pim_instance *pim,
     620             :                 struct pim_upstream *up, bool inherit)
     621             : {
     622           0 :         struct listnode *listnode;
     623           0 :         struct pim_upstream *child;
     624             : 
     625           0 :         for (ALL_LIST_ELEMENTS_RO(up->sources, listnode,
     626             :                                 child)) {
     627           0 :                 pim_vxlan_update_sg_entry_mlag(pim,
     628             :                                 child, true /* inherit */);
     629             :         }
     630           0 : }
     631             : 
     632           0 : static void pim_vxlan_term_mr_up_add(struct pim_vxlan_sg *vxlan_sg)
     633             : {
     634           0 :         struct pim_upstream *up;
     635           0 :         int flags = 0;
     636             : 
     637           0 :         if (vxlan_sg->up) {
     638             :                 /* nothing to do */
     639             :                 return;
     640             :         }
     641             : 
     642           0 :         if (PIM_DEBUG_VXLAN)
     643           0 :                 zlog_debug("vxlan SG %s term mroute-up add",
     644             :                         vxlan_sg->sg_str);
     645             : 
     646           0 :         PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags);
     647             :         /* enable MLAG designated-forwarder election on termination mroutes */
     648           0 :         PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags);
     649             : 
     650           0 :         up = pim_upstream_add(vxlan_sg->pim, &vxlan_sg->sg, NULL /* iif */,
     651             :                               flags, __func__, NULL);
     652           0 :         vxlan_sg->up = up;
     653             : 
     654           0 :         if (!up) {
     655           0 :                 zlog_warn("vxlan SG %s term mroute-up add failed",
     656             :                         vxlan_sg->sg_str);
     657           0 :                 return;
     658             :         }
     659             : 
     660             :         /* update existing SG entries with the parent's MLAG flag */
     661           0 :         pim_vxlan_inherit_mlag_flags(vxlan_sg->pim, up, true /*enable*/);
     662             : }
     663             : 
     664           0 : static void pim_vxlan_term_mr_up_del(struct pim_vxlan_sg *vxlan_sg)
     665             : {
     666           0 :         struct pim_upstream *up = vxlan_sg->up;
     667             : 
     668           0 :         if (!up)
     669             :                 return;
     670             : 
     671           0 :         if (PIM_DEBUG_VXLAN)
     672           0 :                 zlog_debug("vxlan SG %s term mroute-up del",
     673             :                         vxlan_sg->sg_str);
     674           0 :         vxlan_sg->up = NULL;
     675           0 :         if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM) {
     676             :                 /* update SG entries that are inheriting from this XG entry */
     677           0 :                 pim_vxlan_inherit_mlag_flags(vxlan_sg->pim, up,
     678             :                                 false /*enable*/);
     679             :                 /* clear out all the vxlan related flags */
     680           0 :                 up->flags &= ~(PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM |
     681             :                         PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN);
     682           0 :                 pim_mlag_up_local_del(vxlan_sg->pim, up);
     683           0 :                 pim_upstream_del(vxlan_sg->pim, up, __func__);
     684             :         }
     685             : }
     686             : 
     687           0 : static void pim_vxlan_term_mr_add(struct pim_vxlan_sg *vxlan_sg)
     688             : {
     689           0 :         if (PIM_DEBUG_VXLAN)
     690           0 :                 zlog_debug("vxlan SG %s term mroute add", vxlan_sg->sg_str);
     691             : 
     692           0 :         vxlan_sg->term_oif = vxlan_sg->pim->vxlan.term_if;
     693           0 :         if (!vxlan_sg->term_oif)
     694             :                 /* defer termination mroute till we have a termination device */
     695             :                 return;
     696             : 
     697           0 :         pim_vxlan_term_mr_up_add(vxlan_sg);
     698             :         /* set up local membership for the term-oif */
     699           0 :         pim_vxlan_term_mr_oif_add(vxlan_sg);
     700             : }
     701             : 
     702           0 : static void pim_vxlan_term_mr_del(struct pim_vxlan_sg *vxlan_sg)
     703             : {
     704           0 :         if (PIM_DEBUG_VXLAN)
     705           0 :                 zlog_debug("vxlan SG %s term mroute del", vxlan_sg->sg_str);
     706             : 
     707             :         /* remove local membership associated with the term oif */
     708           0 :         pim_vxlan_term_mr_oif_del(vxlan_sg);
     709             :         /* remove references to the upstream entry */
     710           0 :         pim_vxlan_term_mr_up_del(vxlan_sg);
     711           0 : }
     712             : 
     713             : /************************** vxlan SG cache management ************************/
     714           0 : static unsigned int pim_vxlan_sg_hash_key_make(const void *p)
     715             : {
     716           0 :         const struct pim_vxlan_sg *vxlan_sg = p;
     717             : 
     718           0 :         return pim_sgaddr_hash(vxlan_sg->sg, 0);
     719             : }
     720             : 
     721           0 : static bool pim_vxlan_sg_hash_eq(const void *p1, const void *p2)
     722             : {
     723           0 :         const struct pim_vxlan_sg *sg1 = p1;
     724           0 :         const struct pim_vxlan_sg *sg2 = p2;
     725             : 
     726           0 :         return !pim_sgaddr_cmp(sg1->sg, sg2->sg);
     727             : }
     728             : 
     729           0 : static struct pim_vxlan_sg *pim_vxlan_sg_new(struct pim_instance *pim,
     730             :                                              pim_sgaddr *sg)
     731             : {
     732           0 :         struct pim_vxlan_sg *vxlan_sg;
     733             : 
     734           0 :         vxlan_sg = XCALLOC(MTYPE_PIM_VXLAN_SG, sizeof(*vxlan_sg));
     735             : 
     736           0 :         vxlan_sg->pim = pim;
     737           0 :         vxlan_sg->sg = *sg;
     738           0 :         snprintfrr(vxlan_sg->sg_str, sizeof(vxlan_sg->sg_str), "%pSG", sg);
     739             : 
     740           0 :         if (PIM_DEBUG_VXLAN)
     741           0 :                 zlog_debug("vxlan SG %s alloc", vxlan_sg->sg_str);
     742             : 
     743           0 :         vxlan_sg = hash_get(pim->vxlan.sg_hash, vxlan_sg, hash_alloc_intern);
     744             : 
     745             :         /* we register with the MLAG daemon in the first VxLAN SG and never
     746             :          * de-register during that life of the pimd
     747             :          */
     748           0 :         if (pim->vxlan.sg_hash->count == 1) {
     749           0 :                 vxlan_mlag.flags |= PIM_VXLAN_MLAGF_DO_REG;
     750           0 :                 pim_mlag_register();
     751             :         }
     752             : 
     753           0 :         return vxlan_sg;
     754             : }
     755             : 
     756           6 : struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim, pim_sgaddr *sg)
     757             : {
     758           6 :         struct pim_vxlan_sg lookup;
     759             : 
     760           6 :         lookup.sg = *sg;
     761           6 :         return hash_lookup(pim->vxlan.sg_hash, &lookup);
     762             : }
     763             : 
     764           0 : struct pim_vxlan_sg *pim_vxlan_sg_add(struct pim_instance *pim, pim_sgaddr *sg)
     765             : {
     766           0 :         struct pim_vxlan_sg *vxlan_sg;
     767             : 
     768           0 :         vxlan_sg = pim_vxlan_sg_find(pim, sg);
     769           0 :         if (vxlan_sg)
     770             :                 return vxlan_sg;
     771             : 
     772           0 :         vxlan_sg = pim_vxlan_sg_new(pim, sg);
     773             : 
     774           0 :         if (pim_vxlan_is_orig_mroute(vxlan_sg))
     775           0 :                 pim_vxlan_orig_mr_add(vxlan_sg);
     776             :         else
     777           0 :                 pim_vxlan_term_mr_add(vxlan_sg);
     778             : 
     779             :         return vxlan_sg;
     780             : }
     781             : 
     782           0 : static void pim_vxlan_sg_del_item(struct pim_vxlan_sg *vxlan_sg)
     783             : {
     784           0 :         vxlan_sg->flags |= PIM_VXLAN_SGF_DEL_IN_PROG;
     785             : 
     786           0 :         pim_vxlan_del_work(vxlan_sg);
     787             : 
     788           0 :         if (pim_vxlan_is_orig_mroute(vxlan_sg))
     789           0 :                 pim_vxlan_orig_mr_del(vxlan_sg);
     790             :         else
     791           0 :                 pim_vxlan_term_mr_del(vxlan_sg);
     792             : 
     793           0 :         if (PIM_DEBUG_VXLAN)
     794           0 :                 zlog_debug("vxlan SG %s free", vxlan_sg->sg_str);
     795             : 
     796           0 :         XFREE(MTYPE_PIM_VXLAN_SG, vxlan_sg);
     797           0 : }
     798             : 
     799           0 : void pim_vxlan_sg_del(struct pim_instance *pim, pim_sgaddr *sg)
     800             : {
     801           0 :         struct pim_vxlan_sg *vxlan_sg;
     802             : 
     803           0 :         vxlan_sg = pim_vxlan_sg_find(pim, sg);
     804           0 :         if (!vxlan_sg)
     805             :                 return;
     806             : 
     807           0 :         hash_release(pim->vxlan.sg_hash, vxlan_sg);
     808           0 :         pim_vxlan_sg_del_item(vxlan_sg);
     809             : }
     810             : 
     811             : /******************************* MLAG handling *******************************/
     812           0 : bool pim_vxlan_do_mlag_reg(void)
     813             : {
     814           0 :         return (vxlan_mlag.flags & PIM_VXLAN_MLAGF_DO_REG);
     815             : }
     816             : 
     817             : /* The peerlink sub-interface is added as an OIF to the origination-mroute.
     818             :  * This is done to send a copy of the multicast-vxlan encapsulated traffic
     819             :  * to the MLAG peer which may mroute it over the underlay if there are any
     820             :  * interested receivers.
     821             :  */
     822           0 : static void pim_vxlan_sg_peerlink_oif_update(struct hash_bucket *bucket,
     823             :                                              void *arg)
     824             : {
     825           0 :         struct interface *new_oif = (struct interface *)arg;
     826           0 :         struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)bucket->data;
     827             : 
     828           0 :         if (!pim_vxlan_is_orig_mroute(vxlan_sg))
     829             :                 return;
     830             : 
     831           0 :         if (vxlan_sg->orig_oif == new_oif)
     832             :                 return;
     833             : 
     834           0 :         pim_vxlan_orig_mr_oif_del(vxlan_sg);
     835             : 
     836           0 :         vxlan_sg->orig_oif = new_oif;
     837           0 :         pim_vxlan_orig_mr_oif_add(vxlan_sg);
     838             : }
     839             : 
     840             : /* In the case of anycast VTEPs the VTEP-PIP must be used as the
     841             :  * register source.
     842             :  */
     843           0 : bool pim_vxlan_get_register_src(struct pim_instance *pim,
     844             :                 struct pim_upstream *up, struct in_addr *src_p)
     845             : {
     846           0 :         if (!(vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED))
     847             :                 return true;
     848             : 
     849             :         /* if address is not available suppress the pim-register */
     850           0 :         if (vxlan_mlag.reg_addr.s_addr == INADDR_ANY)
     851             :                 return false;
     852             : 
     853           0 :         *src_p = vxlan_mlag.reg_addr;
     854           0 :         return true;
     855             : }
     856             : 
     857           0 : void pim_vxlan_mlag_update(bool enable, bool peer_state, uint32_t role,
     858             :                                 struct interface *peerlink_rif,
     859             :                                 struct in_addr *reg_addr)
     860             : {
     861           0 :         struct pim_instance *pim;
     862           0 :         char addr_buf[INET_ADDRSTRLEN];
     863           0 :         struct pim_interface *pim_ifp = NULL;
     864             : 
     865           0 :         if (PIM_DEBUG_VXLAN) {
     866           0 :                 inet_ntop(AF_INET, reg_addr,
     867             :                                 addr_buf, INET_ADDRSTRLEN);
     868           0 :                 zlog_debug("vxlan MLAG update %s state %s role %d rif %s addr %s",
     869             :                                 enable ? "enable" : "disable",
     870             :                                 peer_state ? "up" : "down",
     871             :                                 role,
     872             :                                 peerlink_rif ? peerlink_rif->name : "-",
     873             :                                 addr_buf);
     874             :         }
     875             : 
     876             :         /* XXX: for now vxlan termination is only possible in the default VRF
     877             :          * when that changes this will need to change to iterate all VRFs
     878             :          */
     879           0 :         pim = pim_get_pim_instance(VRF_DEFAULT);
     880             : 
     881           0 :         if (!pim) {
     882           0 :                 if (PIM_DEBUG_VXLAN)
     883           0 :                         zlog_debug("%s: Unable to find pim instance", __func__);
     884           0 :                 return;
     885             :         }
     886             : 
     887           0 :         if (enable)
     888           0 :                 vxlan_mlag.flags |= PIM_VXLAN_MLAGF_ENABLED;
     889             :         else
     890           0 :                 vxlan_mlag.flags &= ~PIM_VXLAN_MLAGF_ENABLED;
     891             : 
     892           0 :         if (vxlan_mlag.peerlink_rif != peerlink_rif)
     893           0 :                 vxlan_mlag.peerlink_rif = peerlink_rif;
     894             : 
     895           0 :         vxlan_mlag.reg_addr = *reg_addr;
     896           0 :         vxlan_mlag.peer_state = peer_state;
     897           0 :         vxlan_mlag.role = role;
     898             : 
     899             :         /* process changes */
     900           0 :         if (vxlan_mlag.peerlink_rif)
     901           0 :                 pim_ifp = (struct pim_interface *)vxlan_mlag.peerlink_rif->info;
     902           0 :         if ((vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED) &&
     903           0 :                         pim_ifp && (pim_ifp->mroute_vif_index > 0))
     904           0 :                 pim_vxlan_set_peerlink_rif(pim, peerlink_rif);
     905             :         else
     906           0 :                 pim_vxlan_set_peerlink_rif(pim, NULL);
     907             : }
     908             : 
     909             : /****************************** misc callbacks *******************************/
     910          22 : static void pim_vxlan_set_default_iif(struct pim_instance *pim,
     911             :                                 struct interface *ifp)
     912             : {
     913          22 :         struct interface *old_iif;
     914             : 
     915          22 :         if (pim->vxlan.default_iif == ifp)
     916             :                 return;
     917             : 
     918          22 :         old_iif = pim->vxlan.default_iif;
     919          22 :         if (PIM_DEBUG_VXLAN)
     920           0 :                 zlog_debug("%s: vxlan default iif changed from %s to %s",
     921             :                            __func__, old_iif ? old_iif->name : "-",
     922             :                            ifp ? ifp->name : "-");
     923             : 
     924          22 :         old_iif = pim_vxlan_orig_mr_iif_get(pim);
     925          22 :         pim->vxlan.default_iif = ifp;
     926          22 :         ifp = pim_vxlan_orig_mr_iif_get(pim);
     927          22 :         if (old_iif == ifp)
     928             :                 return;
     929             : 
     930          22 :         if (PIM_DEBUG_VXLAN)
     931           0 :                 zlog_debug("%s: vxlan orig iif changed from %s to %s", __func__,
     932             :                            old_iif ? old_iif->name : "-",
     933             :                            ifp ? ifp->name : "-");
     934             : 
     935             :         /* add/del upstream entries for the existing vxlan SG when the
     936             :          * interface becomes available
     937             :          */
     938          22 :         if (pim->vxlan.sg_hash)
     939          11 :                 hash_iterate(pim->vxlan.sg_hash,
     940             :                                 pim_vxlan_orig_mr_iif_update, NULL);
     941             : }
     942             : 
     943           0 : static void pim_vxlan_up_cost_update(struct pim_instance *pim,
     944             :                 struct pim_upstream *up,
     945             :                 struct interface *old_peerlink_rif)
     946             : {
     947           0 :         if (!PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(up->flags))
     948             :                 return;
     949             : 
     950           0 :         if (up->rpf.source_nexthop.interface &&
     951             :                         ((up->rpf.source_nexthop.interface ==
     952           0 :                           pim->vxlan.peerlink_rif) ||
     953             :                          (up->rpf.source_nexthop.interface ==
     954             :                           old_peerlink_rif))) {
     955           0 :                 if (PIM_DEBUG_VXLAN)
     956           0 :                         zlog_debug("RPF cost adjust for %s on peerlink-rif (old: %s, new: %s) change",
     957             :                                         up->sg_str,
     958             :                                         old_peerlink_rif ?
     959             :                                         old_peerlink_rif->name : "-",
     960             :                                         pim->vxlan.peerlink_rif ?
     961             :                                         pim->vxlan.peerlink_rif->name : "-");
     962           0 :                 pim_mlag_up_local_add(pim, up);
     963             :         }
     964             : }
     965             : 
     966           0 : static void pim_vxlan_term_mr_cost_update(struct hash_bucket *bucket, void *arg)
     967             : {
     968           0 :         struct interface *old_peerlink_rif = (struct interface *)arg;
     969           0 :         struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)bucket->data;
     970           0 :         struct pim_upstream *up;
     971           0 :         struct listnode *listnode;
     972           0 :         struct pim_upstream *child;
     973             : 
     974           0 :         if (pim_vxlan_is_orig_mroute(vxlan_sg))
     975             :                 return;
     976             : 
     977             :         /* Lookup all XG and SG entries with RPF-interface peerlink_rif */
     978           0 :         up = vxlan_sg->up;
     979           0 :         if (!up)
     980             :                 return;
     981             : 
     982           0 :         pim_vxlan_up_cost_update(vxlan_sg->pim, up,
     983             :                         old_peerlink_rif);
     984             : 
     985           0 :         for (ALL_LIST_ELEMENTS_RO(up->sources, listnode,
     986             :                                 child))
     987           0 :                 pim_vxlan_up_cost_update(vxlan_sg->pim, child,
     988             :                                 old_peerlink_rif);
     989             : }
     990             : 
     991           0 : static void pim_vxlan_sg_peerlink_rif_update(struct hash_bucket *bucket,
     992             :                                              void *arg)
     993             : {
     994           0 :         pim_vxlan_orig_mr_iif_update(bucket, NULL);
     995           0 :         pim_vxlan_term_mr_cost_update(bucket, arg);
     996           0 : }
     997             : 
     998           0 : static void pim_vxlan_set_peerlink_rif(struct pim_instance *pim,
     999             :                         struct interface *ifp)
    1000             : {
    1001           0 :         struct interface *old_iif;
    1002           0 :         struct interface *new_iif;
    1003           0 :         struct interface *old_oif;
    1004           0 :         struct interface *new_oif;
    1005             : 
    1006           0 :         if (pim->vxlan.peerlink_rif == ifp)
    1007             :                 return;
    1008             : 
    1009           0 :         old_iif = pim->vxlan.peerlink_rif;
    1010           0 :         if (PIM_DEBUG_VXLAN)
    1011           0 :                 zlog_debug("%s: vxlan peerlink_rif changed from %s to %s",
    1012             :                            __func__, old_iif ? old_iif->name : "-",
    1013             :                            ifp ? ifp->name : "-");
    1014             : 
    1015           0 :         old_iif = pim_vxlan_orig_mr_iif_get(pim);
    1016           0 :         old_oif = pim_vxlan_orig_mr_oif_get(pim);
    1017           0 :         pim->vxlan.peerlink_rif = ifp;
    1018             : 
    1019           0 :         new_iif = pim_vxlan_orig_mr_iif_get(pim);
    1020           0 :         if (old_iif != new_iif) {
    1021           0 :                 if (PIM_DEBUG_VXLAN)
    1022           0 :                         zlog_debug("%s: vxlan orig iif changed from %s to %s",
    1023             :                                    __func__, old_iif ? old_iif->name : "-",
    1024             :                                    new_iif ? new_iif->name : "-");
    1025             : 
    1026             :                 /* add/del upstream entries for the existing vxlan SG when the
    1027             :                  * interface becomes available
    1028             :                  */
    1029           0 :                 if (pim->vxlan.sg_hash)
    1030           0 :                         hash_iterate(pim->vxlan.sg_hash,
    1031             :                                         pim_vxlan_sg_peerlink_rif_update,
    1032             :                                         old_iif);
    1033             :         }
    1034             : 
    1035           0 :         new_oif = pim_vxlan_orig_mr_oif_get(pim);
    1036           0 :         if (old_oif != new_oif) {
    1037           0 :                 if (PIM_DEBUG_VXLAN)
    1038           0 :                         zlog_debug("%s: vxlan orig oif changed from %s to %s",
    1039             :                                    __func__, old_oif ? old_oif->name : "-",
    1040             :                                    new_oif ? new_oif->name : "-");
    1041           0 :                 if (pim->vxlan.sg_hash)
    1042           0 :                         hash_iterate(pim->vxlan.sg_hash,
    1043             :                                         pim_vxlan_sg_peerlink_oif_update,
    1044             :                                         new_oif);
    1045             :         }
    1046             : }
    1047             : 
    1048           0 : static void pim_vxlan_term_mr_oif_update(struct hash_bucket *bucket, void *arg)
    1049             : {
    1050           0 :         struct interface *ifp = (struct interface *)arg;
    1051           0 :         struct pim_vxlan_sg *vxlan_sg = (struct pim_vxlan_sg *)bucket->data;
    1052             : 
    1053           0 :         if (pim_vxlan_is_orig_mroute(vxlan_sg))
    1054             :                 return;
    1055             : 
    1056           0 :         if (vxlan_sg->term_oif == ifp)
    1057             :                 return;
    1058             : 
    1059           0 :         if (PIM_DEBUG_VXLAN)
    1060           0 :                 zlog_debug("vxlan SG %s term oif changed from %s to %s",
    1061             :                         vxlan_sg->sg_str,
    1062             :                         vxlan_sg->term_oif ? vxlan_sg->term_oif->name : "-",
    1063             :                         ifp ? ifp->name : "-");
    1064             : 
    1065           0 :         pim_vxlan_term_mr_del(vxlan_sg);
    1066           0 :         vxlan_sg->term_oif = ifp;
    1067           0 :         pim_vxlan_term_mr_add(vxlan_sg);
    1068             : }
    1069             : 
    1070           0 : static void pim_vxlan_term_oif_update(struct pim_instance *pim,
    1071             :                 struct interface *ifp)
    1072             : {
    1073           0 :         if (pim->vxlan.term_if == ifp)
    1074             :                 return;
    1075             : 
    1076           0 :         if (PIM_DEBUG_VXLAN)
    1077           0 :                 zlog_debug("vxlan term oif changed from %s to %s",
    1078             :                         pim->vxlan.term_if ? pim->vxlan.term_if->name : "-",
    1079             :                         ifp ? ifp->name : "-");
    1080             : 
    1081           0 :         pim->vxlan.term_if = ifp;
    1082           0 :         if (pim->vxlan.sg_hash)
    1083           0 :                 hash_iterate(pim->vxlan.sg_hash,
    1084             :                                 pim_vxlan_term_mr_oif_update, ifp);
    1085             : }
    1086             : 
    1087         104 : void pim_vxlan_add_vif(struct interface *ifp)
    1088             : {
    1089         104 :         struct pim_interface *pim_ifp = ifp->info;
    1090         104 :         struct pim_instance *pim = pim_ifp->pim;
    1091             : 
    1092         104 :         if (pim->vrf->vrf_id != VRF_DEFAULT)
    1093             :                 return;
    1094             : 
    1095         104 :         if (if_is_loopback(ifp))
    1096          11 :                 pim_vxlan_set_default_iif(pim, ifp);
    1097             : 
    1098         104 :         if (vxlan_mlag.flags & PIM_VXLAN_MLAGF_ENABLED &&
    1099           0 :                         (ifp == vxlan_mlag.peerlink_rif))
    1100           0 :                 pim_vxlan_set_peerlink_rif(pim, ifp);
    1101             : 
    1102         104 :         if (pim->vxlan.term_if_cfg == ifp)
    1103           0 :                 pim_vxlan_term_oif_update(pim, ifp);
    1104             : }
    1105             : 
    1106          78 : void pim_vxlan_del_vif(struct interface *ifp)
    1107             : {
    1108          78 :         struct pim_interface *pim_ifp = ifp->info;
    1109          78 :         struct pim_instance *pim = pim_ifp->pim;
    1110             : 
    1111          78 :         if (pim->vrf->vrf_id != VRF_DEFAULT)
    1112             :                 return;
    1113             : 
    1114          78 :         if (pim->vxlan.default_iif == ifp)
    1115          11 :                 pim_vxlan_set_default_iif(pim, NULL);
    1116             : 
    1117          78 :         if (pim->vxlan.peerlink_rif == ifp)
    1118           0 :                 pim_vxlan_set_peerlink_rif(pim, NULL);
    1119             : 
    1120          78 :         if (pim->vxlan.term_if == ifp)
    1121           0 :                 pim_vxlan_term_oif_update(pim, NULL);
    1122             : }
    1123             : 
    1124             : /* enable pim implicitly on the termination device add */
    1125           0 : void pim_vxlan_add_term_dev(struct pim_instance *pim,
    1126             :                 struct interface *ifp)
    1127             : {
    1128           0 :         struct pim_interface *pim_ifp;
    1129             : 
    1130           0 :         if (pim->vxlan.term_if_cfg == ifp)
    1131             :                 return;
    1132             : 
    1133           0 :         if (PIM_DEBUG_VXLAN)
    1134           0 :                 zlog_debug("vxlan term oif cfg changed from %s to %s",
    1135             :                            pim->vxlan.term_if_cfg ?
    1136             :                            pim->vxlan.term_if_cfg->name : "-",
    1137             :                            ifp->name);
    1138             : 
    1139           0 :         pim->vxlan.term_if_cfg = ifp;
    1140             : 
    1141             :         /* enable pim on the term ifp */
    1142           0 :         pim_ifp = (struct pim_interface *)ifp->info;
    1143           0 :         if (pim_ifp) {
    1144           0 :                 pim_ifp->pim_enable = true;
    1145             :                 /* ifp is already oper up; activate it as a term dev */
    1146           0 :                 if (pim_ifp->mroute_vif_index >= 0)
    1147           0 :                         pim_vxlan_term_oif_update(pim, ifp);
    1148             :         } else {
    1149             :                 /* ensure that pimreg exists before using the newly created
    1150             :                  * vxlan termination device
    1151             :                  */
    1152           0 :                 pim_if_create_pimreg(pim);
    1153           0 :                 (void)pim_if_new(ifp, false /*igmp*/, true /*pim*/,
    1154             :                                  false /*pimreg*/, true /*vxlan_term*/);
    1155             :         }
    1156             : }
    1157             : 
    1158             : /* disable pim implicitly, if needed, on the termination device deletion */
    1159           0 : void pim_vxlan_del_term_dev(struct pim_instance *pim)
    1160             : {
    1161           0 :         struct interface *ifp = pim->vxlan.term_if_cfg;
    1162           0 :         struct pim_interface *pim_ifp;
    1163             : 
    1164           0 :         if (PIM_DEBUG_VXLAN)
    1165           0 :                 zlog_debug("vxlan term oif cfg changed from %s to -",
    1166             :                                 ifp->name);
    1167             : 
    1168           0 :         pim->vxlan.term_if_cfg = NULL;
    1169             : 
    1170           0 :         pim_ifp = (struct pim_interface *)ifp->info;
    1171           0 :         if (pim_ifp) {
    1172           0 :                 pim_ifp->pim_enable = false;
    1173           0 :                 if (!pim_ifp->gm_enable)
    1174           0 :                         pim_if_delete(ifp);
    1175             :         }
    1176           0 : }
    1177             : 
    1178          26 : void pim_vxlan_init(struct pim_instance *pim)
    1179             : {
    1180          26 :         char hash_name[64];
    1181             : 
    1182          26 :         snprintf(hash_name, sizeof(hash_name),
    1183          26 :                 "PIM %s vxlan SG hash", pim->vrf->name);
    1184          26 :         pim->vxlan.sg_hash = hash_create(pim_vxlan_sg_hash_key_make,
    1185             :                         pim_vxlan_sg_hash_eq, hash_name);
    1186          26 : }
    1187             : 
    1188          26 : void pim_vxlan_exit(struct pim_instance *pim)
    1189             : {
    1190          26 :         if (pim->vxlan.sg_hash) {
    1191          26 :                 hash_clean(pim->vxlan.sg_hash,
    1192             :                            (void (*)(void *))pim_vxlan_sg_del_item);
    1193          26 :                 hash_free(pim->vxlan.sg_hash);
    1194          26 :                 pim->vxlan.sg_hash = NULL;
    1195             :         }
    1196          26 : }
    1197             : 
    1198          18 : void pim_vxlan_terminate(void)
    1199             : {
    1200          18 :         pim_vxlan_work_timer_setup(false);
    1201          18 : }

Generated by: LCOV version v1.16-topotato