Coverage Report

Created: 2026-03-12 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/pimd/pim_ifchannel.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * PIM for Quagga
4
 * Copyright (C) 2008  Everton da Silva Marques
5
 */
6
7
#include <zebra.h>
8
9
#include "linklist.h"
10
#include "frrevent.h"
11
#include "memory.h"
12
#include "if.h"
13
#include "vrf.h"
14
#include "hash.h"
15
#include "jhash.h"
16
#include "prefix.h"
17
18
#include "pimd.h"
19
#include "pim_instance.h"
20
#include "pim_str.h"
21
#include "pim_iface.h"
22
#include "pim_ifchannel.h"
23
#include "pim_zebra.h"
24
#include "pim_time.h"
25
#include "pim_msg.h"
26
#include "pim_pim.h"
27
#include "pim_join.h"
28
#include "pim_rpf.h"
29
#include "pim_macro.h"
30
#include "pim_oil.h"
31
#include "pim_upstream.h"
32
#include "pim_ssm.h"
33
#include "pim_rp.h"
34
#include "pim_mlag.h"
35
36
RB_GENERATE(pim_ifchannel_rb, pim_ifchannel, pim_ifp_rb, pim_ifchannel_compare);
37
38
int pim_ifchannel_compare(const struct pim_ifchannel *ch1,
39
        const struct pim_ifchannel *ch2)
40
3.07M
{
41
3.07M
  struct pim_interface *pim_ifp1;
42
3.07M
  struct pim_interface *pim_ifp2;
43
44
3.07M
  pim_ifp1 = ch1->interface->info;
45
3.07M
  pim_ifp2 = ch2->interface->info;
46
47
3.07M
  if (pim_ifp1->mroute_vif_index < pim_ifp2->mroute_vif_index)
48
0
    return -1;
49
50
3.07M
  if (pim_ifp1->mroute_vif_index > pim_ifp2->mroute_vif_index)
51
0
    return 1;
52
53
3.07M
  return pim_sgaddr_cmp(ch1->sg, ch2->sg);
54
3.07M
}
55
56
/*
57
 * A (*,G) or a (*,*) is going away
58
 * remove the parent pointer from
59
 * those pointing at us
60
 */
61
static void pim_ifchannel_remove_children(struct pim_ifchannel *ch)
62
42.5k
{
63
42.5k
  struct pim_ifchannel *child;
64
65
42.5k
  if (!ch->sources)
66
42.5k
    return;
67
68
0
  while (!list_isempty(ch->sources)) {
69
0
    child = listnode_head(ch->sources);
70
0
    child->parent = NULL;
71
0
    listnode_delete(ch->sources, child);
72
0
  }
73
0
}
74
75
/*
76
 * A (*,G) or a (*,*) is being created
77
 * find all the children that would point
78
 * at us.
79
 */
80
static void pim_ifchannel_find_new_children(struct pim_ifchannel *ch)
81
43.2k
{
82
43.2k
  struct pim_interface *pim_ifp = ch->interface->info;
83
43.2k
  struct pim_ifchannel *child;
84
85
  // Basic Sanity that we are not being silly
86
43.2k
  if (!pim_addr_is_any(ch->sg.src) && !pim_addr_is_any(ch->sg.grp))
87
43.0k
    return;
88
89
214
  if (pim_addr_is_any(ch->sg.src) && pim_addr_is_any(ch->sg.grp))
90
1
    return;
91
92
72.8k
  RB_FOREACH (child, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
93
72.8k
    if (!pim_addr_is_any(ch->sg.grp) &&
94
58.4k
        !pim_addr_cmp(child->sg.grp, ch->sg.grp) && (child != ch)) {
95
232
      child->parent = ch;
96
232
      listnode_add_sort(ch->sources, child);
97
232
    }
98
72.8k
  }
99
213
}
100
101
void pim_ifchannel_delete(struct pim_ifchannel *ch)
102
42.5k
{
103
42.5k
  struct pim_interface *pim_ifp;
104
42.5k
  struct pim_upstream *up;
105
106
42.5k
  pim_ifp = ch->interface->info;
107
108
42.5k
  if (PIM_DEBUG_PIM_TRACE)
109
0
    zlog_debug("%s: ifchannel entry %s(%s) del start", __func__,
110
42.5k
         ch->sg_str, ch->interface->name);
111
112
42.5k
  if (PIM_I_am_DualActive(pim_ifp)) {
113
0
    if (PIM_DEBUG_MLAG)
114
0
      zlog_debug(
115
0
        "%s: if-chnanel-%s is deleted from a Dual active Interface",
116
0
        __func__, ch->sg_str);
117
    /* Post Delete only if it is the last Dual-active Interface */
118
0
    if (ch->upstream->dualactive_ifchannel_count == 1) {
119
0
      pim_mlag_up_local_del(pim_ifp->pim, ch->upstream);
120
0
      PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(
121
0
        ch->upstream->flags);
122
0
    }
123
0
    ch->upstream->dualactive_ifchannel_count--;
124
0
  }
125
126
42.5k
  if (ch->upstream->channel_oil) {
127
42.5k
    uint32_t mask = PIM_OIF_FLAG_PROTO_PIM;
128
42.5k
    if (ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
129
0
      mask |= PIM_OIF_FLAG_PROTO_GM;
130
131
    /*
132
     * A S,G RPT channel can have an empty oil, we also
133
     * need to take into account the fact that a ifchannel
134
     * might have been suppressing a *,G ifchannel from
135
     * being inherited.  So let's figure out what
136
     * needs to be done here
137
     */
138
42.5k
    if (!pim_addr_is_any(ch->sg.src) &&
139
42.5k
        pim_upstream_evaluate_join_desired_interface(
140
42.5k
          ch->upstream, ch, ch->parent))
141
42.5k
      pim_channel_add_oif(ch->upstream->channel_oil,
142
42.5k
          ch->interface,
143
42.5k
          PIM_OIF_FLAG_PROTO_STAR,
144
42.5k
          __func__);
145
146
42.5k
    pim_channel_del_oif(ch->upstream->channel_oil,
147
42.5k
          ch->interface, mask, __func__);
148
    /*
149
     * Do we have any S,G's that are inheriting?
150
     * Nuke from on high too.
151
     */
152
42.5k
    if (ch->upstream->sources) {
153
0
      struct pim_upstream *child;
154
0
      struct listnode *up_node;
155
156
0
      for (ALL_LIST_ELEMENTS_RO(ch->upstream->sources,
157
0
              up_node, child))
158
0
        pim_channel_del_inherited_oif(
159
0
            child->channel_oil,
160
0
            ch->interface,
161
0
            __func__);
162
0
    }
163
42.5k
  }
164
165
  /*
166
   * When this channel is removed
167
   * we need to find all our children
168
   * and make sure our pointers are fixed
169
   */
170
42.5k
  pim_ifchannel_remove_children(ch);
171
172
42.5k
  if (ch->sources)
173
0
    list_delete(&ch->sources);
174
175
42.5k
  listnode_delete(ch->upstream->ifchannels, ch);
176
177
42.5k
  up = ch->upstream;
178
179
  /* upstream is common across ifchannels, check if upstream's
180
     ifchannel list is empty before deleting upstream_del
181
     ref count will take care of it.
182
  */
183
42.5k
  if (ch->upstream->ref_count > 0)
184
42.5k
    up = pim_upstream_del(pim_ifp->pim, ch->upstream, __func__);
185
186
0
  else {
187
0
    if (PIM_DEBUG_PIM_TRACE)
188
0
      zlog_debug(
189
0
        "%s: Avoiding deletion of upstream with ref_count %d from ifchannel(%s): %s",
190
0
        __func__, ch->upstream->ref_count,
191
0
        ch->interface->name, ch->sg_str);
192
0
  }
193
194
42.5k
  ch->upstream = NULL;
195
196
42.5k
  EVENT_OFF(ch->t_ifjoin_expiry_timer);
197
42.5k
  EVENT_OFF(ch->t_ifjoin_prune_pending_timer);
198
42.5k
  EVENT_OFF(ch->t_ifassert_timer);
199
200
42.5k
  if (ch->parent) {
201
42.5k
    listnode_delete(ch->parent->sources, ch);
202
42.5k
    ch->parent = NULL;
203
42.5k
  }
204
205
42.5k
  RB_REMOVE(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
206
207
42.5k
  if (PIM_DEBUG_PIM_TRACE)
208
0
    zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__,
209
42.5k
         ch->sg_str, ch->interface->name);
210
211
42.5k
  XFREE(MTYPE_PIM_IFCHANNEL, ch);
212
213
42.5k
  if (up)
214
0
    pim_upstream_update_join_desired(pim_ifp->pim, up);
215
42.5k
}
216
217
void pim_ifchannel_delete_all(struct interface *ifp)
218
0
{
219
0
  struct pim_interface *pim_ifp;
220
0
  struct pim_ifchannel *ch;
221
222
0
  pim_ifp = ifp->info;
223
0
  if (!pim_ifp)
224
0
    return;
225
226
0
  while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
227
0
    ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
228
229
0
    pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO);
230
0
    pim_ifchannel_delete(ch);
231
0
  }
