]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_ifchannel.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / pimd / pim_ifchannel.c
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 "thread.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 {
41 struct pim_interface *pim_ifp1;
42 struct pim_interface *pim_ifp2;
43
44 pim_ifp1 = ch1->interface->info;
45 pim_ifp2 = ch2->interface->info;
46
47 if (pim_ifp1->mroute_vif_index < pim_ifp2->mroute_vif_index)
48 return -1;
49
50 if (pim_ifp1->mroute_vif_index > pim_ifp2->mroute_vif_index)
51 return 1;
52
53 return pim_sgaddr_cmp(ch1->sg, ch2->sg);
54 }
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 {
63 struct pim_ifchannel *child;
64
65 if (!ch->sources)
66 return;
67
68 while (!list_isempty(ch->sources)) {
69 child = listnode_head(ch->sources);
70 child->parent = NULL;
71 listnode_delete(ch->sources, child);
72 }
73 }
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 {
82 struct pim_interface *pim_ifp = ch->interface->info;
83 struct pim_ifchannel *child;
84
85 // Basic Sanity that we are not being silly
86 if (!pim_addr_is_any(ch->sg.src) && !pim_addr_is_any(ch->sg.grp))
87 return;
88
89 if (pim_addr_is_any(ch->sg.src) && pim_addr_is_any(ch->sg.grp))
90 return;
91
92 RB_FOREACH (child, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
93 if (!pim_addr_is_any(ch->sg.grp) &&
94 !pim_addr_cmp(child->sg.grp, ch->sg.grp) && (child != ch)) {
95 child->parent = ch;
96 listnode_add_sort(ch->sources, child);
97 }
98 }
99 }
100
101 void pim_ifchannel_delete(struct pim_ifchannel *ch)
102 {
103 struct pim_interface *pim_ifp;
104 struct pim_upstream *up;
105
106 pim_ifp = ch->interface->info;
107
108 if (PIM_DEBUG_PIM_TRACE)
109 zlog_debug("%s: ifchannel entry %s(%s) del start", __func__,
110 ch->sg_str, ch->interface->name);
111
112 if (PIM_I_am_DualActive(pim_ifp)) {
113 if (PIM_DEBUG_MLAG)
114 zlog_debug(
115 "%s: if-chnanel-%s is deleted from a Dual active Interface",
116 __func__, ch->sg_str);
117 /* Post Delete only if it is the last Dual-active Interface */
118 if (ch->upstream->dualactive_ifchannel_count == 1) {
119 pim_mlag_up_local_del(pim_ifp->pim, ch->upstream);
120 PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(
121 ch->upstream->flags);
122 }
123 ch->upstream->dualactive_ifchannel_count--;
124 }
125
126 if (ch->upstream->channel_oil) {
127 uint32_t mask = PIM_OIF_FLAG_PROTO_PIM;
128 if (ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
129 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 if (!pim_addr_is_any(ch->sg.src) &&
139 pim_upstream_evaluate_join_desired_interface(
140 ch->upstream, ch, ch->parent))
141 pim_channel_add_oif(ch->upstream->channel_oil,
142 ch->interface,
143 PIM_OIF_FLAG_PROTO_STAR,
144 __func__);
145
146 pim_channel_del_oif(ch->upstream->channel_oil,
147 ch->interface, mask, __func__);
148 /*
149 * Do we have any S,G's that are inheriting?
150 * Nuke from on high too.
151 */
152 if (ch->upstream->sources) {
153 struct pim_upstream *child;
154 struct listnode *up_node;
155
156 for (ALL_LIST_ELEMENTS_RO(ch->upstream->sources,
157 up_node, child))
158 pim_channel_del_inherited_oif(
159 child->channel_oil,
160 ch->interface,
161 __func__);
162 }
163 }
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 pim_ifchannel_remove_children(ch);
171
172 if (ch->sources)
173 list_delete(&ch->sources);
174
175 listnode_delete(ch->upstream->ifchannels, ch);
176
177 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 if (ch->upstream->ref_count > 0)
184 up = pim_upstream_del(pim_ifp->pim, ch->upstream, __func__);
185
186 else {
187 if (PIM_DEBUG_PIM_TRACE)
188 zlog_debug(
189 "%s: Avoiding deletion of upstream with ref_count %d from ifchannel(%s): %s",
190 __func__, ch->upstream->ref_count,
191 ch->interface->name, ch->sg_str);
192 }
193
194 ch->upstream = NULL;
195
196 THREAD_OFF(ch->t_ifjoin_expiry_timer);
197 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
198 THREAD_OFF(ch->t_ifassert_timer);
199
200 if (ch->parent) {
201 listnode_delete(ch->parent->sources, ch);
202 ch->parent = NULL;
203 }
204
205 RB_REMOVE(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
206
207 if (PIM_DEBUG_PIM_TRACE)
208 zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__,
209 ch->sg_str, ch->interface->name);
210
211 XFREE(MTYPE_PIM_IFCHANNEL, ch);
212
213 if (up)
214 pim_upstream_update_join_desired(pim_ifp->pim, up);
215 }
216
217 void pim_ifchannel_delete_all(struct interface *ifp)
218 {
219 struct pim_interface *pim_ifp;
220 struct pim_ifchannel *ch;
221
222 pim_ifp = ifp->info;
223 if (!pim_ifp)
224 return;
225
226 while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
227 ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
228
229 pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO);
230 pim_ifchannel_delete(ch);
231 }
232 }
233
234 void delete_on_noinfo(struct pim_ifchannel *ch)
235 {
236 if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO
237 && ch->ifjoin_state == PIM_IFJOIN_NOINFO
238 && ch->t_ifjoin_expiry_timer == NULL)
239 pim_ifchannel_delete(ch);
240 }
241
242 void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch,
243 enum pim_ifjoin_state new_state)
244 {
245 enum pim_ifjoin_state old_state = ch->ifjoin_state;
246 struct pim_interface *pim_ifp = ch->interface->info;
247 struct pim_ifchannel *child_ch;
248
249 if (PIM_DEBUG_PIM_EVENTS)
250 zlog_debug(
251 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
252 ch->interface->name, ch->sg_str,
253 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
254 pim_ifchannel_ifjoin_name(new_state, 0));
255
256
257 if (old_state == new_state) {
258 if (PIM_DEBUG_PIM_EVENTS) {
259 zlog_debug(
260 "%s called by %s: non-transition on state %d (%s)",
261 __func__, caller, new_state,
262 pim_ifchannel_ifjoin_name(new_state, 0));
263 }
264 return;
265 }
266
267 ch->ifjoin_state = new_state;
268
269 if (pim_addr_is_any(ch->sg.src)) {
270 struct pim_upstream *up = ch->upstream;
271 struct pim_upstream *child;
272 struct listnode *up_node;
273
274 if (up) {
275 if (ch->ifjoin_state == PIM_IFJOIN_NOINFO) {
276 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
277 child)) {
278 struct channel_oil *c_oil =
279 child->channel_oil;
280
281 if (PIM_DEBUG_PIM_TRACE)
282 zlog_debug(
283 "%s %s: Prune(S,G)=%s from %s",
284 __FILE__, __func__,
285 child->sg_str,
286 up->sg_str);
287 if (!c_oil)
288 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 if (oil_if_has(c_oil,
298 pim_ifp->mroute_vif_index))
299 pim_channel_del_inherited_oif(
300 c_oil, ch->interface,
301 __func__);
302 }
303 }
304 if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
305 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
306 child)) {
307 if (PIM_DEBUG_PIM_TRACE)
308 zlog_debug(
309 "%s %s: Join(S,G)=%s from %s",
310 __FILE__, __func__,
311 child->sg_str,
312 up->sg_str);
313
314 /* check if the channel can be
315 * inherited into the SG's OIL
316 */
317 child_ch = pim_ifchannel_find(
318 ch->interface,
319 &child->sg);
320 if (pim_upstream_eval_inherit_if(
321 child, child_ch, ch)) {
322 pim_channel_add_oif(
323 child->channel_oil,
324 ch->interface,
325 PIM_OIF_FLAG_PROTO_STAR,
326 __func__);
327 pim_upstream_update_join_desired(
328 pim_ifp->pim, child);
329 }
330 }
331 }
332 }
333 }
334 /* Transition to/from NOINFO ? */
335 if ((old_state == PIM_IFJOIN_NOINFO)
336 || (new_state == PIM_IFJOIN_NOINFO)) {
337
338 if (PIM_DEBUG_PIM_EVENTS) {
339 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
340 ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN"
341 : "UP"),
342 ch->sg_str, ch->interface->name);
343 }
344
345 /*
346 Record uptime of state transition to/from NOINFO
347 */
348 ch->ifjoin_creation = pim_time_monotonic_sec();
349
350 pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
351 pim_ifchannel_update_could_assert(ch);
352 pim_ifchannel_update_assert_tracking_desired(ch);
353 }
354 }
355
356 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state,
357 int flags)
358 {
359 switch (ifjoin_state) {
360 case PIM_IFJOIN_NOINFO:
361 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
362 return "SGRpt(NI)";
363 else
364 return "NOINFO";
365 case PIM_IFJOIN_JOIN:
366 return "JOIN";
367 case PIM_IFJOIN_PRUNE:
368 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
369 return "SGRpt(P)";
370 else
371 return "PRUNE";
372 case PIM_IFJOIN_PRUNE_PENDING:
373 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
374 return "SGRpt(PP)";
375 else
376 return "PRUNEP";
377 case PIM_IFJOIN_PRUNE_TMP:
378 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
379 return "SGRpt(P')";
380 else
381 return "PRUNET";
382 case PIM_IFJOIN_PRUNE_PENDING_TMP:
383 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
384 return "SGRpt(PP')";
385 else
386 return "PRUNEPT";
387 }
388
389 return "ifjoin_bad_state";
390 }
391
392 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
393 {
394 switch (ifassert_state) {
395 case PIM_IFASSERT_NOINFO:
396 return "NOINFO";
397 case PIM_IFASSERT_I_AM_WINNER:
398 return "WINNER";
399 case PIM_IFASSERT_I_AM_LOSER:
400 return "LOSER";
401 }
402
403 return "ifassert_bad_state";
404 }
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 {
414 THREAD_OFF(ch->t_ifassert_timer);
415
416 pim_ifassert_winner_set(ch, PIM_IFASSERT_NOINFO, PIMADDR_ANY,
417 router->infinite_assert_metric);
418 }
419
420 struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, pim_sgaddr *sg)
421 {
422 struct pim_interface *pim_ifp;
423 struct pim_ifchannel *ch;
424 struct pim_ifchannel lookup;
425
426 pim_ifp = ifp->info;
427
428 if (!pim_ifp) {
429 zlog_warn("%s: (S,G)=%pSG: multicast not enabled on interface %s",
430 __func__, sg, ifp->name);
431 return NULL;
432 }
433
434 lookup.sg = *sg;
435 lookup.interface = ifp;
436 ch = RB_FIND(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, &lookup);
437
438 return ch;
439 }
440
441 static void ifmembership_set(struct pim_ifchannel *ch,
442 enum pim_ifmembership membership)
443 {
444 struct pim_interface *pim_ifp = ch->interface->info;
445
446 if (ch->local_ifmembership == membership)
447 return;
448
449 if (PIM_DEBUG_PIM_EVENTS) {
450 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
451 __func__, ch->sg_str,
452 membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE"
453 : "NOINFO",
454 ch->interface->name);
455 }
456
457 ch->local_ifmembership = membership;
458
459 pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
460 pim_ifchannel_update_could_assert(ch);
461 pim_ifchannel_update_assert_tracking_desired(ch);
462 }
463
464
465 void pim_ifchannel_membership_clear(struct interface *ifp)
466 {
467 struct pim_interface *pim_ifp;
468 struct pim_ifchannel *ch;
469
470 pim_ifp = ifp->info;
471 assert(pim_ifp);
472
473 RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb)
474 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
475 }
476
477 void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
478 {
479 struct pim_interface *pim_ifp;
480 struct pim_ifchannel *ch, *ch_tmp;
481
482 pim_ifp = ifp->info;
483 assert(pim_ifp);
484
485 RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch_tmp)
486 delete_on_noinfo(ch);
487 }
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 {
497 pim_sgaddr parent_sg = ch->sg;
498 struct pim_ifchannel *parent = NULL;
499
500 // (S,G)
501 if (!pim_addr_is_any(parent_sg.src) &&
502 !pim_addr_is_any(parent_sg.grp)) {
503 parent_sg.src = PIMADDR_ANY;
504 parent = pim_ifchannel_find(ch->interface, &parent_sg);
505
506 if (parent)
507 listnode_add(parent->sources, ch);
508 return parent;
509 }
510
511 return NULL;
512 }
513
514 struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, pim_sgaddr *sg,
515 uint8_t source_flags, int up_flags)
516 {
517 struct pim_interface *pim_ifp;
518 struct pim_ifchannel *ch;
519 struct pim_upstream *up;
520
521 ch = pim_ifchannel_find(ifp, sg);
522 if (ch) {
523 if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
524 PIM_IF_FLAG_SET_PROTO_PIM(ch->flags);
525
526 if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
527 PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags);
528
529 ch->upstream->flags |= up_flags;
530
531 return ch;
532 }
533
534 pim_ifp = ifp->info;
535
536 ch = XCALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
537
538 ch->flags = 0;
539 if ((source_flags & PIM_ENCODE_RPT_BIT)
540 && !(source_flags & PIM_ENCODE_WC_BIT))
541 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
542
543 ch->interface = ifp;
544 ch->sg = *sg;
545 snprintfrr(ch->sg_str, sizeof(ch->sg_str), "%pSG", sg);
546 ch->parent = pim_ifchannel_find_parent(ch);
547 if (pim_addr_is_any(ch->sg.src)) {
548 ch->sources = list_new();
549 ch->sources->cmp =
550 (int (*)(void *, void *))pim_ifchannel_compare;
551 } else
552 ch->sources = NULL;
553
554 pim_ifchannel_find_new_children(ch);
555 ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
556
557 ch->ifjoin_state = PIM_IFJOIN_NOINFO;
558 ch->t_ifjoin_expiry_timer = NULL;
559 ch->t_ifjoin_prune_pending_timer = NULL;
560 ch->ifjoin_creation = 0;
561
562 RB_INSERT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
563
564 up = pim_upstream_add(pim_ifp->pim, sg, NULL, up_flags, __func__, ch);
565
566 ch->upstream = up;
567
568 listnode_add_sort(up->ifchannels, ch);
569
570 ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
571 ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval(ch);
572
573 ch->ifassert_winner = PIMADDR_ANY;
574
575 /* Assert state */
576 ch->t_ifassert_timer = NULL;
577 ch->ifassert_state = PIM_IFASSERT_NOINFO;
578 reset_ifassert_state(ch);
579 if (pim_macro_ch_could_assert_eval(ch))
580 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
581 else
582 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
583
584 if (pim_macro_assert_tracking_desired_eval(ch))
585 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
586 else
587 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
588
589 /*
590 * advertise MLAG Data to MLAG peer
591 */
592 if (PIM_I_am_DualActive(pim_ifp)) {
593 up->dualactive_ifchannel_count++;
594 /* Sync once for upstream */
595 if (up->dualactive_ifchannel_count == 1) {
596 PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up->flags);
597 pim_mlag_up_local_add(pim_ifp->pim, up);
598 }
599 if (PIM_DEBUG_MLAG)
600 zlog_debug(
601 "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
602 __func__, up->sg_str,
603 up->dualactive_ifchannel_count, up->flags);
604 }
605
606 if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
607 PIM_IF_FLAG_SET_PROTO_PIM(ch->flags);
608
609 if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
610 PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags);
611
612 if (PIM_DEBUG_PIM_TRACE)
613 zlog_debug("%s: ifchannel %s(%s) is created ", __func__,
614 ch->sg_str, ch->interface->name);
615
616 return ch;
617 }
618
619 static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
620 {
621 pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO);
622 pim_forward_stop(ch);
623
624 PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(ch->upstream->flags);
625
626 PIM_IF_FLAG_UNSET_PROTO_PIM(ch->flags);
627
628 delete_on_noinfo(ch);
629 }
630
631 static void on_ifjoin_expiry_timer(struct thread *t)
632 {
633 struct pim_ifchannel *ch;
634
635 ch = THREAD_ARG(t);
636
637 if (PIM_DEBUG_PIM_TRACE)
638 zlog_debug("%s: ifchannel %s expiry timer", __func__,
639 ch->sg_str);
640
641 ifjoin_to_noinfo(ch);
642 /* ch may have been deleted */
643 }
644
645 static void on_ifjoin_prune_pending_timer(struct thread *t)
646 {
647 struct pim_ifchannel *ch;
648 int send_prune_echo; /* boolean */
649 struct interface *ifp;
650 struct pim_interface *pim_ifp;
651
652 ch = THREAD_ARG(t);
653
654 if (PIM_DEBUG_PIM_TRACE)
655 zlog_debug("%s: IFCHANNEL%pSG %s Prune Pending Timer Popped",
656 __func__, &ch->sg,
657 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags));
658
659 if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) {
660 ifp = ch->interface;
661 pim_ifp = ifp->info;
662 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) {
663 /* Send PruneEcho(S,G) ? */
664 send_prune_echo =
665 (listcount(pim_ifp->pim_neighbor_list) > 1);
666
667 if (send_prune_echo) {
668 struct pim_rpf rpf;
669
670 rpf.source_nexthop.interface = ifp;
671 rpf.rpf_addr = pim_ifp->primary_address;
672 pim_jp_agg_single_upstream_send(
673 &rpf, ch->upstream, 0);
674 }
675
676 ifjoin_to_noinfo(ch);
677 } else {
678 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
679 * message on RP path upon prune timer expiry.
680 */
681 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
682 struct pim_upstream *parent =
683 ch->upstream->parent;
684
685 pim_upstream_update_join_desired(pim_ifp->pim,
686 ch->upstream);
687
688 pim_jp_agg_single_upstream_send(&parent->rpf,
689 parent, true);
690 /*
691 * SGRpt prune pending expiry has to install
692 * SG entry with empty olist to drop the SG
693 * traffic incase no other intf exists.
694 * On that scenario, SG entry wouldn't have
695 * got installed until Prune pending timer
696 * expired. So install now.
697 */
698 pim_channel_del_oif(
699 ch->upstream->channel_oil, ifp,
700 PIM_OIF_FLAG_PROTO_STAR, __func__);
701 pim_channel_del_oif(ch->upstream->channel_oil, ifp,
702 PIM_OIF_FLAG_PROTO_PIM, __func__);
703 if (!ch->upstream->channel_oil->installed)
704 pim_upstream_mroute_add(
705 ch->upstream->channel_oil,
706 __func__);
707 }
708 /* from here ch may have been deleted */
709 }
710 }
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 {
716 struct pim_upstream *up;
717 struct pim_interface *pim_ifp = recv_ifp->info;
718 pim_addr rpf_addr;
719
720 /* Upstream (S,G) in Joined state ? */
721 up = pim_upstream_find(pim_ifp->pim, sg);
722 if (!up)
723 return;
724 if (up->join_state != PIM_UPSTREAM_JOINED)
725 return;
726
727 /* Upstream (S,G) in Joined state */
728
729 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
730 /* RPF'(S,G) not found */
731 zlog_warn("%s %s: RPF'%s not found", __FILE__, __func__,
732 up->sg_str);
733 return;
734 }
735
736 rpf_addr = up->rpf.rpf_addr;
737
738 /* upstream directed to RPF'(S,G) ? */
739 if (pim_addr_cmp(upstream, rpf_addr)) {
740 zlog_warn(
741 "%s %s: (S,G)=%s upstream=%pPAs not directed to RPF'(S,G)=%pPAs on interface %s",
742 __FILE__, __func__, up->sg_str, &upstream, &rpf_addr,
743 recv_ifp->name);
744 return;
745 }
746 /* upstream directed to RPF'(S,G) */
747
748 if (is_join) {
749 /* Join(S,G) to RPF'(S,G) */
750 pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
751 return;
752 }
753
754 /* Prune to RPF'(S,G) */
755
756 if (source_flags & PIM_RPT_BIT_MASK) {
757 if (source_flags & PIM_WILDCARD_BIT_MASK) {
758 /* Prune(*,G) to RPF'(S,G) */
759 pim_upstream_join_timer_decrease_to_t_override(
760 "Prune(*,G)", up);
761 return;
762 }
763
764 /* Prune(S,G,rpt) to RPF'(S,G) */
765 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
766 up);
767 return;
768 }
769
770 /* Prune(S,G) to RPF'(S,G) */
771 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
772 }
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 {
778 struct pim_interface *recv_pim_ifp;
779 int is_local; /* boolean */
780
781 recv_pim_ifp = recv_ifp->info;
782 assert(recv_pim_ifp);
783
784 is_local = !pim_addr_cmp(upstream, recv_pim_ifp->primary_address);
785
786 if (is_local)
787 return 0;
788
789 if (PIM_DEBUG_PIM_TRACE_DETAIL)
790 zlog_warn(
791 "%s: recv %s (S,G)=%pSG to non-local upstream=%pPAs on %s",
792 __func__, is_join ? "join" : "prune", sg, &upstream,
793 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 check_recv_upstream(is_join, recv_ifp, upstream, sg, source_flags,
800 holdtime);
801
802 return 1; /* non-local */
803 }
804
805 static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel *ch,
806 struct pim_interface *pim_ifp)
807 {
808 pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN);
809 PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags);
810 /* check if the interface qualifies as an immediate
811 * OIF
812 */
813 if (pim_upstream_evaluate_join_desired_interface(
814 ch->upstream, ch,
815 NULL /*starch*/)) {
816 pim_channel_add_oif(ch->upstream->channel_oil,
817 ch->interface,
818 PIM_OIF_FLAG_PROTO_PIM,
819 __func__);
820 pim_upstream_update_join_desired(pim_ifp->pim,
821 ch->upstream);
822 }
823 }
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 {
830 struct pim_interface *pim_ifp;
831 struct pim_ifchannel *ch;
832
833 if (nonlocal_upstream(1 /* join */, ifp, upstream, sg, source_flags,
834 holdtime)) {
835 return;
836 }
837
838 ch = pim_ifchannel_add(ifp, sg, source_flags,
839 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 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
858 zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s",
859 __func__, ch->sg_str, &neigh_addr, ifp->name);
860
861 assert_action_a5(ch);
862 }
863
864 pim_ifp = ifp->info;
865 assert(pim_ifp);
866
867 switch (ch->ifjoin_state) {
868 case PIM_IFJOIN_NOINFO:
869 pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN);
870 if (pim_macro_chisin_oiflist(ch)) {
871 pim_upstream_inherited_olist(pim_ifp->pim,
872 ch->upstream);
873 pim_forward_start(ch);
874 }
875 /*
876 * If we are going to be a LHR, we need to note it
877 */
878 if (ch->upstream->parent &&
879 (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(
880 ch->upstream->parent->flags))
881 && !(ch->upstream->flags
882 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)) {
883 pim_upstream_ref(ch->upstream,
884 PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
885 __func__);
886 pim_upstream_keep_alive_timer_start(
887 ch->upstream, pim_ifp->pim->keep_alive_time);
888 }
889 break;
890 case PIM_IFJOIN_JOIN:
891 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 if (ch->t_ifjoin_expiry_timer) {
899 unsigned long remain = thread_timer_remain_second(
900 ch->t_ifjoin_expiry_timer);
901 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 return;
921 }
922 }
923 THREAD_OFF(ch->t_ifjoin_expiry_timer);
924 break;
925 case PIM_IFJOIN_PRUNE:
926 if (source_flags & PIM_ENCODE_RPT_BIT) {
927 pim_ifchannel_ifjoin_switch(__func__, ch,
928 PIM_IFJOIN_NOINFO);
929 THREAD_OFF(ch->t_ifjoin_expiry_timer);
930 delete_on_noinfo(ch);
931 return;
932 } else
933 pim_ifchannel_ifjoin_handler(ch, pim_ifp);
934 break;
935 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 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
947
948 /* Check if SGRpt join Received */
949 if ((source_flags & PIM_ENCODE_RPT_BIT) &&
950 !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 THREAD_OFF(ch->t_ifjoin_expiry_timer);
959 pim_ifchannel_ifjoin_switch(__func__, ch,
960 PIM_IFJOIN_NOINFO);
961 return;
962 }
963
964 pim_ifchannel_ifjoin_handler(ch, pim_ifp);
965
966 if (ch->t_ifjoin_expiry_timer) {
967 unsigned long remain = thread_timer_remain_second(
968 ch->t_ifjoin_expiry_timer);
969
970 if (remain > holdtime)
971 return;
972 }
973 THREAD_OFF(ch->t_ifjoin_expiry_timer);
974
975 break;
976 case PIM_IFJOIN_PRUNE_TMP:
977 break;
978 case PIM_IFJOIN_PRUNE_PENDING_TMP:
979 break;
980 }
981
982 if (holdtime != 0xFFFF) {
983 thread_add_timer(router->master, on_ifjoin_expiry_timer, ch,
984 holdtime, &ch->t_ifjoin_expiry_timer);
985 }
986 }
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 {
992 struct pim_ifchannel *ch;
993 struct pim_interface *pim_ifp;
994 int jp_override_interval_msec;
995
996 if (nonlocal_upstream(0 /* prune */, ifp, upstream, sg, source_flags,
997 holdtime)) {
998 return;
999 }
1000
1001 ch = pim_ifchannel_find(ifp, sg);
1002 if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT)) {
1003 if (PIM_DEBUG_PIM_TRACE)
1004 zlog_debug("%s: Received prune with no relevant ifchannel %s%pSG state: %d",
1005 __func__, ifp->name, sg,
1006 source_flags);
1007 return;
1008 }
1009
1010 ch = pim_ifchannel_add(ifp, sg, source_flags,
1011 PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
1012
1013 pim_ifp = ifp->info;
1014
1015 switch (ch->ifjoin_state) {
1016 case PIM_IFJOIN_NOINFO:
1017 if (source_flags & PIM_ENCODE_RPT_BIT) {
1018 if (!(source_flags & PIM_ENCODE_WC_BIT))
1019 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
1020
1021 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
1022 if (listcount(pim_ifp->pim_neighbor_list) > 1)
1023 jp_override_interval_msec =
1024 pim_if_jp_override_interval_msec(ifp);
1025 else
1026 jp_override_interval_msec =
1027 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 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1034 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1035 thread_add_timer_msec(
1036 router->master, on_ifjoin_prune_pending_timer,
1037 ch, jp_override_interval_msec,
1038 &ch->t_ifjoin_prune_pending_timer);
1039 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1040 ch, holdtime,
1041 &ch->t_ifjoin_expiry_timer);
1042 pim_upstream_update_join_desired(pim_ifp->pim,
1043 ch->upstream);
1044 }
1045 break;
1046 case PIM_IFJOIN_PRUNE_PENDING:
1047 /* nothing to do */
1048 break;
1049 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 pim_ifchannel_ifjoin_switch(__func__, ch,
1060 PIM_IFJOIN_PRUNE_PENDING);
1061
1062 if (listcount(pim_ifp->pim_neighbor_list) > 1)
1063 jp_override_interval_msec =
1064 pim_if_jp_override_interval_msec(ifp);
1065 else
1066 jp_override_interval_msec =
1067 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 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1072 thread_add_timer_msec(router->master,
1073 on_ifjoin_prune_pending_timer, ch,
1074 jp_override_interval_msec,
1075 &ch->t_ifjoin_prune_pending_timer);
1076 break;
1077 case PIM_IFJOIN_PRUNE:
1078 if (source_flags & PIM_ENCODE_RPT_BIT) {
1079 THREAD_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 if (ch->t_ifjoin_expiry_timer) {
1090 unsigned long rem = thread_timer_remain_second(
1091 ch->t_ifjoin_expiry_timer);
1092
1093 if (rem > holdtime)
1094 return;
1095 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1096 }
1097
1098 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1099 ch, holdtime,
1100 &ch->t_ifjoin_expiry_timer);
1101 }
1102 break;
1103 case PIM_IFJOIN_PRUNE_TMP:
1104 if (source_flags & PIM_ENCODE_RPT_BIT) {
1105 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
1106 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1107 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1108 ch, holdtime,
1109 &ch->t_ifjoin_expiry_timer);
1110 }
1111 break;
1112 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1113 if (source_flags & PIM_ENCODE_RPT_BIT) {
1114 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
1115 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1116 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1117 ch, holdtime,
1118 &ch->t_ifjoin_expiry_timer);
1119 }
1120 break;
1121 }
1122 }
1123
1124 int pim_ifchannel_local_membership_add(struct interface *ifp, pim_sgaddr *sg,
1125 bool is_vxlan)
1126 {
1127 struct pim_ifchannel *ch, *starch;
1128 struct pim_interface *pim_ifp;
1129 struct pim_instance *pim;
1130 int up_flags;
1131
1132 /* PIM enabled on interface? */
1133 pim_ifp = ifp->info;
1134 if (!pim_ifp) {
1135 if (PIM_DEBUG_EVENTS)
1136 zlog_debug("%s:%pSG Expected pim interface setup for %s",
1137 __func__, sg, ifp->name);
1138 return 0;
1139 }
1140
1141 if (!pim_ifp->pim_enable) {
1142 if (PIM_DEBUG_EVENTS)
1143 zlog_debug("%s:%pSG PIM is not configured on this interface %s",
1144 __func__, sg, ifp->name);
1145 return 0;
1146 }
1147
1148 pim = pim_ifp->pim;
1149
1150 /* skip (*,G) ch creation if G is of type SSM */
1151 if (pim_addr_is_any(sg->src)) {
1152 if (pim_is_grp_ssm(pim, sg->grp)) {
1153 if (PIM_DEBUG_PIM_EVENTS)
1154 zlog_debug("%s: local membership (S,G)=%pSG ignored as group is SSM",
1155 __func__, sg);
1156 return 1;
1157 }
1158 }
1159
1160 /* vxlan term mroutes use ipmr-lo as local member to
1161 * pull down multicast vxlan tunnel traffic
1162 */
1163 up_flags = is_vxlan ? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM :
1164 PIM_UPSTREAM_FLAG_MASK_SRC_IGMP;
1165 ch = pim_ifchannel_add(ifp, sg, 0, up_flags);
1166
1167 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
1168
1169 if (pim_addr_is_any(sg->src)) {
1170 struct pim_upstream *up = pim_upstream_find(pim, sg);
1171 struct pim_upstream *child;
1172 struct listnode *up_node;
1173
1174 starch = ch;
1175
1176 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
1177 if (PIM_DEBUG_EVENTS)
1178 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1179 __FILE__, __func__, child->sg_str,
1180 ifp->name, up->sg_str);
1181
1182 if (!child->rpf.source_nexthop.interface) {
1183 /* when iif unknown, do not inherit */
1184 if (PIM_DEBUG_EVENTS)
1185 zlog_debug(
1186 "Skipped (S,G)=%s(%s) from %s: no iif",
1187 child->sg_str, ifp->name,
1188 up->sg_str);
1189 continue;
1190 }
1191
1192 ch = pim_ifchannel_find(ifp, &child->sg);
1193 if (pim_upstream_evaluate_join_desired_interface(
1194 child, ch, starch)) {
1195 pim_channel_add_oif(child->channel_oil, ifp,
1196 PIM_OIF_FLAG_PROTO_STAR,
1197 __func__);
1198 pim_upstream_update_join_desired(pim, child);
1199 }
1200 }
1201
1202 if (pim->spt.switchover == PIM_SPT_INFINITY) {
1203 if (pim->spt.plist) {
1204 struct prefix_list *plist = prefix_list_lookup(
1205 AFI_IP, pim->spt.plist);
1206 struct prefix g;
1207
1208 pim_addr_to_prefix(&g, up->sg.grp);
1209 if (prefix_list_apply_ext(plist, NULL, &g,
1210 true) ==
1211 PREFIX_DENY) {
1212 pim_channel_add_oif(
1213 up->channel_oil, pim->regiface,
1214 PIM_OIF_FLAG_PROTO_GM,
1215 __func__);
1216 }
1217 }
1218 } else
1219 pim_channel_add_oif(up->channel_oil, pim->regiface,
1220 PIM_OIF_FLAG_PROTO_GM, __func__);
1221 }
1222
1223 return 1;
1224 }
1225
1226 void pim_ifchannel_local_membership_del(struct interface *ifp, pim_sgaddr *sg)
1227 {
1228 struct pim_ifchannel *starch, *ch, *orig;
1229 struct pim_interface *pim_ifp;
1230
1231 /* PIM enabled on interface? */
1232 pim_ifp = ifp->info;
1233 if (!pim_ifp)
1234 return;
1235 if (!pim_ifp->pim_enable)
1236 return;
1237
1238 orig = ch = pim_ifchannel_find(ifp, sg);
1239 if (!ch)
1240 return;
1241 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
1242
1243 if (pim_addr_is_any(sg->src)) {
1244 struct pim_upstream *up = pim_upstream_find(pim_ifp->pim, sg);
1245 struct pim_upstream *child;
1246 struct listnode *up_node, *up_nnode;
1247
1248 starch = ch;
1249
1250 for (ALL_LIST_ELEMENTS(up->sources, up_node, up_nnode, child)) {
1251 struct channel_oil *c_oil = child->channel_oil;
1252 struct pim_ifchannel *chchannel =
1253 pim_ifchannel_find(ifp, &child->sg);
1254
1255 pim_ifp = ifp->info;
1256
1257 if (PIM_DEBUG_EVENTS)
1258 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1259 __FILE__, __func__, up->sg_str,
1260 ifp->name, child->sg_str);
1261
1262 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 if (!pim_upstream_evaluate_join_desired_interface(
1270 child, ch, starch) ||
1271 (!chchannel &&
1272 oil_if_has(c_oil, pim_ifp->mroute_vif_index))) {
1273 pim_channel_del_inherited_oif(c_oil, ifp,
1274 __func__);
1275 }
1276
1277 /* Child node removal/ref count-- will happen as part of
1278 * parent' delete_no_info */
1279 }
1280 }
1281
1282 /* Resettng the IGMP flags here */
1283 if (orig->upstream)
1284 PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(orig->upstream->flags);
1285
1286 PIM_IF_FLAG_UNSET_PROTO_IGMP(orig->flags);
1287
1288 delete_on_noinfo(orig);
1289 }
1290
1291 void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
1292 {
1293 int old_couldassert =
1294 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
1295 int new_couldassert =
1296 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
1297
1298 if (new_couldassert == old_couldassert)
1299 return;
1300
1301 if (PIM_DEBUG_PIM_EVENTS)
1302 zlog_debug("%s: CouldAssert(%pPAs,%pPAs,%s) changed from %d to %d",
1303 __func__, &ch->sg.src, &ch->sg.grp,
1304 ch->interface->name, old_couldassert,
1305 new_couldassert);
1306
1307 if (new_couldassert) {
1308 /* CouldAssert(S,G,I) switched from false to true */
1309 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
1310 } else {
1311 /* CouldAssert(S,G,I) switched from true to false */
1312 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
1313
1314 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
1315 assert_action_a4(ch);
1316 }
1317 }
1318
1319 pim_ifchannel_update_my_assert_metric(ch);
1320 }
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 {
1332 struct pim_assert_metric my_metric_new =
1333 pim_macro_ch_my_assert_metric_eval(ch);
1334
1335 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
1336 return;
1337
1338 if (PIM_DEBUG_PIM_EVENTS)
1339 zlog_debug(
1340 "%s: my_assert_metric(%pPAs,%pPAs,%s) changed from %u,%u,%u,%pPAs to %u,%u,%u,%pPAs",
1341 __func__, &ch->sg.src, &ch->sg.grp, ch->interface->name,
1342 ch->ifassert_my_metric.rpt_bit_flag,
1343 ch->ifassert_my_metric.metric_preference,
1344 ch->ifassert_my_metric.route_metric,
1345 &ch->ifassert_my_metric.ip_address,
1346 my_metric_new.rpt_bit_flag,
1347 my_metric_new.metric_preference,
1348 my_metric_new.route_metric, &my_metric_new.ip_address);
1349
1350 ch->ifassert_my_metric = my_metric_new;
1351
1352 if (pim_assert_metric_better(&ch->ifassert_my_metric,
1353 &ch->ifassert_winner_metric)) {
1354 assert_action_a5(ch);
1355 }
1356 }
1357
1358 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
1359 {
1360 int old_atd = PIM_FORCE_BOOLEAN(
1361 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
1362 int new_atd =
1363 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
1364
1365 if (new_atd == old_atd)
1366 return;
1367
1368 if (PIM_DEBUG_PIM_EVENTS)
1369 zlog_debug(
1370 "%s: AssertTrackingDesired(%pPAs,%pPAs,%s) changed from %d to %d",
1371 __func__, &ch->sg.src, &ch->sg.grp, ch->interface->name,
1372 old_atd, new_atd);
1373
1374 if (new_atd) {
1375 /* AssertTrackingDesired(S,G,I) switched from false to true */
1376 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
1377 } else {
1378 /* AssertTrackingDesired(S,G,I) switched from true to false */
1379 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
1380
1381 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
1382 assert_action_a5(ch);
1383 }
1384 }
1385 }
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 {
1395 struct pim_interface *new_pim_ifp = new_ifp->info;
1396 struct pim_instance *pim = new_pim_ifp->pim;
1397 struct interface *ifp;
1398
1399 FOR_ALL_INTERFACES (pim->vrf, ifp) {
1400 struct pim_interface *loop_pim_ifp = ifp->info;
1401 struct pim_ifchannel *ch;
1402
1403 if (!loop_pim_ifp)
1404 continue;
1405
1406 if (new_pim_ifp == loop_pim_ifp)
1407 continue;
1408
1409 RB_FOREACH (ch, pim_ifchannel_rb, &loop_pim_ifp->ifchannel_rb) {
1410 if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
1411 struct pim_upstream *up = ch->upstream;
1412 if ((!up->channel_oil)
1413 && (up->rpf.source_nexthop
1414 .interface == new_ifp))
1415 pim_forward_start(ch);
1416 }
1417 }
1418 }
1419 }
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 {
1431 bool send_upstream_starg = false;
1432 struct pim_ifchannel *child;
1433 struct listnode *ch_node, *nch_node;
1434 struct pim_instance *pim =
1435 ((struct pim_interface *)ch->interface->info)->pim;
1436 struct pim_upstream *starup = ch->upstream;
1437
1438 if (PIM_DEBUG_PIM_TRACE)
1439 zlog_debug(
1440 "%s: %s %s eom: %d join %u", __func__,
1441 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
1442 ch->sg_str, eom, join);
1443 if (!ch->sources)
1444 return;
1445
1446 for (ALL_LIST_ELEMENTS(ch->sources, ch_node, nch_node, child)) {
1447 if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1448 continue;
1449
1450 switch (child->ifjoin_state) {
1451 case PIM_IFJOIN_NOINFO:
1452 case PIM_IFJOIN_JOIN:
1453 break;
1454 case PIM_IFJOIN_PRUNE:
1455 if (!eom)
1456 child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
1457 break;
1458 case PIM_IFJOIN_PRUNE_PENDING:
1459 if (!eom)
1460 child->ifjoin_state =
1461 PIM_IFJOIN_PRUNE_PENDING_TMP;
1462 break;
1463 case PIM_IFJOIN_PRUNE_TMP:
1464 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1465 if (!eom)
1466 break;
1467
1468 if (child->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING_TMP)
1469 THREAD_OFF(child->t_ifjoin_prune_pending_timer);
1470 THREAD_OFF(child->t_ifjoin_expiry_timer);
1471
1472 PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
1473 child->ifjoin_state = PIM_IFJOIN_NOINFO;
1474
1475 if ((I_am_RP(pim, child->sg.grp)) &&
1476 (!pim_upstream_empty_inherited_olist(
1477 child->upstream))) {
1478 pim_channel_add_oif(
1479 child->upstream->channel_oil,
1480 ch->interface, PIM_OIF_FLAG_PROTO_STAR,
1481 __func__);
1482 pim_upstream_update_join_desired(pim,
1483 child->upstream);
1484 }
1485 send_upstream_starg = true;
1486
1487 delete_on_noinfo(child);
1488 break;
1489 }
1490 }
1491
1492 if (send_upstream_starg)
1493 pim_jp_agg_single_upstream_send(&starup->rpf, starup, true);
1494 }