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