232
0
}
233
234
void delete_on_noinfo(struct pim_ifchannel *ch)
235
42.5k
{
236
42.5k
  if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO
237
42.5k
      && ch->ifjoin_state == PIM_IFJOIN_NOINFO
238
42.5k
      && ch->t_ifjoin_expiry_timer == NULL)
239
42.5k
    pim_ifchannel_delete(ch);
240
42.5k
}
241
242
void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch,
243
         enum pim_ifjoin_state new_state)
244
8.25k
{
245
8.25k
  enum pim_ifjoin_state old_state = ch->ifjoin_state;
246
8.25k
  struct pim_interface *pim_ifp = ch->interface->info;
247
8.25k
  struct pim_ifchannel *child_ch;
248
249
8.25k
  if (PIM_DEBUG_PIM_EVENTS)
250
0
    zlog_debug(
251
8.25k
      "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
252
8.25k
      ch->interface->name, ch->sg_str,
253
8.25k
      pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
254
8.25k
      pim_ifchannel_ifjoin_name(new_state, 0));
255
256
257
8.25k
  if (old_state == new_state) {
258
0
    if (PIM_DEBUG_PIM_EVENTS) {
259
0
      zlog_debug(
260
0
        "%s called by %s: non-transition on state %d (%s)",
261
0
        __func__, caller, new_state,
262
0
        pim_ifchannel_ifjoin_name(new_state, 0));
263
0
    }
264
0
    return;
265
0
  }
266
267
8.25k
  ch->ifjoin_state = new_state;
268
269
8.25k
  if (pim_addr_is_any(ch->sg.src)) {
270
4.81k
    struct pim_upstream *up = ch->upstream;
271
4.81k
    struct pim_upstream *child;
272
4.81k
    struct listnode *up_node;
273
274
4.81k
    if (up) {
275
4.81k
      if (ch->ifjoin_state == PIM_IFJOIN_NOINFO) {
276
0
        for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
277
0
                child)) {
278
0
          struct channel_oil *c_oil =
279
0
            child->channel_oil;
280
281
0
          if (PIM_DEBUG_PIM_TRACE)
282
0
            zlog_debug(
283
0
              "%s %s: Prune(S,G)=%s from %s",
284
0
              __FILE__, __func__,
285
0
              child->sg_str,
286
0
              up->sg_str);
287
0
          if (!c_oil)
288
0
            continue;
289
290
          /*
291
           * If the S,G has no if channel and the
292
           * c_oil still
293
           * has output here then the *,G was
294
           * supplying the implied
295
           * if channel.  So remove it.
296
           */
297
0
          if (oil_if_has(c_oil,
298
0
                   pim_ifp->mroute_vif_index))
299
0
            pim_channel_del_inherited_oif(
300
0
              c_oil, ch->interface,
301
0
              __func__);
302
0
        }
303
0
      }
304
4.81k
      if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
305
2.42k
        for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
306
12.6k
                child)) {
307
12.6k
          if (PIM_DEBUG_PIM_TRACE)
308
0
            zlog_debug(
309
12.6k
              "%s %s: Join(S,G)=%s from %s",
310
12.6k
              __FILE__, __func__,
311
12.6k
              child->sg_str,
312
12.6k
              up->sg_str);
313
314
          /* check if the channel can be
315
           * inherited into the SG's OIL
316
           */
317
12.6k
          child_ch = pim_ifchannel_find(
318
12.6k
              ch->interface,
319
12.6k
              &child->sg);
320
12.6k
          if (pim_upstream_eval_inherit_if(
321
12.6k
                child, child_ch, ch)) {
322
8.23k
            pim_channel_add_oif(
323
8.23k
              child->channel_oil,
324
8.23k
              ch->interface,
325
8.23k
              PIM_OIF_FLAG_PROTO_STAR,
326
8.23k
              __func__);
327
8.23k
            pim_upstream_update_join_desired(
328
8.23k
              pim_ifp->pim, child);
329
8.23k
          }
330
12.6k
        }
331
2.42k
      }
332
4.81k
    }
333
4.81k
  }
334
  /* Transition to/from NOINFO ? */
335
8.25k
  if ((old_state == PIM_IFJOIN_NOINFO)
336
6.78k
      || (new_state == PIM_IFJOIN_NOINFO)) {
337
338
1.84k
    if (PIM_DEBUG_PIM_EVENTS) {
339
0
      zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
340
0
           ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN"
341
0
                     : "UP"),
342
0
           ch->sg_str, ch->interface->name);
343
0
    }
344
345
    /*
346
      Record uptime of state transition to/from NOINFO
347
    */
348
1.84k
    ch->ifjoin_creation = pim_time_monotonic_sec();
349
350
1.84k
    pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
351
1.84k
    pim_ifchannel_update_could_assert(ch);
352
1.84k
    pim_ifchannel_update_assert_tracking_desired(ch);
353
1.84k
  }
354
8.25k
}
355
356
const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state,
357
              int flags)
