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