Coverage Report

Created: 2025-08-28 06:29

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