358
0
{
359
0
  switch (ifjoin_state) {
360
0
  case PIM_IFJOIN_NOINFO:
361
0
    if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
362
0
      return "SGRpt(NI)";
363
0
    else
364
0
      return "NOINFO";
365
0
  case PIM_IFJOIN_JOIN:
366
0
    return "JOIN";
367
0
  case PIM_IFJOIN_PRUNE:
368
0
    if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
369
0
      return "SGRpt(P)";
370
0
    else
371
0
      return "PRUNE";
372
0
  case PIM_IFJOIN_PRUNE_PENDING:
373
0
    if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
374
0
      return "SGRpt(PP)";
375
0
    else
376
0
      return "PRUNEP";
377
0
  case PIM_IFJOIN_PRUNE_TMP:
378
0
    if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
379
0
      return "SGRpt(P')";
380
0
    else
381
0
      return "PRUNET";
382
0
  case PIM_IFJOIN_PRUNE_PENDING_TMP:
383
0
    if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
384
0
      return "SGRpt(PP')";
385
0
    else
386
0
      return "PRUNEPT";
387
0
  }
388
389
0
  return "ifjoin_bad_state";
390
0
}
391
392
const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
393
0
{
394
0
  switch (ifassert_state) {
395
0
  case PIM_IFASSERT_NOINFO:
396
0
    return "NOINFO";
397
0
  case PIM_IFASSERT_I_AM_WINNER:
398
0
    return "WINNER";
399
0
  case PIM_IFASSERT_I_AM_LOSER:
400
0
    return "LOSER";
401
0
  }
402
403
0
  return "ifassert_bad_state";
404
0
}
405
406
/*
407
  RFC 4601: 4.6.5.  Assert State Macros
408
409
  AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
410
  defaults to Infinity when in the NoInfo state.
411
*/
412
void reset_ifassert_state(struct pim_ifchannel *ch)
413
43.2k
{
414
43.2k
  EVENT_OFF(ch->t_ifassert_timer);
415
416
43.2k
  pim_ifassert_winner_set(ch, PIM_IFASSERT_NOINFO, PIMADDR_ANY,
417
43.2k
        router->infinite_assert_metric);
418
43.2k
}
419
420
struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, pim_sgaddr *sg)
421
338k
{
422
338k
  struct pim_interface *pim_ifp;
423
338k
  struct pim_ifchannel *ch;
424
338k
  struct pim_ifchannel lookup;
425
426
338k
  pim_ifp = ifp->info;
427
428
338k
  if (!pim_ifp) {
429
0
    zlog_warn("%s: (S,G)=%pSG: multicast not enabled on interface %s",
430
0
        __func__, sg, ifp->name);
431
0
    return NULL;
432
0
  }
433
434
338k
  lookup.sg = *sg;
435
338k
  lookup.interface = ifp;
436
338k
  ch = RB_FIND(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, &lookup);
437
438
338k
  return ch;
439
338k
}
440
441
static void ifmembership_set(struct pim_ifchannel *ch,
442
           enum pim_ifmembership membership)
443
0
{
444
0
  struct pim_interface *pim_ifp = ch->interface->info;
445
446
0
  if (ch->local_ifmembership == membership)
447
0
    return;
448
449
0
  if (PIM_DEBUG_PIM_EVENTS) {
450
0
    zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
451
0
         __func__, ch->sg_str,
452
0
         membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE"
453
0
                  : "NOINFO",
454
0
         ch->interface->name);
455
0
  }
456
457
0
  ch->local_ifmembership = membership;
458
459
0
  pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
460
0
  pim_ifchannel_update_could_assert(ch);
461
0
  pim_ifchannel_update_assert_tracking_desired(ch);
462
0
}
463
464
465
void pim_ifchannel_membership_clear(struct interface *ifp)
466
0
{
467
0
  struct pim_interface *pim_ifp;
468
0
  struct pim_ifchannel *ch;
469
470
0
  pim_ifp = ifp->info;
471
0
  assert(pim_ifp);
472
473
0
  RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb)
474
0
    ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
475
0
}
476
477
void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
478
0
{
479
0
  struct pim_interface *pim_ifp;
480
0
  struct pim_ifchannel *ch, *ch_tmp;
481
482
0
  pim_ifp = ifp->info;
483
0
  assert(pim_ifp);
484
485
0
  RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch_tmp)
486
0
    delete_on_noinfo(ch);
487
0
}
488
489
/*
490
 * For a given Interface, if we are given a S,G
491
 * Find the *,G (If we have it).
492
 * If we are passed a *,G, find the *,* ifchannel
493
 * if we have it.
494
 */
495
static struct pim_ifchannel *pim_ifchannel_find_parent(struct pim_ifchannel *ch)
496
43.2k
{
497
43.2k
  pim_sgaddr parent_sg = ch->sg;
498
43.2k
  struct pim_ifchannel *parent = NULL;
499
500
  // (S,G)
501
43.2k
  if (!pim_addr_is_any(parent_sg.src) &&
502
43.0k
      !pim_addr_is_any(parent_sg.grp)) {
503
43.0k
    parent_sg.src = PIMADDR_ANY;
504
43.0k
    parent = pim_ifchannel_find(ch->interface, &parent_sg);
505
506
43.0k
    if (parent)
507
42.6k
      listnode_add(parent->sources, ch);
508
43.0k
    return parent;
509
43.0k
  }
510
511
214
  return NULL;
512
43.2k
}
513
514
struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, pim_sgaddr *sg,
515
          uint8_t source_flags, int up_flags)
516
59.7k
{
517
59.7k
  struct pim_interface *pim_ifp;
518
59.7k
  struct pim_ifchannel *ch;
519
59.7k
  struct pim_upstream *up;
520
521
59.7k
  ch = pim_ifchannel_find(ifp, sg);
522
59.7k
  if (ch) {
523
16.4k
    if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
524
16.4k
      PIM_IF_FLAG_SET_PROTO_PIM(ch->flags);
525
526
16.4k
    if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
527
0
      PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags);
528
529
16.4k
    ch->upstream->flags |= up_flags;
530
531
16.4k
    return ch;
532
16.4k
  }
533
534
43.2k
  pim_ifp = ifp->info;
535
536
43.2k
  ch = XCALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
537
538
43.2k
  ch->flags = 0;
539
43.2k
  if ((source_flags & PIM_ENCODE_RPT_BIT)
540
42.2k
      && !(source_flags & PIM_ENCODE_WC_BIT))
541
42.2k
    PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
542
543
43.2k
  ch->interface = ifp;
544
43.2k
  ch->sg = *sg;
545
43.2k
  snprintfrr(ch->sg_str, sizeof(ch->sg_str), "%pSG", sg);
546
43.2k
  ch->parent = pim_ifchannel_find_parent(ch);
547
43.2k
  if (pim_addr_is_any(ch->sg.src)) {
548
172
    ch->sources = list_new();
549
172
    ch->sources->cmp =
550
172
      (int (*)(void *, void *))pim_ifchannel_compare;
551
172
  } else
552
43.0k
    ch->sources = NULL;
553
554
43.2k
  pim_ifchannel_find_new_children(ch);
555
43.2k
  ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
556
557
43.2k
  ch->ifjoin_state = PIM_IFJOIN_NOINFO;
558
43.2k
  ch->t_ifjoin_expiry_timer = NULL;
559
43.2k
  ch->t_ifjoin_prune_pending_timer = NULL;
560
43.2k
  ch->ifjoin_creation = 0;
561
562
43.2k
  RB_INSERT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
563
564
43.2k
  up = pim_upstream_add(pim_ifp->pim, sg, NULL, up_flags, __func__, ch);
565
566
43.2k
  ch->upstream = up;
567
568
43.2k
  listnode_add_sort(up->ifchannels, ch);
569
570
43.2k
  ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
571
43.2k
  ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval(ch);
572
573
43.2k
  ch->ifassert_winner = PIMADDR_ANY;
574
575
  /* Assert state */
576
43.2k
  ch->t_ifassert_timer = NULL;
577
43.2k
  ch->ifassert_state = PIM_IFASSERT_NOINFO;
578
43.2k
  reset_ifassert_state(ch);
579
43.2k
  if (pim_macro_ch_could_assert_eval(ch))
580
0
    PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
581
43.2k
  else
582
43.2k
    PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
583
584
43.2k
  if (pim_macro_assert_tracking_desired_eval(ch))
585
0
    PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
586
43.2k
  else
587
43.2k
    PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
588
589
  /*
590
   * advertise MLAG Data to MLAG peer
591
   */
592
43.2k
  if (PIM_I_am_DualActive(pim_ifp)) {
593
0
    up->dualactive_ifchannel_count++;
594
    /* Sync once for upstream */
595
0
    if (up->dualactive_ifchannel_count == 1) {
596
0
      PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up->flags);
597
0
      pim_mlag_up_local_add(pim_ifp->pim, up);
598
0
    }
