]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_ifchannel.c
pim6d: Completing "ipv6 mld" command.
[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 if (ch->upstream)
543 ch->upstream->flags |= up_flags;
544 else if (PIM_DEBUG_EVENTS)
545 zlog_debug("%s:%pSG No Upstream found", __func__, sg);
546
547 return ch;
548 }
549
550 pim_ifp = ifp->info;
551
552 ch = XCALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
553
554 ch->flags = 0;
555 if ((source_flags & PIM_ENCODE_RPT_BIT)
556 && !(source_flags & PIM_ENCODE_WC_BIT))
557 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
558
559 ch->interface = ifp;
560 ch->sg = *sg;
561 snprintfrr(ch->sg_str, sizeof(ch->sg_str), "%pSG", sg);
562 ch->parent = pim_ifchannel_find_parent(ch);
563 if (pim_addr_is_any(ch->sg.src)) {
564 ch->sources = list_new();
565 ch->sources->cmp =
566 (int (*)(void *, void *))pim_ifchannel_compare;
567 } else
568 ch->sources = NULL;
569
570 pim_ifchannel_find_new_children(ch);
571 ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
572
573 ch->ifjoin_state = PIM_IFJOIN_NOINFO;
574 ch->t_ifjoin_expiry_timer = NULL;
575 ch->t_ifjoin_prune_pending_timer = NULL;
576 ch->ifjoin_creation = 0;
577
578 RB_INSERT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
579
580 up = pim_upstream_add(pim_ifp->pim, sg, NULL, up_flags, __func__, ch);
581
582 ch->upstream = up;
583
584 listnode_add_sort(up->ifchannels, ch);
585
586 ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
587 ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval(ch);
588
589 ch->ifassert_winner = PIMADDR_ANY;
590
591 /* Assert state */
592 ch->t_ifassert_timer = NULL;
593 ch->ifassert_state = PIM_IFASSERT_NOINFO;
594 reset_ifassert_state(ch);
595 if (pim_macro_ch_could_assert_eval(ch))
596 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
597 else
598 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
599
600 if (pim_macro_assert_tracking_desired_eval(ch))
601 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
602 else
603 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
604
605 /*
606 * advertise MLAG Data to MLAG peer
607 */
608 if (PIM_I_am_DualActive(pim_ifp)) {
609 up->dualactive_ifchannel_count++;
610 /* Sync once for upstream */
611 if (up->dualactive_ifchannel_count == 1) {
612 PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up->flags);
613 pim_mlag_up_local_add(pim_ifp->pim, up);
614 }
615 if (PIM_DEBUG_MLAG)
616 zlog_debug(
617 "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
618 __func__, up->sg_str,
619 up->dualactive_ifchannel_count, up->flags);
620 }
621
622 if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
623 PIM_IF_FLAG_SET_PROTO_PIM(ch->flags);
624
625 if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
626 PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags);
627
628 if (PIM_DEBUG_PIM_TRACE)
629 zlog_debug("%s: ifchannel %s(%s) is created ", __func__,
630 ch->sg_str, ch->interface->name);
631
632 return ch;
633 }
634
635 static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
636 {
637 pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO);
638 pim_forward_stop(ch);
639
640 if (ch->upstream)
641 PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(ch->upstream->flags);
642
643 PIM_IF_FLAG_UNSET_PROTO_PIM(ch->flags);
644
645 delete_on_noinfo(ch);
646 }
647
648 static void on_ifjoin_expiry_timer(struct thread *t)
649 {
650 struct pim_ifchannel *ch;
651
652 ch = THREAD_ARG(t);
653
654 if (PIM_DEBUG_PIM_TRACE)
655 zlog_debug("%s: ifchannel %s expiry timer", __func__,
656 ch->sg_str);
657
658 ifjoin_to_noinfo(ch);
659 /* ch may have been deleted */
660 }
661
662 static void on_ifjoin_prune_pending_timer(struct thread *t)
663 {
664 struct pim_ifchannel *ch;
665 int send_prune_echo; /* boolean */
666 struct interface *ifp;
667 struct pim_interface *pim_ifp;
668
669 ch = THREAD_ARG(t);
670
671 if (PIM_DEBUG_PIM_TRACE)
672 zlog_debug("%s: IFCHANNEL%pSG %s Prune Pending Timer Popped",
673 __func__, &ch->sg,
674 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags));
675
676 if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) {
677 ifp = ch->interface;
678 pim_ifp = ifp->info;
679 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) {
680 /* Send PruneEcho(S,G) ? */
681 send_prune_echo =
682 (listcount(pim_ifp->pim_neighbor_list) > 1);
683
684 if (send_prune_echo) {
685 struct pim_rpf rpf;
686
687 rpf.source_nexthop.interface = ifp;
688 pim_addr_to_prefix(&rpf.rpf_addr,
689 pim_ifp->primary_address);
690 pim_jp_agg_single_upstream_send(
691 &rpf, ch->upstream, 0);
692 }
693
694 ifjoin_to_noinfo(ch);
695 } else {
696 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
697 * message on RP path upon prune timer expiry.
698 */
699 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
700 if (ch->upstream) {
701 struct pim_upstream *parent =
702 ch->upstream->parent;
703
704 pim_upstream_update_join_desired(pim_ifp->pim,
705 ch->upstream);
706
707 pim_jp_agg_single_upstream_send(&parent->rpf,
708 parent, true);
709 /*
710 * SGRpt prune pending expiry has to install
711 * SG entry with empty olist to drop the SG
712 * traffic incase no other intf exists.
713 * On that scenario, SG entry wouldn't have
714 * got installed until Prune pending timer
715 * expired. So install now.
716 */
717 pim_channel_del_oif(
718 ch->upstream->channel_oil, ifp,
719 PIM_OIF_FLAG_PROTO_STAR, __func__);
720 if (!ch->upstream->channel_oil->installed)
721 pim_upstream_mroute_add(
722 ch->upstream->channel_oil,
723 __func__);
724 }
725 }
726 /* from here ch may have been deleted */
727 }
728 }
729
730 static void check_recv_upstream(int is_join, struct interface *recv_ifp,
731 pim_addr upstream, pim_sgaddr *sg,
732 uint8_t source_flags, int holdtime)
733 {
734 struct pim_upstream *up;
735 struct pim_interface *pim_ifp = recv_ifp->info;
736 pim_addr rpf_addr;
737
738 /* Upstream (S,G) in Joined state ? */
739 up = pim_upstream_find(pim_ifp->pim, sg);
740 if (!up)
741 return;
742 if (up->join_state != PIM_UPSTREAM_JOINED)
743 return;
744
745 /* Upstream (S,G) in Joined state */
746
747 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
748 /* RPF'(S,G) not found */
749 zlog_warn("%s %s: RPF'%s not found", __FILE__, __func__,
750 up->sg_str);
751 return;
752 }
753
754 rpf_addr = pim_addr_from_prefix(&up->rpf.rpf_addr);
755
756 /* upstream directed to RPF'(S,G) ? */
757 if (pim_addr_cmp(upstream, rpf_addr)) {
758 zlog_warn(
759 "%s %s: (S,G)=%s upstream=%pPAs not directed to RPF'(S,G)=%pPAs on interface %s",
760 __FILE__, __func__, up->sg_str, &upstream, &rpf_addr,
761 recv_ifp->name);
762 return;
763 }
764 /* upstream directed to RPF'(S,G) */
765
766 if (is_join) {
767 /* Join(S,G) to RPF'(S,G) */
768 pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
769 return;
770 }
771
772 /* Prune to RPF'(S,G) */
773
774 if (source_flags & PIM_RPT_BIT_MASK) {
775 if (source_flags & PIM_WILDCARD_BIT_MASK) {
776 /* Prune(*,G) to RPF'(S,G) */
777 pim_upstream_join_timer_decrease_to_t_override(
778 "Prune(*,G)", up);
779 return;
780 }
781
782 /* Prune(S,G,rpt) to RPF'(S,G) */
783 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
784 up);
785 return;
786 }
787
788 /* Prune(S,G) to RPF'(S,G) */
789 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
790 }
791
792 static int nonlocal_upstream(int is_join, struct interface *recv_ifp,
793 pim_addr upstream, pim_sgaddr *sg,
794 uint8_t source_flags, uint16_t holdtime)
795 {
796 struct pim_interface *recv_pim_ifp;
797 int is_local; /* boolean */
798
799 recv_pim_ifp = recv_ifp->info;
800 assert(recv_pim_ifp);
801
802 is_local = !pim_addr_cmp(upstream, recv_pim_ifp->primary_address);
803
804 if (is_local)
805 return 0;
806
807 if (PIM_DEBUG_PIM_TRACE_DETAIL)
808 zlog_warn(
809 "%s: recv %s (S,G)=%pSG to non-local upstream=%pPAs on %s",
810 __func__, is_join ? "join" : "prune", sg, &upstream,
811 recv_ifp->name);
812
813 /*
814 * Since recv upstream addr was not directed to our primary
815 * address, check if we should react to it in any way.
816 */
817 check_recv_upstream(is_join, recv_ifp, upstream, sg, source_flags,
818 holdtime);
819
820 return 1; /* non-local */
821 }
822
823 static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel *ch,
824 struct pim_interface *pim_ifp)
825 {
826 pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN);
827 PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags);
828 /* check if the interface qualifies as an immediate
829 * OIF
830 */
831 if (pim_upstream_evaluate_join_desired_interface(
832 ch->upstream, ch,
833 NULL /*starch*/)) {
834 pim_channel_add_oif(ch->upstream->channel_oil,
835 ch->interface,
836 PIM_OIF_FLAG_PROTO_PIM,
837 __func__);
838 pim_upstream_update_join_desired(pim_ifp->pim,
839 ch->upstream);
840 }
841 }
842
843
844 void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr,
845 pim_addr upstream, pim_sgaddr *sg,
846 uint8_t source_flags, uint16_t holdtime)
847 {
848 struct pim_interface *pim_ifp;
849 struct pim_ifchannel *ch;
850
851 if (nonlocal_upstream(1 /* join */, ifp, upstream, sg, source_flags,
852 holdtime)) {
853 return;
854 }
855
856 ch = pim_ifchannel_add(ifp, sg, source_flags,
857 PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
858
859 /*
860 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
861
862 Transitions from "I am Assert Loser" State
863
864 Receive Join(S,G) on Interface I
865
866 We receive a Join(S,G) that has the Upstream Neighbor Address
867 field set to my primary IP address on interface I. The action is
868 to transition to NoInfo state, delete this (S,G) assert state
869 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
870 to operate.
871
872 Notice: The nonlocal_upstream() test above ensures the upstream
873 address of the join message is our primary address.
874 */
875 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
876 zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s",
877 __func__, ch->sg_str, &neigh_addr, ifp->name);
878
879 assert_action_a5(ch);
880 }
881
882 pim_ifp = ifp->info;
883 assert(pim_ifp);
884
885 switch (ch->ifjoin_state) {
886 case PIM_IFJOIN_NOINFO:
887 pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN);
888 if (pim_macro_chisin_oiflist(ch)) {
889 pim_upstream_inherited_olist(pim_ifp->pim,
890 ch->upstream);
891 pim_forward_start(ch);
892 }
893 /*
894 * If we are going to be a LHR, we need to note it
895 */
896 if (ch->upstream->parent &&
897 (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(
898 ch->upstream->parent->flags))
899 && !(ch->upstream->flags
900 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)) {
901 pim_upstream_ref(ch->upstream,
902 PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
903 __func__);
904 pim_upstream_keep_alive_timer_start(
905 ch->upstream, pim_ifp->pim->keep_alive_time);
906 }
907 break;
908 case PIM_IFJOIN_JOIN:
909 assert(!ch->t_ifjoin_prune_pending_timer);
910
911 /*
912 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
913 a
914 previously received join message with holdtime=0xFFFF.
915 */
916 if (ch->t_ifjoin_expiry_timer) {
917 unsigned long remain = thread_timer_remain_second(
918 ch->t_ifjoin_expiry_timer);
919 if (remain > holdtime) {
920 /*
921 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
922 Messages
923
924 Transitions from Join State
925
926 The (S,G) downstream state machine on
927 interface I remains in
928 Join state, and the Expiry Timer (ET) is
929 restarted, set to
930 maximum of its current value and the HoldTime
931 from the
932 triggering Join/Prune message.
933
934 Conclusion: Do not change the ET if the
935 current value is
936 higher than the received join holdtime.
937 */
938 return;
939 }
940 }
941 THREAD_OFF(ch->t_ifjoin_expiry_timer);
942 break;
943 case PIM_IFJOIN_PRUNE:
944 if (source_flags & PIM_ENCODE_RPT_BIT) {
945 pim_ifchannel_ifjoin_switch(__func__, ch,
946 PIM_IFJOIN_NOINFO);
947 THREAD_OFF(ch->t_ifjoin_expiry_timer);
948 delete_on_noinfo(ch);
949 return;
950 } else
951 pim_ifchannel_ifjoin_handler(ch, pim_ifp);
952 break;
953 case PIM_IFJOIN_PRUNE_PENDING:
954 /*
955 * Transitions from Prune-Pending State (Receive Join)
956 * RFC 7761 Sec 4.5.2:
957 * The (S,G) downstream state machine on interface I
958 * transitions to the Join state. The Prune-Pending Timer is
959 * canceled (without triggering an expiry event). The
960 * Expiry Timer (ET) is restarted and is then set to the
961 * maximum of its current value and the HoldTime from the
962 * triggering Join/Prune message.
963 */
964 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
965
966 /* Check if SGRpt join Received */
967 if ((source_flags & PIM_ENCODE_RPT_BIT) &&
968 !pim_addr_is_any(sg->src)) {
969 /*
970 * Transitions from Prune-Pending State (Rcv SGRpt Join)
971 * RFC 7761 Sec 4.5.3:
972 * The (S,G,rpt) downstream state machine on interface
973 * I transitions to the NoInfo state.The ET and PPT are
974 * cancelled.
975 */
976 THREAD_OFF(ch->t_ifjoin_expiry_timer);
977 pim_ifchannel_ifjoin_switch(__func__, ch,
978 PIM_IFJOIN_NOINFO);
979 return;
980 }
981
982 pim_ifchannel_ifjoin_handler(ch, pim_ifp);
983
984 if (ch->t_ifjoin_expiry_timer) {
985 unsigned long remain = thread_timer_remain_second(
986 ch->t_ifjoin_expiry_timer);
987
988 if (remain > holdtime)
989 return;
990 }
991 THREAD_OFF(ch->t_ifjoin_expiry_timer);
992
993 break;
994 case PIM_IFJOIN_PRUNE_TMP:
995 break;
996 case PIM_IFJOIN_PRUNE_PENDING_TMP:
997 break;
998 }
999
1000 if (holdtime != 0xFFFF) {
1001 thread_add_timer(router->master, on_ifjoin_expiry_timer, ch,
1002 holdtime, &ch->t_ifjoin_expiry_timer);
1003 }
1004 }
1005
1006 void pim_ifchannel_prune(struct interface *ifp, pim_addr upstream,
1007 pim_sgaddr *sg, uint8_t source_flags,
1008 uint16_t holdtime)
1009 {
1010 struct pim_ifchannel *ch;
1011 struct pim_interface *pim_ifp;
1012 int jp_override_interval_msec;
1013
1014 if (nonlocal_upstream(0 /* prune */, ifp, upstream, sg, source_flags,
1015 holdtime)) {
1016 return;
1017 }
1018
1019 ch = pim_ifchannel_find(ifp, sg);
1020 if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT)) {
1021 if (PIM_DEBUG_PIM_TRACE)
1022 zlog_debug("%s: Received prune with no relevant ifchannel %s%pSG state: %d",
1023 __func__, ifp->name, sg,
1024 source_flags);
1025 return;
1026 }
1027
1028 ch = pim_ifchannel_add(ifp, sg, source_flags,
1029 PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
1030
1031 pim_ifp = ifp->info;
1032
1033 switch (ch->ifjoin_state) {
1034 case PIM_IFJOIN_NOINFO:
1035 if (source_flags & PIM_ENCODE_RPT_BIT) {
1036 if (!(source_flags & PIM_ENCODE_WC_BIT))
1037 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
1038
1039 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
1040 if (listcount(pim_ifp->pim_neighbor_list) > 1)
1041 jp_override_interval_msec =
1042 pim_if_jp_override_interval_msec(ifp);
1043 else
1044 jp_override_interval_msec =
1045 0; /* schedule to expire immediately */
1046 /* If we called ifjoin_prune() directly instead, care
1047 should
1048 be taken not to use "ch" afterwards since it would be
1049 deleted. */
1050
1051 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1052 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1053 thread_add_timer_msec(
1054 router->master, on_ifjoin_prune_pending_timer,
1055 ch, jp_override_interval_msec,
1056 &ch->t_ifjoin_prune_pending_timer);
1057 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1058 ch, holdtime,
1059 &ch->t_ifjoin_expiry_timer);
1060 pim_upstream_update_join_desired(pim_ifp->pim,
1061 ch->upstream);
1062 }
1063 break;
1064 case PIM_IFJOIN_PRUNE_PENDING:
1065 /* nothing to do */
1066 break;
1067 case PIM_IFJOIN_JOIN:
1068 /*
1069 * The (S,G) downstream state machine on interface I
1070 * transitions to the Prune-Pending state. The
1071 * Prune-Pending Timer is started. It is set to the
1072 * J/P_Override_Interval(I) if the router has more than one
1073 * neighbor on that interface; otherwise, it is set to zero,
1074 * causing it to expire immediately.
1075 */
1076
1077 pim_ifchannel_ifjoin_switch(__func__, ch,
1078 PIM_IFJOIN_PRUNE_PENDING);
1079
1080 if (listcount(pim_ifp->pim_neighbor_list) > 1)
1081 jp_override_interval_msec =
1082 pim_if_jp_override_interval_msec(ifp);
1083 else
1084 jp_override_interval_msec =
1085 0; /* schedule to expire immediately */
1086 /* If we called ifjoin_prune() directly instead, care should
1087 be taken not to use "ch" afterwards since it would be
1088 deleted. */
1089 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1090 thread_add_timer_msec(router->master,
1091 on_ifjoin_prune_pending_timer, ch,
1092 jp_override_interval_msec,
1093 &ch->t_ifjoin_prune_pending_timer);
1094 break;
1095 case PIM_IFJOIN_PRUNE:
1096 if (source_flags & PIM_ENCODE_RPT_BIT) {
1097 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1098 /*
1099 * While in Prune State, Receive SGRpt Prune.
1100 * RFC 7761 Sec 4.5.3:
1101 * The (S,G,rpt) downstream state machine on interface I
1102 * remains in Prune state. The Expiry Timer (ET) is
1103 * restarted and is then set to the maximum of its
1104 * current value and the HoldTime from the triggering
1105 * Join/Prune message.
1106 */
1107 if (ch->t_ifjoin_expiry_timer) {
1108 unsigned long rem = thread_timer_remain_second(
1109 ch->t_ifjoin_expiry_timer);
1110
1111 if (rem > holdtime)
1112 return;
1113 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1114 }
1115
1116 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1117 ch, holdtime,
1118 &ch->t_ifjoin_expiry_timer);
1119 }
1120 break;
1121 case PIM_IFJOIN_PRUNE_TMP:
1122 if (source_flags & PIM_ENCODE_RPT_BIT) {
1123 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
1124 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1125 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1126 ch, holdtime,
1127 &ch->t_ifjoin_expiry_timer);
1128 }
1129 break;
1130 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1131 if (source_flags & PIM_ENCODE_RPT_BIT) {
1132 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
1133 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1134 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1135 ch, holdtime,
1136 &ch->t_ifjoin_expiry_timer);
1137 }
1138 break;
1139 }
1140 }
1141
1142 int pim_ifchannel_local_membership_add(struct interface *ifp, pim_sgaddr *sg,
1143 bool is_vxlan)
1144 {
1145 struct pim_ifchannel *ch, *starch;
1146 struct pim_interface *pim_ifp;
1147 struct pim_instance *pim;
1148 int up_flags;
1149
1150 /* PIM enabled on interface? */
1151 pim_ifp = ifp->info;
1152 if (!pim_ifp) {
1153 if (PIM_DEBUG_EVENTS)
1154 zlog_debug("%s:%pSG Expected pim interface setup for %s",
1155 __func__, sg, ifp->name);
1156 return 0;
1157 }
1158
1159 if (!pim_ifp->pim_enable) {
1160 if (PIM_DEBUG_EVENTS)
1161 zlog_debug("%s:%pSG PIM is not configured on this interface %s",
1162 __func__, sg, ifp->name);
1163 return 0;
1164 }
1165
1166 pim = pim_ifp->pim;
1167
1168 /* skip (*,G) ch creation if G is of type SSM */
1169 if (pim_addr_is_any(sg->src)) {
1170 if (pim_is_grp_ssm(pim, sg->grp)) {
1171 if (PIM_DEBUG_PIM_EVENTS)
1172 zlog_debug("%s: local membership (S,G)=%pSG ignored as group is SSM",
1173 __func__, sg);
1174 return 1;
1175 }
1176 }
1177
1178 /* vxlan term mroutes use ipmr-lo as local member to
1179 * pull down multicast vxlan tunnel traffic
1180 */
1181 up_flags = is_vxlan ? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM :
1182 PIM_UPSTREAM_FLAG_MASK_SRC_IGMP;
1183 ch = pim_ifchannel_add(ifp, sg, 0, up_flags);
1184
1185 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
1186
1187 if (pim_addr_is_any(sg->src)) {
1188 struct pim_upstream *up = pim_upstream_find(pim, sg);
1189 struct pim_upstream *child;
1190 struct listnode *up_node;
1191
1192 starch = ch;
1193
1194 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
1195 if (PIM_DEBUG_EVENTS)
1196 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1197 __FILE__, __func__, child->sg_str,
1198 ifp->name, up->sg_str);
1199
1200 if (!child->rpf.source_nexthop.interface) {
1201 /* when iif unknown, do not inherit */
1202 if (PIM_DEBUG_EVENTS)
1203 zlog_debug(
1204 "Skipped (S,G)=%s(%s) from %s: no iif",
1205 child->sg_str, ifp->name,
1206 up->sg_str);
1207 continue;
1208 }
1209
1210 ch = pim_ifchannel_find(ifp, &child->sg);
1211 if (pim_upstream_evaluate_join_desired_interface(
1212 child, ch, starch)) {
1213 pim_channel_add_oif(child->channel_oil, ifp,
1214 PIM_OIF_FLAG_PROTO_STAR,
1215 __func__);
1216 pim_upstream_update_join_desired(pim, child);
1217 }
1218 }
1219
1220 if (pim->spt.switchover == PIM_SPT_INFINITY) {
1221 if (pim->spt.plist) {
1222 struct prefix_list *plist = prefix_list_lookup(
1223 AFI_IP, pim->spt.plist);
1224 struct prefix g;
1225
1226 pim_addr_to_prefix(&g, up->sg.grp);
1227 if (prefix_list_apply(plist, &g)
1228 == PREFIX_DENY) {
1229 pim_channel_add_oif(
1230 up->channel_oil, pim->regiface,
1231 PIM_OIF_FLAG_PROTO_GM,
1232 __func__);
1233 }
1234 }
1235 } else
1236 pim_channel_add_oif(up->channel_oil, pim->regiface,
1237 PIM_OIF_FLAG_PROTO_GM, __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_ifp->pim_enable)
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 }