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