599
0
    if (PIM_DEBUG_MLAG)
600
0
      zlog_debug(
601
0
        "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
602
0
        __func__, up->sg_str,
603
0
        up->dualactive_ifchannel_count, up->flags);
604
0
  }
605
606
43.2k
  if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
607
43.1k
    PIM_IF_FLAG_SET_PROTO_PIM(ch->flags);
608
609
43.2k
  if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
610
0
    PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags);
611
612
43.2k
  if (PIM_DEBUG_PIM_TRACE)
613
0
    zlog_debug("%s: ifchannel %s(%s) is created ", __func__,
614
43.2k
         ch->sg_str, ch->interface->name);
615
616
43.2k
  return ch;
617
59.7k
}
618
619
static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
620
0
{
621
0
  pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO);
622
0
  pim_forward_stop(ch);
623
0
624
0
  PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(ch->upstream->flags);
625
0
626
0
  PIM_IF_FLAG_UNSET_PROTO_PIM(ch->flags);
627
0
628
0
  delete_on_noinfo(ch);
629
0
}
630
631
static void on_ifjoin_expiry_timer(struct event *t)
632
0
{
633
0
  struct pim_ifchannel *ch;
634
0
635
0
  ch = EVENT_ARG(t);
636
0
637
0
  if (PIM_DEBUG_PIM_TRACE)
638
0
    zlog_debug("%s: ifchannel %s expiry timer", __func__,
639
0
         ch->sg_str);
640
0
641
0
  ifjoin_to_noinfo(ch);
642
0
  /* ch may have been deleted */
643
0
}
644
645
static void on_ifjoin_prune_pending_timer(struct event *t)
646
0
{
647
0
  struct pim_ifchannel *ch;
648
0
  int send_prune_echo; /* boolean */
649
0
  struct interface *ifp;
650
0
  struct pim_interface *pim_ifp;
651
0
652
0
  ch = EVENT_ARG(t);
653
0
654
0
  if (PIM_DEBUG_PIM_TRACE)
655
0
    zlog_debug("%s: IFCHANNEL%pSG %s Prune Pending Timer Popped",
656
0
         __func__, &ch->sg,
657
0
         pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags));
658
0
659
0
  if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) {
660
0
    ifp = ch->interface;
661
0
    pim_ifp = ifp->info;
662
0
    if (!PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) {
663
0
      /* Send PruneEcho(S,G) ? */
664
0
      send_prune_echo =
665
0
        (listcount(pim_ifp->pim_neighbor_list) > 1);
666
0
667
0
      if (send_prune_echo) {
668
0
        struct pim_rpf rpf;
669
0
670
0
        rpf.source_nexthop.interface = ifp;
671
0
        rpf.rpf_addr = pim_ifp->primary_address;
672
0
        pim_jp_agg_single_upstream_send(
673
0
          &rpf, ch->upstream, 0);
674
0
      }
675
0
676
0
      ifjoin_to_noinfo(ch);
677
0
    } else {
678
0
      /* If SGRpt flag is set on ifchannel, Trigger SGRpt
679
0
       *  message on RP path upon prune timer expiry.
680
0
       */
681
0
      ch->ifjoin_state = PIM_IFJOIN_PRUNE;
682
0
      struct pim_upstream *parent =
683
0
        ch->upstream->parent;
684
0
685
0
      pim_upstream_update_join_desired(pim_ifp->pim,
686
0
               ch->upstream);
687
0
688
0
      pim_jp_agg_single_upstream_send(&parent->rpf,
689
0
              parent, true);
690
0
      /*
691
0
       * SGRpt prune pending expiry has to install
692
0
       * SG entry with empty olist to drop the SG
693
0
       * traffic incase no other intf exists.
694
0
       * On that scenario, SG entry wouldn't have
695
0
       * got installed until Prune pending timer
696
0
       * expired. So install now.
697
0
       */
698
0
      pim_channel_del_oif(
699
0
        ch->upstream->channel_oil, ifp,
700
0
        PIM_OIF_FLAG_PROTO_STAR, __func__);
701
0
      pim_channel_del_oif(ch->upstream->channel_oil, ifp,
702
0
              PIM_OIF_FLAG_PROTO_PIM, __func__);
703
0
      if (!ch->upstream->channel_oil->installed)
704
0
        pim_upstream_mroute_add(
705
0
          ch->upstream->channel_oil,
706
0
          __func__);
707
0
    }
708
0
    /* from here ch may have been deleted */
709
0
  }
710
0
}
711
712
static void check_recv_upstream(int is_join, struct interface *recv_ifp,
713
        pim_addr upstream, pim_sgaddr *sg,
714
        uint8_t source_flags, int holdtime)
715
1.09k
{
716
1.09k
  struct pim_upstream *up;
717
1.09k
  struct pim_interface *pim_ifp = recv_ifp->info;
718
1.09k
  pim_addr rpf_addr;
719
720
  /* Upstream (S,G) in Joined state ? */
721
1.09k
  up = pim_upstream_find(pim_ifp->pim, sg);
722
1.09k
  if (!up)
723
566
    return;
724
525
  if (up->join_state != PIM_UPSTREAM_JOINED)
725
525
    return;
726
727
  /* Upstream (S,G) in Joined state */
728
729
0
  if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
730
    /* RPF'(S,G) not found */
731
0
    zlog_warn("%s %s: RPF'%s not found", __FILE__, __func__,
732
0
        up->sg_str);
733
0
    return;
734
0
  }
735
736
0
  rpf_addr = up->rpf.rpf_addr;
737
738
  /* upstream directed to RPF'(S,G) ? */
739
0
  if (pim_addr_cmp(upstream, rpf_addr)) {
740
0
    zlog_warn(
741
0
      "%s %s: (S,G)=%s upstream=%pPAs not directed to RPF'(S,G)=%pPAs on interface %s",
742
0
      __FILE__, __func__, up->sg_str, &upstream, &rpf_addr,
743
0
      recv_ifp->name);
744
0
    return;
745
0
  }
746
  /* upstream directed to RPF'(S,G) */
747
748
0
  if (is_join) {
749
    /* Join(S,G) to RPF'(S,G) */
750
0
    pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
751
0
    return;
752
0
  }
753
754
  /* Prune to RPF'(S,G) */
755
756
0
  if (source_flags & PIM_RPT_BIT_MASK) {
757
0
    if (source_flags & PIM_WILDCARD_BIT_MASK) {
758
      /* Prune(*,G) to RPF'(S,G) */
759
0
      pim_upstream_join_timer_decrease_to_t_override(
760
0
        "Prune(*,G)", up);
761
0
      return;
762
0
    }
763
764
    /* Prune(S,G,rpt) to RPF'(S,G) */
765
0
    pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
766
0
                     up);
767
0
    return;
768
0
  }
769
770
  /* Prune(S,G) to RPF'(S,G) */
771
0
  pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
772
0
}
773
774
static int nonlocal_upstream(int is_join, struct interface *recv_ifp,
775
           pim_addr upstream, pim_sgaddr *sg,
776
           uint8_t source_flags, uint16_t holdtime)
