back to topotato report
topotato coverage report
Current view: top level - pimd - pim_jp_agg.c (source / functions) Hit Total Coverage
Test: test_mld_basic.py::MLDBasic Lines: 9 138 6.5 %
Date: 2023-02-24 18:38:01 Functions: 2 12 16.7 %

          Line data    Source code
       1             : /*
       2             :  * PIM for FRR - J/P Aggregation
       3             :  * Copyright (C) 2017 Cumulus Networks, Inc.
       4             :  * Donald Sharp
       5             :  *
       6             :  * This program is free software; you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation; either version 2 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * This program is distributed in the hope that it will be useful, but
      12             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License along
      17             :  * with this program; see the file COPYING; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : #include <zebra.h>
      21             : 
      22             : #include "linklist.h"
      23             : #include "log.h"
      24             : #include "vrf.h"
      25             : #include "if.h"
      26             : 
      27             : #include "pimd.h"
      28             : #include "pim_instance.h"
      29             : #include "pim_msg.h"
      30             : #include "pim_jp_agg.h"
      31             : #include "pim_join.h"
      32             : #include "pim_iface.h"
      33             : 
      34           0 : void pim_jp_agg_group_list_free(struct pim_jp_agg_group *jag)
      35             : {
      36           0 :         list_delete(&jag->sources);
      37             : 
      38           0 :         XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
      39           0 : }
      40             : 
      41           0 : static void pim_jp_agg_src_free(struct pim_jp_sources *js)
      42             : {
      43           0 :         struct pim_upstream *up = js->up;
      44             : 
      45             :         /*
      46             :          * When we are being called here, we know
      47             :          * that the neighbor is going away start
      48             :          * the normal j/p timer so that it can
      49             :          * pick this shit back up when the
      50             :          * nbr comes back alive
      51             :          */
      52           0 :         if (up)
      53           0 :                 join_timer_start(js->up);
      54           0 :         XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
      55           0 : }
      56             : 
      57           0 : int pim_jp_agg_group_list_cmp(void *arg1, void *arg2)
      58             : {
      59           0 :         const struct pim_jp_agg_group *jag1 =
      60             :                 (const struct pim_jp_agg_group *)arg1;
      61           0 :         const struct pim_jp_agg_group *jag2 =
      62             :                 (const struct pim_jp_agg_group *)arg2;
      63             : 
      64           0 :         return pim_addr_cmp(jag1->group, jag2->group);
      65             : }
      66             : 
      67           0 : static int pim_jp_agg_src_cmp(void *arg1, void *arg2)
      68             : {
      69           0 :         const struct pim_jp_sources *js1 = (const struct pim_jp_sources *)arg1;
      70           0 :         const struct pim_jp_sources *js2 = (const struct pim_jp_sources *)arg2;
      71             : 
      72           0 :         if (js1->is_join && !js2->is_join)
      73             :                 return -1;
      74             : 
      75           0 :         if (!js1->is_join && js2->is_join)
      76             :                 return 1;
      77             : 
      78           0 :         return pim_addr_cmp(js1->up->sg.src, js2->up->sg.src);
      79             : }
      80             : 
      81             : /*
      82             :  * This function is used by scan_oil to clear
      83             :  * the created jp_agg_group created when
      84             :  * figuring out where to send prunes
      85             :  * and joins.
      86             :  */
      87           0 : void pim_jp_agg_clear_group(struct list *group)
      88             : {
      89           0 :         struct listnode *gnode, *gnnode;
      90           0 :         struct listnode *snode, *snnode;
      91           0 :         struct pim_jp_agg_group *jag;
      92           0 :         struct pim_jp_sources *js;
      93             : 
      94           0 :         for (ALL_LIST_ELEMENTS(group, gnode, gnnode, jag)) {
      95           0 :                 for (ALL_LIST_ELEMENTS(jag->sources, snode, snnode, js)) {
      96           0 :                         listnode_delete(jag->sources, js);
      97           0 :                         js->up = NULL;
      98           0 :                         XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
      99             :                 }
     100           0 :                 list_delete(&jag->sources);
     101           0 :                 listnode_delete(group, jag);
     102           0 :                 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
     103             :         }
     104           0 : }
     105             : 
     106             : static struct pim_iface_upstream_switch *
     107           0 : pim_jp_agg_get_interface_upstream_switch_list(struct pim_rpf *rpf)
     108             : {
     109           0 :         struct interface *ifp = rpf->source_nexthop.interface;
     110           0 :         struct pim_interface *pim_ifp;
     111           0 :         struct pim_iface_upstream_switch *pius;
     112           0 :         struct listnode *node, *nnode;
     113             : 
     114           0 :         if (!ifp)
     115             :                 return NULL;
     116             : 
     117           0 :         pim_ifp = ifp->info;
     118             : 
     119             :         /* Old interface is pim disabled */
     120           0 :         if (!pim_ifp)
     121             :                 return NULL;
     122             : 
     123           0 :         for (ALL_LIST_ELEMENTS(pim_ifp->upstream_switch_list, node, nnode,
     124             :                                pius)) {
     125           0 :                 if (!pim_addr_cmp(pius->address, rpf->rpf_addr))
     126             :                         break;
     127             :         }
     128             : 
     129           0 :         if (!pius) {
     130           0 :                 pius = XCALLOC(MTYPE_PIM_JP_AGG_GROUP,
     131             :                                sizeof(struct pim_iface_upstream_switch));
     132           0 :                 pius->address = rpf->rpf_addr;
     133           0 :                 pius->us = list_new();
     134           0 :                 listnode_add_sort(pim_ifp->upstream_switch_list, pius);
     135             :         }
     136             : 
     137             :         return pius;
     138             : }
     139             : 
     140           0 : void pim_jp_agg_remove_group(struct list *group, struct pim_upstream *up,
     141             :                 struct pim_neighbor *nbr)
     142             : {
     143           0 :         struct listnode *node, *nnode;
     144           0 :         struct pim_jp_agg_group *jag = NULL;
     145           0 :         struct pim_jp_sources *js = NULL;
     146             : 
     147           0 :         for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
     148           0 :                 if (!pim_addr_cmp(jag->group, up->sg.grp))
     149             :                         break;
     150             :         }
     151             : 
     152           0 :         if (!jag)
     153             :                 return;
     154             : 
     155           0 :         for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
     156           0 :                 if (js->up == up)
     157             :                         break;
     158             :         }
     159             : 
     160           0 :         if (nbr) {
     161           0 :                 if (PIM_DEBUG_TRACE)
     162           0 :                         zlog_debug("up %s remove from nbr %s/%pPAs jp-agg-list",
     163             :                                    up->sg_str, nbr->interface->name,
     164             :                                    &nbr->source_addr);
     165             :         }
     166             : 
     167           0 :         if (js) {
     168           0 :                 js->up = NULL;
     169           0 :                 listnode_delete(jag->sources, js);
     170           0 :                 XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
     171             :         }
     172             : 
     173           0 :         if (jag->sources->count == 0) {
     174           0 :                 list_delete(&jag->sources);
     175           0 :                 listnode_delete(group, jag);
     176           0 :                 XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
     177             :         }
     178             : }
     179             : 
     180           0 : int pim_jp_agg_is_in_list(struct list *group, struct pim_upstream *up)
     181             : {
     182           0 :         struct listnode *node, *nnode;
     183           0 :         struct pim_jp_agg_group *jag = NULL;
     184           0 :         struct pim_jp_sources *js = NULL;
     185             : 
     186           0 :         for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
     187           0 :                 if (!pim_addr_cmp(jag->group, up->sg.grp))
     188             :                         break;
     189             :         }
     190             : 
     191           0 :         if (!jag)
     192             :                 return 0;
     193             : 
     194           0 :         for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
     195           0 :                 if (js->up == up)
     196             :                         return 1;
     197             :         }
     198             : 
     199             :         return 0;
     200             : }
     201             : 
     202             : //#define PIM_JP_AGG_DEBUG 1
     203             : /*
     204             :  * For the given upstream, check all the neighbor
     205             :  * jp_agg lists and ensure that it is not
     206             :  * in another list
     207             :  *
     208             :  * *IF* ignore is true we can skip
     209             :  * up->rpf.source_nexthop.interface particular interface for checking
     210             :  *
     211             :  * This is a debugging function, Probably
     212             :  * can be safely compiled out in real
     213             :  * builds
     214             :  */
     215           4 : void pim_jp_agg_upstream_verification(struct pim_upstream *up, bool ignore)
     216             : {
     217             : #ifdef PIM_JP_AGG_DEBUG
     218             :         struct interface *ifp;
     219             :         struct pim_interface *pim_ifp;
     220             :         struct pim_instance *pim;
     221             : 
     222             :         if (!up->rpf.source_nexthop.interface) {
     223             :                 if (PIM_DEBUG_PIM_TRACE)
     224             :                         zlog_debug("%s: up %s RPF is not present", __func__,
     225             :                                    up->sg_str);
     226             :                 return;
     227             :         }
     228             : 
     229             :         pim_ifp = up->rpf.source_nexthop.interface->info;
     230             :         pim = pim_ifp->pim;
     231             : 
     232             :         FOR_ALL_INTERFACES (pim->vrf, ifp) {
     233             :                 pim_ifp = ifp->info;
     234             :                 struct listnode *nnode;
     235             : 
     236             :                 if (ignore && ifp == up->rpf.source_nexthop.interface)
     237             :                         continue;
     238             : 
     239             :                 if (pim_ifp) {
     240             :                         struct pim_neighbor *neigh;
     241             :                         for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list,
     242             :                                                   nnode, neigh)) {
     243             :                                 assert(!pim_jp_agg_is_in_list(
     244             :                                         neigh->upstream_jp_agg, up));
     245             :                         }
     246             :                 }
     247             :         }
     248             : #else
     249           4 :         return;
     250             : #endif
     251             : }
     252             : 
     253           0 : void pim_jp_agg_add_group(struct list *group, struct pim_upstream *up,
     254             :                           bool is_join, struct pim_neighbor *nbr)
     255             : {
     256           0 :         struct listnode *node, *nnode;
     257           0 :         struct pim_jp_agg_group *jag = NULL;
     258           0 :         struct pim_jp_sources *js = NULL;
     259             : 
     260           0 :         for (ALL_LIST_ELEMENTS(group, node, nnode, jag)) {
     261           0 :                 if (!pim_addr_cmp(jag->group, up->sg.grp))
     262             :                         break;
     263             :         }
     264             : 
     265           0 :         if (!jag) {
     266           0 :                 jag = XCALLOC(MTYPE_PIM_JP_AGG_GROUP,
     267             :                               sizeof(struct pim_jp_agg_group));
     268           0 :                 jag->group = up->sg.grp;
     269           0 :                 jag->sources = list_new();
     270           0 :                 jag->sources->cmp = pim_jp_agg_src_cmp;
     271           0 :                 jag->sources->del = (void (*)(void *))pim_jp_agg_src_free;
     272           0 :                 listnode_add_sort(group, jag);
     273             :         }
     274             : 
     275           0 :         for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js)) {
     276           0 :                 if (js->up == up)
     277             :                         break;
     278             :         }
     279             : 
     280           0 :         if (nbr) {
     281           0 :                 if (PIM_DEBUG_TRACE)
     282           0 :                         zlog_debug("up %s add to nbr %s/%pPAs jp-agg-list",
     283             :                                    up->sg_str,
     284             :                                    up->rpf.source_nexthop.interface->name,
     285             :                                    &nbr->source_addr);
     286             :         }
     287             : 
     288           0 :         if (!js) {
     289           0 :                 js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE,
     290             :                              sizeof(struct pim_jp_sources));
     291           0 :                 js->up = up;
     292           0 :                 js->is_join = is_join;
     293           0 :                 listnode_add_sort(jag->sources, js);
     294             :         } else {
     295           0 :                 if (js->is_join != is_join) {
     296           0 :                         listnode_delete(jag->sources, js);
     297           0 :                         js->is_join = is_join;
     298           0 :                         listnode_add_sort(jag->sources, js);
     299             :                 }
     300             :         }
     301           0 : }
     302             : 
     303           0 : void pim_jp_agg_switch_interface(struct pim_rpf *orpf, struct pim_rpf *nrpf,
     304             :                                  struct pim_upstream *up)
     305             : {
     306           0 :         struct pim_iface_upstream_switch *opius;
     307           0 :         struct pim_iface_upstream_switch *npius;
     308             : 
     309           0 :         opius = pim_jp_agg_get_interface_upstream_switch_list(orpf);
     310           0 :         npius = pim_jp_agg_get_interface_upstream_switch_list(nrpf);
     311             : 
     312             :         /*
     313             :          * RFC 4601: 4.5.7.  Sending (S,G) Join/Prune Messages
     314             :          *
     315             :          * Transitions from Joined State
     316             :          *
     317             :          * RPF'(S,G) changes not due to an Assert
     318             :          *
     319             :          * The upstream (S,G) state machine remains in Joined
     320             :          * state. Send Join(S,G) to the new upstream neighbor, which is
     321             :          * the new value of RPF'(S,G).  Send Prune(S,G) to the old
     322             :          * upstream neighbor, which is the old value of RPF'(S,G).  Set
     323             :          * the Join Timer (JT) to expire after t_periodic seconds.
     324             :          */
     325             : 
     326             :         /* send Prune(S,G) to the old upstream neighbor */
     327           0 :         if (opius)
     328           0 :                 pim_jp_agg_add_group(opius->us, up, false, NULL);
     329             : 
     330             :         /* send Join(S,G) to the current upstream neighbor */
     331           0 :         if (npius)
     332           0 :                 pim_jp_agg_add_group(npius->us, up, true, NULL);
     333           0 : }
     334             : 
     335             : 
     336           1 : void pim_jp_agg_single_upstream_send(struct pim_rpf *rpf,
     337             :                                      struct pim_upstream *up, bool is_join)
     338             : {
     339           1 :         struct list groups, sources;
     340           1 :         struct pim_jp_agg_group jag;
     341           1 :         struct pim_jp_sources js;
     342             : 
     343             :         /* skip JP upstream messages if source is directly connected */
     344           2 :         if (!up || !rpf->source_nexthop.interface ||
     345           1 :                 pim_if_connected_to_source(rpf->source_nexthop.interface,
     346           0 :                         up->sg.src) ||
     347           0 :                 if_is_loopback(rpf->source_nexthop.interface))
     348           1 :                 return;
     349             : 
     350           0 :         memset(&groups, 0, sizeof(groups));
     351           0 :         memset(&sources, 0, sizeof(sources));
     352           0 :         jag.sources = &sources;
     353             : 
     354           0 :         listnode_add(&groups, &jag);
     355           0 :         listnode_add(jag.sources, &js);
     356             : 
     357           0 :         jag.group = up->sg.grp;
     358           0 :         js.up = up;
     359           0 :         js.is_join = is_join;
     360             : 
     361           0 :         pim_joinprune_send(rpf, &groups);
     362             : 
     363           0 :         list_delete_all_node(jag.sources);
     364           0 :         list_delete_all_node(&groups);
     365             : }

Generated by: LCOV version v1.16-topotato