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