777
61.2k
{
778
61.2k
  struct pim_interface *recv_pim_ifp;
779
61.2k
  int is_local; /* boolean */
780
781
61.2k
  recv_pim_ifp = recv_ifp->info;
782
61.2k
  assert(recv_pim_ifp);
783
784
61.2k
  is_local = !pim_addr_cmp(upstream, recv_pim_ifp->primary_address);
785
786
61.2k
  if (is_local)
787
60.1k
    return 0;
788
789
1.09k
  if (PIM_DEBUG_PIM_TRACE_DETAIL)
790
0
    zlog_warn(
791
1.09k
      "%s: recv %s (S,G)=%pSG to non-local upstream=%pPAs on %s",
792
1.09k
      __func__, is_join ? "join" : "prune", sg, &upstream,
793
1.09k
      recv_ifp->name);
794
795
  /*
796
   * Since recv upstream addr was not directed to our primary
797
   * address, check if we should react to it in any way.
798
   */
799
1.09k
  check_recv_upstream(is_join, recv_ifp, upstream, sg, source_flags,
800
1.09k
          holdtime);
801
802
1.09k
  return 1; /* non-local */
803
61.2k
}
804
805
static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel *ch,
806
    struct pim_interface *pim_ifp)
807
2.77k
{
808
2.77k
  pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN);
809
2.77k
  PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags);
810
  /* check if the interface qualifies as an immediate
811
   * OIF
812
   */
813
2.77k
  if (pim_upstream_evaluate_join_desired_interface(
814
2.77k
        ch->upstream, ch,
815
2.77k
        NULL /*starch*/)) {
816
2.77k
    pim_channel_add_oif(ch->upstream->channel_oil,
817
2.77k
        ch->interface,
818
2.77k
        PIM_OIF_FLAG_PROTO_PIM,
819
2.77k
        __func__);
820
2.77k
    pim_upstream_update_join_desired(pim_ifp->pim,
821
2.77k
        ch->upstream);
822
2.77k
  }
823
2.77k
}
824
825
826
void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr,
827
          pim_addr upstream, pim_sgaddr *sg,
828
          uint8_t source_flags, uint16_t holdtime)
829
6.56k
{
830
6.56k
  struct pim_interface *pim_ifp;
831
6.56k
  struct pim_ifchannel *ch;
832
833
6.56k
  if (nonlocal_upstream(1 /* join */, ifp, upstream, sg, source_flags,
834
6.56k
            holdtime)) {
835
555
    return;
836
555
  }
837
838
6.00k
  ch = pim_ifchannel_add(ifp, sg, source_flags,
839
6.00k
             PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
840
841
  /*
842
    RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
843
844
    Transitions from "I am Assert Loser" State
845
846
    Receive Join(S,G) on Interface I
847
848
    We receive a Join(S,G) that has the Upstream Neighbor Address
849
    field set to my primary IP address on interface I.  The action is
850
    to transition to NoInfo state, delete this (S,G) assert state
851
    (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
852
    to operate.
853
854
    Notice: The nonlocal_upstream() test above ensures the upstream
855
    address of the join message is our primary address.
856
   */
857
6.00k
  if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
858
4
    zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s",
859
4
        __func__, ch->sg_str, &neigh_addr, ifp->name);
860
861
4
    assert_action_a5(ch);
862
4
  }
863
864
6.00k
  pim_ifp = ifp->info;
865
6.00k
  assert(pim_ifp);
866
867
6.00k
  switch (ch->ifjoin_state) {
868
1.46k
  case PIM_IFJOIN_NOINFO:
869
1.46k
    pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN);
870
1.46k
    if (pim_macro_chisin_oiflist(ch)) {
871
0
      pim_upstream_inherited_olist(pim_ifp->pim,
872
0
                 ch->upstream);
873
0
      pim_forward_start(ch);
874
0
    }
875
    /*
876
     * If we are going to be a LHR, we need to note it
877
     */
878
1.46k
    if (ch->upstream->parent &&
879
1.21k
      (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(
880
1.21k
               ch->upstream->parent->flags))
881
0
        && !(ch->upstream->flags
882
0
       & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)) {
883
0
      pim_upstream_ref(ch->upstream,
884
0
           PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
885
0
           __func__);
886
0
      pim_upstream_keep_alive_timer_start(
887
0
        ch->upstream, pim_ifp->pim->keep_alive_time);
888
0
    }
889
1.46k
    break;
890
1.31k
  case PIM_IFJOIN_JOIN:
891
1.31k
    assert(!ch->t_ifjoin_prune_pending_timer);
892
893
    /*
894
      In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
895
      a
896
      previously received join message with holdtime=0xFFFF.
897
     */
898
1.31k
    if (ch->t_ifjoin_expiry_timer) {
899
0
      unsigned long remain = event_timer_remain_second(
900
0
        ch->t_ifjoin_expiry_timer);
901
0
      if (remain > holdtime) {
902
        /*
903
          RFC 4601: 4.5.3.  Receiving (S,G) Join/Prune
904
          Messages
905
906
          Transitions from Join State
907
908
          The (S,G) downstream state machine on
909
          interface I remains in
910
          Join state, and the Expiry Timer (ET) is
911
          restarted, set to
912
          maximum of its current value and the HoldTime
913
          from the
914
          triggering Join/Prune message.
915
916
          Conclusion: Do not change the ET if the
917
          current value is
918
          higher than the received join holdtime.
919
         */
920
0
        return;
921
0
      }
922
0
    }
923
1.31k
    EVENT_OFF(ch->t_ifjoin_expiry_timer);
924
1.31k
    break;
925
0
  case PIM_IFJOIN_PRUNE:
926
0
    if (source_flags & PIM_ENCODE_RPT_BIT) {
927
0
      pim_ifchannel_ifjoin_switch(__func__, ch,
928
0
                PIM_IFJOIN_NOINFO);
929
0
      EVENT_OFF(ch->t_ifjoin_expiry_timer);
930
0
      delete_on_noinfo(ch);
931
0
      return;
932
0
    } else
933
0
      pim_ifchannel_ifjoin_handler(ch, pim_ifp);
934
0
    break;
935
3.15k
  case PIM_IFJOIN_PRUNE_PENDING:
936
    /*
937
     * Transitions from Prune-Pending State (Receive Join)
938
     * RFC 7761 Sec 4.5.2:
939
     *    The (S,G) downstream state machine on interface I
940
     * transitions to the Join state.  The Prune-Pending Timer is
941
     * canceled (without triggering an expiry event).  The
942
     * Expiry Timer (ET) is restarted and is then set to the
943
     * maximum of its current value and the HoldTime from the
944
     * triggering Join/Prune message.
945
     */
946
3.15k
    EVENT_OFF(ch->t_ifjoin_prune_pending_timer);
947
948
    /* Check if SGRpt join Received */
949
3.15k
    if ((source_flags & PIM_ENCODE_RPT_BIT) &&
950
798
        !pim_addr_is_any(sg->src)) {
951
      /*
952
       * Transitions from Prune-Pending State (Rcv SGRpt Join)
953
       * RFC 7761 Sec 4.5.3:
954
       * The (S,G,rpt) downstream state machine on interface
955
       * I transitions to the NoInfo state.The ET and PPT are
956
       * cancelled.
957
       */
958
379
      EVENT_OFF(ch->t_ifjoin_expiry_timer);
959
379
      pim_ifchannel_ifjoin_switch(__func__, ch,
960
379
                PIM_IFJOIN_NOINFO);
961
379
      return;
962
379
    }
963
964
2.77k
    pim_ifchannel_ifjoin_handler(ch, pim_ifp);
965
966
2.77k
    if (ch->t_ifjoin_expiry_timer) {
967
0
      unsigned long remain = event_timer_remain_second(
968
0
        ch->t_ifjoin_expiry_timer);
969
970
0
      if (remain > holdtime)
971
0
        return;
972
0
    }
973
2.77k
    EVENT_OFF(ch->t_ifjoin_expiry_timer);
974
975
2.77k
    break;
976
0
  case PIM_IFJOIN_PRUNE_TMP:
977
0
    break;
978
75
  case PIM_IFJOIN_PRUNE_PENDING_TMP:
979
75
    break;
980
6.00k
  }
981
982
5.62k
  if (holdtime != 0xFFFF) {
983
5.62k
    event_add_timer(router->master, on_ifjoin_expiry_timer, ch,
984
5.62k
        holdtime, &ch->t_ifjoin_expiry_timer);
985
5.62k
  }
986
5.62k
}
987
988
void pim_ifchannel_prune(struct interface *ifp, pim_addr upstream,
989
       pim_sgaddr *sg, uint8_t source_flags,
990
       uint16_t holdtime)
991
54.6k
{
992
54.6k
  struct pim_ifchannel *ch;
993
54.6k
  struct pim_interface *pim_ifp;
994
54.6k
  int jp_override_interval_msec;
995
996
54.6k
  if (nonlocal_upstream(0 /* prune */, ifp, upstream, sg, source_flags,
997
54.6k
            holdtime)) {
998
536
    return;
999
536
  }
1000
1001
54.1k
  ch = pim_ifchannel_find(ifp, sg);
1002
54.1k
  if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT)) {
1003
555
    if (PIM_DEBUG_PIM_TRACE)
1004
0
      zlog_debug("%s: Received prune with no relevant ifchannel %s%pSG state: %d",
1005
555
           __func__, ifp->name, sg,
1006
555
           source_flags);
1007
555
    return;
1008
555
  }
1009
1010
53.5k
  ch = pim_ifchannel_add(ifp, sg, source_flags,
1011
53.5k
             PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
1012
1013
53.5k
  pim_ifp = ifp->info;
1014
1015
53.5k
  switch (ch->ifjoin_state) {
1016
41.8k
  case PIM_IFJOIN_NOINFO:
1017
41.8k
    if (source_flags & PIM_ENCODE_RPT_BIT) {
1018
41.8k
      if (!(source_flags & PIM_ENCODE_WC_BIT))
1019
41.8k
        PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
1020
1021
41.8k
      ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
1022
41.8k
      if (listcount(pim_ifp->pim_neighbor_list) > 1)
1023
41.8k
        jp_override_interval_msec =
1024
41.8k
          pim_if_jp_override_interval_msec(ifp);
1025
0
      else
1026
0
        jp_override_interval_msec =
1027
0
          0; /* schedule to expire immediately */
1028
      /* If we called ifjoin_prune() directly instead, care
1029
         should
1030
         be taken not to use "ch" afterwards since it would be
1031
         deleted. */
1032
1033
41.8k
      EVENT_OFF(ch->t_ifjoin_prune_pending_timer);
1034
41.8k
      EVENT_OFF(ch->t_ifjoin_expiry_timer);
1035
41.8k
      event_add_timer_msec(router->master,
1036
41.8k
               on_ifjoin_prune_pending_timer, ch,
1037
41.8k
               jp_override_interval_msec,
1038
41.8k
               &ch->t_ifjoin_prune_pending_timer);
1039
41.8k
      event_add_timer(router->master, on_ifjoin_expiry_timer,
1040
41.8k
          ch, holdtime,
1041
41.8k
          &ch->t_ifjoin_expiry_timer);
1042
41.8k
      pim_upstream_update_join_desired(pim_ifp->pim,
1043
41.8k
               ch->upstream);
1044
41.8k
    }
1045
41.8k
    break;
1046
7.48k
  case PIM_IFJOIN_PRUNE_PENDING:
1047
    /* nothing to do */
1048
7.48k
    break;
1049
3.63k
  case PIM_IFJOIN_JOIN:
1050
    /*
1051
     * The (S,G) downstream state machine on interface I
1052
     * transitions to the Prune-Pending state.  The
1053
     * Prune-Pending Timer is started.  It is set to the
1054
     * J/P_Override_Interval(I) if the router has more than one
1055
     * neighbor on that interface; otherwise, it is set to zero,
1056
     * causing it to expire immediately.
1057
     */
1058
1059
3.63k
    pim_ifchannel_ifjoin_switch(__func__, ch,
1060
3.63k
              PIM_IFJOIN_PRUNE_PENDING);
1061
1062
3.63k
    if (listcount(pim_ifp->pim_neighbor_list) > 1)
1063
3.63k
      jp_override_interval_msec =
1064
3.63k
        pim_if_jp_override_interval_msec(ifp);
1065
0
    else
1066
0
      jp_override_interval_msec =
1067
0
        0; /* schedule to expire immediately */
1068
    /* If we called ifjoin_prune() directly instead, care should
1069
       be taken not to use "ch" afterwards since it would be
1070
       deleted. */
1071
3.63k
    EVENT_OFF(ch->t_ifjoin_prune_pending_timer);
1072
3.63k
    event_add_timer_msec(router->master,
1073
3.63k
             on_ifjoin_prune_pending_timer, ch,
1074
3.63k
             jp_override_interval_msec,
1075
3.63k
             &ch->t_ifjoin_prune_pending_timer);
1076
3.63k
    break;
1077
0
  case PIM_IFJOIN_PRUNE:
1078
0
    if (source_flags & PIM_ENCODE_RPT_BIT) {
1079
0
      EVENT_OFF(ch->t_ifjoin_prune_pending_timer);
1080
      /*
1081
       * While in Prune State, Receive SGRpt Prune.
1082
       * RFC 7761 Sec 4.5.3:
1083
       * The (S,G,rpt) downstream state machine on interface I
1084
       * remains in Prune state.  The Expiry Timer (ET) is
1085
       * restarted and is then set to the maximum of its
1086
       * current value and the HoldTime from the triggering
1087
       * Join/Prune message.
1088
       */
1089
0
      if (ch->t_ifjoin_expiry_timer) {
1090
0
        unsigned long rem = event_timer_remain_second(
1091
0
          ch->t_ifjoin_expiry_timer);
1092
1093
0
        if (rem > holdtime)
1094
0
          return;
1095
0
        EVENT_OFF(ch->t_ifjoin_expiry_timer);
1096
0
      }
1097
1098
0
      event_add_timer(router->master, on_ifjoin_expiry_timer,
1099
0
          ch, holdtime,
1100
0
          &ch->t_ifjoin_expiry_timer);
1101
0
    }
1102
0
    break;
1103
0
  case PIM_IFJOIN_PRUNE_TMP:
1104
0
    if (source_flags & PIM_ENCODE_RPT_BIT) {
1105
0
      ch->ifjoin_state = PIM_IFJOIN_PRUNE;
1106
0
      EVENT_OFF(ch->t_ifjoin_expiry_timer);
1107
0
      event_add_timer(router->master, on_ifjoin_expiry_timer,
1108
0
          ch, holdtime,
1109
0
          &ch->t_ifjoin_expiry_timer);
1110
0
    }
1111
0
    break;
1112
582
  case PIM_IFJOIN_PRUNE_PENDING_TMP:
1113
582
    if (source_flags & PIM_ENCODE_RPT_BIT) {
1114
452
      ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
1115
452
      EVENT_OFF(ch->t_ifjoin_expiry_timer);
1116
452
      event_add_timer(router->master, on_ifjoin_expiry_timer,
1117
452
          ch, holdtime,
1118
452
          &ch->t_ifjoin_expiry_timer);
1119
452
    }
1120
582
    break;
1121
53.5k
  }
1122
53.5k
}
1123
1124
int pim_ifchannel_local_membership_add(struct interface *ifp, pim_sgaddr *sg,
1125
               bool is_vxlan)
1126
0
{
1127
0
  struct pim_ifchannel *ch, *starch;
1128
0
  struct pim_interface *pim_ifp;
1129
0
  struct pim_instance *pim;
1130
0
  int up_flags;
1131
1132
  /* PIM enabled on interface? */
1133
0
  pim_ifp = ifp->info;
1134
0
  if (!pim_ifp) {
1135
0
    if (PIM_DEBUG_EVENTS)
1136
0
      zlog_debug("%s:%pSG Expected pim interface setup for %s",
1137
0
           __func__, sg, ifp->name);
1138
0
    return 0;
1139
0
  }
1140
1141
0
  if (!pim_ifp->pim_enable) {
1142
0
    if (PIM_DEBUG_EVENTS)
1143
0
      zlog_debug("%s:%pSG PIM is not configured on this interface %s",
1144
0
           __func__, sg, ifp->name);
1145
0
    return 0;
1146
0
  }
1147
1148
0
  pim = pim_ifp->pim;
1149
1150
  /* skip (*,G) ch creation if G is of type SSM */
1151
0
  if (pim_addr_is_any(sg->src)) {
1152
0
    if (pim_is_grp_ssm(pim, sg->grp)) {
1153
0
      if (PIM_DEBUG_PIM_EVENTS)
1154
0
        zlog_debug("%s: local membership (S,G)=%pSG ignored as group is SSM",
1155
0
             __func__, sg);
1156
0
      return 1;
1157
0
    }
1158
0
  }
1159
1160
  /* vxlan term mroutes use ipmr-lo as local member to
1161
   * pull down multicast vxlan tunnel traffic
1162
   */
1163
0
  up_flags = is_vxlan ? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM :
1164
0
    PIM_UPSTREAM_FLAG_MASK_SRC_IGMP;
1165
0
  ch = pim_ifchannel_add(ifp, sg, 0, up_flags);
1166
1167
0
  ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
1168
1169
0
  if (pim_addr_is_any(sg->src)) {
1170
0
    struct pim_upstream *up = pim_upstream_find(pim, sg);
1171
0
    struct pim_upstream *child;
1172
0
    struct listnode *up_node;
1173
1174
0
    starch = ch;
1175
1176
0
    for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
1177
0
      if (PIM_DEBUG_EVENTS)
1178
0
        zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1179
0
             __FILE__, __func__, child->sg_str,
1180
0
             ifp->name, up->sg_str);
1181
1182
0
      if (!child->rpf.source_nexthop.interface) {
1183
        /* when iif unknown, do not inherit */
1184
0
        if (PIM_DEBUG_EVENTS)
1185
0
          zlog_debug(
1186
0
            "Skipped (S,G)=%s(%s) from %s: no iif",
1187
0
            child->sg_str, ifp->name,
1188
0
            up->sg_str);
1189
0
        continue;
1190
0
      }
1191
1192
0
      ch = pim_ifchannel_find(ifp, &child->sg);
1193
0
      if (pim_upstream_evaluate_join_desired_interface(
1194
0
            child, ch, starch)) {
1195
0
        pim_channel_add_oif(child->channel_oil, ifp,
1196
0
                PIM_OIF_FLAG_PROTO_STAR,
1197
0
              __func__);
1198
0
        pim_upstream_update_join_desired(pim, child);
1199
0
      }
1200
0
    }
1201
1202
0
    if (pim->spt.switchover == PIM_SPT_INFINITY) {
1203
0
      if (pim->spt.plist) {
1204
0
        struct prefix_list *plist = prefix_list_lookup(
1205
0
          AFI_IP, pim->spt.plist);
1206
0
        struct prefix g;
1207
1208
0
        pim_addr_to_prefix(&g, up->sg.grp);
1209
0
        if (prefix_list_apply_ext(plist, NULL, &g,
1210
0
                true) ==
1211
0
            PREFIX_DENY) {
1212
0
          pim_channel_add_oif(
1213
0
            up->channel_oil, pim->regiface,
1214
0
            PIM_OIF_FLAG_PROTO_GM,
1215
0
            __func__);
1216
0
        }
1217
0
      }
1218
0
    } else
1219
0
      pim_channel_add_oif(up->channel_oil, pim->regiface,
1220
0
              PIM_OIF_FLAG_PROTO_GM, __func__);
1221
0
  }
1222
1223
0
  return 1;
1224
0
}
1225
1226
void pim_ifchannel_local_membership_del(struct interface *ifp, pim_sgaddr *sg)
1227
0
{
1228
0
  struct pim_ifchannel *starch, *ch, *orig;
1229
0
  struct pim_interface *pim_ifp;
1230
1231
  /* PIM enabled on interface? */
1232
0
  pim_ifp = ifp->info;
1233
0
  if (!pim_ifp)
1234
0
    return;
1235
0
  if (!pim_ifp->pim_enable)
1236
0
    return;
1237
1238
0
  orig = ch = pim_ifchannel_find(ifp, sg);
1239
0
  if (!ch)
1240
0
    return;
1241
0
  ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
1242
1243
0
  if (pim_addr_is_any(sg->src)) {
1244
0
    struct pim_upstream *up = pim_upstream_find(pim_ifp->pim, sg);
1245
0
    struct pim_upstream *child;
1246
0
    struct listnode *up_node, *up_nnode;
1247
1248
0
    starch = ch;
1249
1250
0
    for (ALL_LIST_ELEMENTS(up->sources, up_node, up_nnode, child)) {
1251
0
      struct channel_oil *c_oil = child->channel_oil;
1252
0
      struct pim_ifchannel *chchannel =
1253
0
        pim_ifchannel_find(ifp, &child->sg);
1254
1255
0
      pim_ifp = ifp->info;
1256
1257
0
      if (PIM_DEBUG_EVENTS)
1258
0
        zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1259
0
             __FILE__, __func__, up->sg_str,
1260
0
             ifp->name, child->sg_str);
1261
1262
0
      ch = pim_ifchannel_find(ifp, &child->sg);
1263
      /*
1264
       * If the S,G has no if channel and the c_oil still
1265
       * has output here then the *,G was supplying the
1266
       * implied
1267
       * if channel.  So remove it.
1268
       */
1269
0
      if (!pim_upstream_evaluate_join_desired_interface(
1270
0
        child, ch, starch) ||
1271
0
        (!chchannel &&
1272
0
         oil_if_has(c_oil, pim_ifp->mroute_vif_index))) {
1273
0
        pim_channel_del_inherited_oif(c_oil, ifp,
1274
0
            __func__);
1275
0
      }
1276
1277
      /* Child node removal/ref count-- will happen as part of
1278
       * parent' delete_no_info */
1279
0
    }
1280
0
  }
1281
1282
  /* Resettng the IGMP flags here */
1283
0
  if (orig->upstream)
1284
0
    PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(orig->upstream->flags);
1285
1286
0
  PIM_IF_FLAG_UNSET_PROTO_IGMP(orig->flags);
1287
1288
0
  delete_on_noinfo(orig);
1289
0
}
1290
1291
void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
1292
1.85k
{
1293
1.85k
  int old_couldassert =
1294
1.85k
    PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
1295
1.85k
  int new_couldassert =
1296
1.85k
    PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
1297
1298
1.85k
  if (new_couldassert == old_couldassert)
1299
1.85k
    return;
1300
1301
0
  if (PIM_DEBUG_PIM_EVENTS)
1302
0
    zlog_debug("%s: CouldAssert(%pPAs,%pPAs,%s) changed from %d to %d",
1303
0
         __func__, &ch->sg.src, &ch->sg.grp,
1304
0
         ch->interface->name, old_couldassert,
1305
0
         new_couldassert);
1306
1307
0
  if (new_couldassert) {
1308
    /* CouldAssert(S,G,I) switched from false to true */
1309
0
    PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
1310
0
  } else {
1311
    /* CouldAssert(S,G,I) switched from true to false */
1312
0
    PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
1313
1314
0
    if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
1315
0
      assert_action_a4(ch);
1316
0
    }
1317
0
  }
1318
1319
0
  pim_ifchannel_update_my_assert_metric(ch);
1320
0
}
1321
1322
/*
1323
  my_assert_metric may be affected by:
1324
1325
  CouldAssert(S,G)
1326
  pim_ifp->primary_address
1327
  rpf->source_nexthop.mrib_metric_preference;
1328
  rpf->source_nexthop.mrib_route_metric;
1329
 */
1330
void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
1331
0
{
1332
0
  struct pim_assert_metric my_metric_new =
1333
0
    pim_macro_ch_my_assert_metric_eval(ch);
1334
1335
0
  if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
1336
0
    return;
1337
1338
0
  if (PIM_DEBUG_PIM_EVENTS)
1339
0
    zlog_debug(
1340
0
      "%s: my_assert_metric(%pPAs,%pPAs,%s) changed from %u,%u,%u,%pPAs to %u,%u,%u,%pPAs",
1341
0
      __func__, &ch->sg.src, &ch->sg.grp, ch->interface->name,
1342
0
      ch->ifassert_my_metric.rpt_bit_flag,
1343
0
      ch->ifassert_my_metric.metric_preference,
1344
0
      ch->ifassert_my_metric.route_metric,
1345
0
      &ch->ifassert_my_metric.ip_address,
1346
0
      my_metric_new.rpt_bit_flag,
1347
0
      my_metric_new.metric_preference,
1348
0
      my_metric_new.route_metric, &my_metric_new.ip_address);
1349
1350
0
  ch->ifassert_my_metric = my_metric_new;
1351
1352
0
  if (pim_assert_metric_better(&ch->ifassert_my_metric,
1353
0
             &ch->ifassert_winner_metric)) {
1354
0
    assert_action_a5(ch);
1355
0
  }
1356
0
}
1357
1358
void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
1359
1.85k
{
1360
1.85k
  int old_atd = PIM_FORCE_BOOLEAN(
1361
1.85k
    PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
1362
1.85k
  int new_atd =
1363
1.85k
    PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
1364
1365
1.85k
  if (new_atd == old_atd)
1366
184
    return;
1367
1368
1.67k
  if (PIM_DEBUG_PIM_EVENTS)
1369
0
    zlog_debug(
1370
1.67k
      "%s: AssertTrackingDesired(%pPAs,%pPAs,%s) changed from %d to %d",
1371
1.67k
      __func__, &ch->sg.src, &ch->sg.grp, ch->interface->name,
1372
1.67k
      old_atd, new_atd);
1373
1374
1.67k
  if (new_atd) {
1375
    /* AssertTrackingDesired(S,G,I) switched from false to true */
1376
1.46k
    PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
1377
1.46k
  } else {
1378
    /* AssertTrackingDesired(S,G,I) switched from true to false */
1379
208
    PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
1380
1381
208
    if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
1382
0
      assert_action_a5(ch);
1383
0
    }
1384
208
  }
1385
1.67k
}
1386
1387
/*
1388
 * If we have a new pim interface, check to
1389
 * see if any of the pre-existing channels have
1390
 * their upstream out that way and turn on forwarding
1391
 * for that ifchannel then.
1392
 */
1393
void pim_ifchannel_scan_forward_start(struct interface *new_ifp)
1394
0
{
1395
0
  struct pim_interface *new_pim_ifp = new_ifp->info;
1396
0
  struct pim_instance *pim = new_pim_ifp->pim;
1397
0
  struct interface *ifp;
1398
1399
0
  FOR_ALL_INTERFACES (pim->vrf, ifp) {
1400
0
    struct pim_interface *loop_pim_ifp = ifp->info;
1401
0
    struct pim_ifchannel *ch;
1402
1403
0
    if (!loop_pim_ifp)
1404
0
      continue;
1405
1406
0
    if (new_pim_ifp == loop_pim_ifp)
1407
0
      continue;
1408
1409
0
    RB_FOREACH (ch, pim_ifchannel_rb, &loop_pim_ifp->ifchannel_rb) {
1410
0
      if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
1411
0
        struct pim_upstream *up = ch->upstream;
1412
0
        if ((!up->channel_oil)
1413
0
            && (up->rpf.source_nexthop
1414
0
            .interface == new_ifp))
1415
0
          pim_forward_start(ch);
1416
0
      }
1417
0
    }
1418
0
  }
1419
0
}
1420
1421
/*
1422
 * Downstream per-interface (S,G,rpt) state machine
1423
 * states that we need to move (S,G,rpt) items
1424
 * into different states at the start of the
1425
 * reception of a *,G join as well, when
1426
 * we get End of Message
1427
 */
1428
void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
1429
           uint8_t join)
1430
6.16k
{
1431
6.16k
  bool send_upstream_starg = false;
1432
6.16k
  struct pim_ifchannel *child;
1433
6.16k
  struct listnode *ch_node, *nch_node;
1434
6.16k
  struct pim_instance *pim =
1435
6.16k
    ((struct pim_interface *)ch->interface->info)->pim;
1436
6.16k
  struct pim_upstream *starup = ch->upstream;
1437
1438
6.16k
  if (PIM_DEBUG_PIM_TRACE)
1439
0
    zlog_debug(
1440
6.16k
      "%s: %s %s eom: %d join %u", __func__,
1441
6.16k
      pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
1442
6.16k
      ch->sg_str, eom, join);
1443
6.16k
  if (!ch->sources)
1444
0
    return;
1445
1446
34.7k
  for (ALL_LIST_ELEMENTS(ch->sources, ch_node, nch_node, child)) {
1447
34.7k
    if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1448
18.7k
      continue;
1449
1450
15.9k
    switch (child->ifjoin_state) {
1451
4
    case PIM_IFJOIN_NOINFO:
1452
897
    case PIM_IFJOIN_JOIN:
1453
897
      break;
1454
0
    case PIM_IFJOIN_PRUNE:
1455
0
      if (!eom)
1456
0
        child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
1457
0
      break;
1458
11.1k
    case PIM_IFJOIN_PRUNE_PENDING:
1459
11.1k
      if (!eom)
1460
5.92k
        child->ifjoin_state =
1461
5.92k
          PIM_IFJOIN_PRUNE_PENDING_TMP;
1462
11.1k
      break;
1463
0
    case PIM_IFJOIN_PRUNE_TMP:
1464
3.95k
    case PIM_IFJOIN_PRUNE_PENDING_TMP:
1465
3.95k
      if (!eom)
1466
212
        break;
1467
1468
3.74k
      if (child->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING_TMP)
1469
3.74k
        EVENT_OFF(child->t_ifjoin_prune_pending_timer);
1470
3.74k
      EVENT_OFF(child->t_ifjoin_expiry_timer);
1471
1472
3.74k
      PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
1473
3.74k
      child->ifjoin_state = PIM_IFJOIN_NOINFO;
1474
1475
3.74k
      if ((I_am_RP(pim, child->sg.grp)) &&
1476
0
          (!pim_upstream_empty_inherited_olist(
1477
0
        child->upstream))) {
1478
0
        pim_channel_add_oif(
1479
0
          child->upstream->channel_oil,
1480
0
          ch->interface, PIM_OIF_FLAG_PROTO_STAR,
1481
0
          __func__);
1482
0
        pim_upstream_update_join_desired(pim,
1483
0
            child->upstream);
1484
0
      }
1485
3.74k
      send_upstream_starg = true;
1486
1487
3.74k
      delete_on_noinfo(child);
1488
3.74k
      break;
1489
15.9k
    }
1490
15.9k
  }
1491
1492
6.16k
  if (send_upstream_starg)
1493
937
    pim_jp_agg_single_upstream_send(&starup->rpf, starup, true);
1494
6.16k
}