]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_ifchannel.c
Merge pull request #2133 from Fredi-raspall/fix_label_manager
[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 if (!ch) {
524 zlog_warn(
525 "%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
526 __PRETTY_FUNCTION__, pim_str_sg_dump(sg), ifp->name);
527 return NULL;
528 }
529
530 ch->flags = 0;
531 if ((source_flags & PIM_ENCODE_RPT_BIT)
532 && !(source_flags & PIM_ENCODE_WC_BIT))
533 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
534
535 ch->interface = ifp;
536 ch->sg = *sg;
537 pim_str_sg_set(sg, ch->sg_str);
538 ch->parent = pim_ifchannel_find_parent(ch);
539 if (ch->sg.src.s_addr == INADDR_ANY) {
540 ch->sources = list_new();
541 ch->sources->cmp =
542 (int (*)(void *, void *))pim_ifchannel_compare;
543 } else
544 ch->sources = NULL;
545
546 pim_ifchannel_find_new_children(ch);
547 ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
548
549 ch->ifjoin_state = PIM_IFJOIN_NOINFO;
550 ch->t_ifjoin_expiry_timer = NULL;
551 ch->t_ifjoin_prune_pending_timer = NULL;
552 ch->ifjoin_creation = 0;
553
554 RB_INSERT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
555
556 up = pim_upstream_add(pim_ifp->pim, sg, NULL, up_flags,
557 __PRETTY_FUNCTION__, ch);
558
559 if (!up) {
560 zlog_err(
561 "%s: could not attach upstream (S,G)=%s on interface %s",
562 __PRETTY_FUNCTION__, pim_str_sg_dump(sg), ifp->name);
563
564 if (ch->parent)
565 listnode_delete(ch->parent->sources, ch);
566
567 pim_ifchannel_remove_children(ch);
568 if (ch->sources)
569 list_delete_and_null(&ch->sources);
570
571 THREAD_OFF(ch->t_ifjoin_expiry_timer);
572 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
573 THREAD_OFF(ch->t_ifassert_timer);
574
575 RB_REMOVE(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
576 XFREE(MTYPE_PIM_IFCHANNEL, ch);
577 return NULL;
578 }
579 ch->upstream = up;
580
581 listnode_add_sort(up->ifchannels, ch);
582
583 ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
584 ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval(ch);
585
586 ch->ifassert_winner.s_addr = 0;
587
588 /* Assert state */
589 ch->t_ifassert_timer = NULL;
590 ch->ifassert_state = PIM_IFASSERT_NOINFO;
591 reset_ifassert_state(ch);
592 if (pim_macro_ch_could_assert_eval(ch))
593 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
594 else
595 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
596
597 if (pim_macro_assert_tracking_desired_eval(ch))
598 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
599 else
600 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
601
602 if (PIM_DEBUG_PIM_TRACE)
603 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__,
604 ch->sg_str);
605
606 return ch;
607 }
608
609 static void ifjoin_to_noinfo(struct pim_ifchannel *ch, bool ch_del)
610 {
611 pim_forward_stop(ch, !ch_del);
612 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
613 if (ch_del)
614 delete_on_noinfo(ch);
615 }
616
617 static int on_ifjoin_expiry_timer(struct thread *t)
618 {
619 struct pim_ifchannel *ch;
620
621 ch = THREAD_ARG(t);
622
623 ifjoin_to_noinfo(ch, true);
624 /* ch may have been deleted */
625
626 return 0;
627 }
628
629 static int on_ifjoin_prune_pending_timer(struct thread *t)
630 {
631 struct pim_ifchannel *ch;
632 int send_prune_echo; /* boolean */
633 struct interface *ifp;
634 struct pim_interface *pim_ifp;
635
636 ch = THREAD_ARG(t);
637
638 if (PIM_DEBUG_TRACE)
639 zlog_debug(
640 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
641 __PRETTY_FUNCTION__, pim_str_sg_dump(&ch->sg),
642 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags));
643
644 if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) {
645 ifp = ch->interface;
646 pim_ifp = ifp->info;
647 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) {
648 /* Send PruneEcho(S,G) ? */
649 send_prune_echo =
650 (listcount(pim_ifp->pim_neighbor_list) > 1);
651
652 if (send_prune_echo) {
653 struct pim_rpf rpf;
654
655 rpf.source_nexthop.interface = ifp;
656 rpf.rpf_addr.u.prefix4 =
657 pim_ifp->primary_address;
658 pim_jp_agg_single_upstream_send(
659 &rpf, ch->upstream, 0);
660 }
661
662 ifjoin_to_noinfo(ch, true);
663 } else {
664 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
665 * message on RP path upon prune timer expiry.
666 */
667 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
668 if (ch->upstream) {
669 struct pim_upstream *parent =
670 ch->upstream->parent;
671
672 pim_upstream_update_join_desired(pim_ifp->pim,
673 ch->upstream);
674
675 pim_jp_agg_single_upstream_send(&parent->rpf,
676 parent, true);
677 }
678 }
679 /* from here ch may have been deleted */
680 }
681
682 return 0;
683 }
684
685 static void check_recv_upstream(int is_join, struct interface *recv_ifp,
686 struct in_addr upstream, struct prefix_sg *sg,
687 uint8_t source_flags, int holdtime)
688 {
689 struct pim_upstream *up;
690 struct pim_interface *pim_ifp = recv_ifp->info;
691
692 /* Upstream (S,G) in Joined state ? */
693 up = pim_upstream_find(pim_ifp->pim, sg);
694 if (!up)
695 return;
696 if (up->join_state != PIM_UPSTREAM_JOINED)
697 return;
698
699 /* Upstream (S,G) in Joined state */
700
701 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
702 /* RPF'(S,G) not found */
703 zlog_warn("%s %s: RPF'%s not found", __FILE__,
704 __PRETTY_FUNCTION__, up->sg_str);
705 return;
706 }
707
708 /* upstream directed to RPF'(S,G) ? */
709 if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) {
710 char up_str[INET_ADDRSTRLEN];
711 char rpf_str[PREFIX_STRLEN];
712 pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
713 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str,
714 sizeof(rpf_str));
715 zlog_warn(
716 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
717 __FILE__, __PRETTY_FUNCTION__, up->sg_str, up_str,
718 rpf_str, recv_ifp->name);
719 return;
720 }
721 /* upstream directed to RPF'(S,G) */
722
723 if (is_join) {
724 /* Join(S,G) to RPF'(S,G) */
725 pim_upstream_join_suppress(up, up->rpf.rpf_addr.u.prefix4,
726 holdtime);
727 return;
728 }
729
730 /* Prune to RPF'(S,G) */
731
732 if (source_flags & PIM_RPT_BIT_MASK) {
733 if (source_flags & PIM_WILDCARD_BIT_MASK) {
734 /* Prune(*,G) to RPF'(S,G) */
735 pim_upstream_join_timer_decrease_to_t_override(
736 "Prune(*,G)", up);
737 return;
738 }
739
740 /* Prune(S,G,rpt) to RPF'(S,G) */
741 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
742 up);
743 return;
744 }
745
746 /* Prune(S,G) to RPF'(S,G) */
747 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
748 }
749
750 static int nonlocal_upstream(int is_join, struct interface *recv_ifp,
751 struct in_addr upstream, struct prefix_sg *sg,
752 uint8_t source_flags, uint16_t holdtime)
753 {
754 struct pim_interface *recv_pim_ifp;
755 int is_local; /* boolean */
756
757 recv_pim_ifp = recv_ifp->info;
758 zassert(recv_pim_ifp);
759
760 is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
761
762 if (is_local)
763 return 0;
764
765 if (PIM_DEBUG_PIM_TRACE_DETAIL) {
766 char up_str[INET_ADDRSTRLEN];
767 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
768 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
769 __PRETTY_FUNCTION__, is_join ? "join" : "prune",
770 pim_str_sg_dump(sg), up_str, recv_ifp->name);
771 }
772
773 /*
774 * Since recv upstream addr was not directed to our primary
775 * address, check if we should react to it in any way.
776 */
777 check_recv_upstream(is_join, recv_ifp, upstream, sg, source_flags,
778 holdtime);
779
780 return 1; /* non-local */
781 }
782
783 void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr,
784 struct in_addr upstream, struct prefix_sg *sg,
785 uint8_t source_flags, uint16_t holdtime)
786 {
787 struct pim_interface *pim_ifp;
788 struct pim_ifchannel *ch;
789
790 if (nonlocal_upstream(1 /* join */, ifp, upstream, sg, source_flags,
791 holdtime)) {
792 return;
793 }
794
795 ch = pim_ifchannel_add(ifp, sg, source_flags,
796 PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
797 if (!ch)
798 return;
799
800 /*
801 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
802
803 Transitions from "I am Assert Loser" State
804
805 Receive Join(S,G) on Interface I
806
807 We receive a Join(S,G) that has the Upstream Neighbor Address
808 field set to my primary IP address on interface I. The action is
809 to transition to NoInfo state, delete this (S,G) assert state
810 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
811 to operate.
812
813 Notice: The nonlocal_upstream() test above ensures the upstream
814 address of the join message is our primary address.
815 */
816 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
817 char neigh_str[INET_ADDRSTRLEN];
818 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str,
819 sizeof(neigh_str));
820 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
821 __PRETTY_FUNCTION__, ch->sg_str, neigh_str,
822 ifp->name);
823
824 assert_action_a5(ch);
825 }
826
827 pim_ifp = ifp->info;
828 zassert(pim_ifp);
829
830 switch (ch->ifjoin_state) {
831 case PIM_IFJOIN_NOINFO:
832 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
833 PIM_IFJOIN_JOIN);
834 if (pim_macro_chisin_oiflist(ch)) {
835 pim_upstream_inherited_olist(pim_ifp->pim,
836 ch->upstream);
837 pim_forward_start(ch);
838 }
839 /*
840 * If we are going to be a LHR, we need to note it
841 */
842 if (ch->upstream->parent && (ch->upstream->parent->flags
843 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
844 && !(ch->upstream->flags
845 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)) {
846 pim_upstream_ref(ch->upstream,
847 PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
848 __PRETTY_FUNCTION__);
849 pim_upstream_keep_alive_timer_start(
850 ch->upstream, pim_ifp->pim->keep_alive_time);
851 }
852 break;
853 case PIM_IFJOIN_JOIN:
854 zassert(!ch->t_ifjoin_prune_pending_timer);
855
856 /*
857 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
858 a
859 previously received join message with holdtime=0xFFFF.
860 */
861 if (ch->t_ifjoin_expiry_timer) {
862 unsigned long remain = thread_timer_remain_second(
863 ch->t_ifjoin_expiry_timer);
864 if (remain > holdtime) {
865 /*
866 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
867 Messages
868
869 Transitions from Join State
870
871 The (S,G) downstream state machine on
872 interface I remains in
873 Join state, and the Expiry Timer (ET) is
874 restarted, set to
875 maximum of its current value and the HoldTime
876 from the
877 triggering Join/Prune message.
878
879 Conclusion: Do not change the ET if the
880 current value is
881 higher than the received join holdtime.
882 */
883 return;
884 }
885 }
886 THREAD_OFF(ch->t_ifjoin_expiry_timer);
887 break;
888 case PIM_IFJOIN_PRUNE:
889 if (source_flags & PIM_ENCODE_RPT_BIT)
890 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
891 PIM_IFJOIN_NOINFO);
892 break;
893 case PIM_IFJOIN_PRUNE_PENDING:
894 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
895 if (source_flags & PIM_ENCODE_RPT_BIT) {
896 THREAD_OFF(ch->t_ifjoin_expiry_timer);
897 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
898 PIM_IFJOIN_NOINFO);
899 } else
900 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
901 PIM_IFJOIN_JOIN);
902 break;
903 case PIM_IFJOIN_PRUNE_TMP:
904 break;
905 case PIM_IFJOIN_PRUNE_PENDING_TMP:
906 break;
907 }
908
909 if (holdtime != 0xFFFF) {
910 thread_add_timer(master, on_ifjoin_expiry_timer, ch, holdtime,
911 &ch->t_ifjoin_expiry_timer);
912 }
913 }
914
915 void pim_ifchannel_prune(struct interface *ifp, struct in_addr upstream,
916 struct prefix_sg *sg, uint8_t source_flags,
917 uint16_t holdtime)
918 {
919 struct pim_ifchannel *ch;
920 struct pim_interface *pim_ifp;
921 int jp_override_interval_msec;
922
923 if (nonlocal_upstream(0 /* prune */, ifp, upstream, sg, source_flags,
924 holdtime)) {
925 return;
926 }
927
928 ch = pim_ifchannel_find(ifp, sg);
929 if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT)) {
930 if (PIM_DEBUG_TRACE)
931 zlog_debug(
932 "%s: Received prune with no relevant ifchannel %s(%s) state: %d",
933 __PRETTY_FUNCTION__, ifp->name,
934 pim_str_sg_dump(sg), source_flags);
935 return;
936 }
937
938 ch = pim_ifchannel_add(ifp, sg, source_flags,
939 PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
940 if (!ch)
941 return;
942
943 pim_ifp = ifp->info;
944
945 switch (ch->ifjoin_state) {
946 case PIM_IFJOIN_NOINFO:
947 if (source_flags & PIM_ENCODE_RPT_BIT) {
948 if (!(source_flags & PIM_ENCODE_WC_BIT))
949 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
950
951 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
952 if (listcount(pim_ifp->pim_neighbor_list) > 1)
953 jp_override_interval_msec =
954 pim_if_jp_override_interval_msec(ifp);
955 else
956 jp_override_interval_msec =
957 0; /* schedule to expire immediately */
958 /* If we called ifjoin_prune() directly instead, care
959 should
960 be taken not to use "ch" afterwards since it would be
961 deleted. */
962
963 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
964 THREAD_OFF(ch->t_ifjoin_expiry_timer);
965 thread_add_timer_msec(
966 master, on_ifjoin_prune_pending_timer, ch,
967 jp_override_interval_msec,
968 &ch->t_ifjoin_prune_pending_timer);
969 thread_add_timer(master, on_ifjoin_expiry_timer, ch,
970 holdtime, &ch->t_ifjoin_expiry_timer);
971 pim_upstream_update_join_desired(pim_ifp->pim,
972 ch->upstream);
973 }
974 break;
975 case PIM_IFJOIN_PRUNE_PENDING:
976 /* nothing to do */
977 break;
978 case PIM_IFJOIN_JOIN:
979 THREAD_OFF(ch->t_ifjoin_expiry_timer);
980
981 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
982 PIM_IFJOIN_PRUNE_PENDING);
983
984 if (listcount(pim_ifp->pim_neighbor_list) > 1)
985 jp_override_interval_msec =
986 pim_if_jp_override_interval_msec(ifp);
987 else
988 jp_override_interval_msec =
989 0; /* schedule to expire immediately */
990 /* If we called ifjoin_prune() directly instead, care should
991 be taken not to use "ch" afterwards since it would be
992 deleted. */
993 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
994 thread_add_timer_msec(master, on_ifjoin_prune_pending_timer, ch,
995 jp_override_interval_msec,
996 &ch->t_ifjoin_prune_pending_timer);
997 break;
998 case PIM_IFJOIN_PRUNE:
999 if (source_flags & PIM_ENCODE_RPT_BIT) {
1000 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1001 thread_add_timer(master, on_ifjoin_expiry_timer, ch,
1002 holdtime, &ch->t_ifjoin_expiry_timer);
1003 }
1004 break;
1005 case PIM_IFJOIN_PRUNE_TMP:
1006 if (source_flags & PIM_ENCODE_RPT_BIT) {
1007 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
1008 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1009 thread_add_timer(master, on_ifjoin_expiry_timer, ch,
1010 holdtime, &ch->t_ifjoin_expiry_timer);
1011 }
1012 break;
1013 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1014 if (source_flags & PIM_ENCODE_RPT_BIT) {
1015 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
1016 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1017 thread_add_timer(master, on_ifjoin_expiry_timer, ch,
1018 holdtime, &ch->t_ifjoin_expiry_timer);
1019 }
1020 break;
1021 }
1022 }
1023
1024 int pim_ifchannel_local_membership_add(struct interface *ifp,
1025 struct prefix_sg *sg)
1026 {
1027 struct pim_ifchannel *ch, *starch;
1028 struct pim_interface *pim_ifp;
1029 struct pim_instance *pim;
1030
1031 /* PIM enabled on interface? */
1032 pim_ifp = ifp->info;
1033 if (!pim_ifp)
1034 return 0;
1035 if (!PIM_IF_TEST_PIM(pim_ifp->options))
1036 return 0;
1037
1038 pim = pim_ifp->pim;
1039
1040 /* skip (*,G) ch creation if G is of type SSM */
1041 if (sg->src.s_addr == INADDR_ANY) {
1042 if (pim_is_grp_ssm(pim, sg->grp)) {
1043 if (PIM_DEBUG_PIM_EVENTS)
1044 zlog_debug(
1045 "%s: local membership (S,G)=%s ignored as group is SSM",
1046 __PRETTY_FUNCTION__,
1047 pim_str_sg_dump(sg));
1048 return 1;
1049 }
1050 }
1051
1052 ch = pim_ifchannel_add(ifp, sg, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP);
1053 if (!ch) {
1054 return 0;
1055 }
1056
1057 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
1058
1059 if (sg->src.s_addr == INADDR_ANY) {
1060 struct pim_upstream *up = pim_upstream_find(pim, sg);
1061 struct pim_upstream *child;
1062 struct listnode *up_node;
1063
1064 starch = ch;
1065
1066 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
1067 if (PIM_DEBUG_EVENTS)
1068 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1069 __FILE__, __PRETTY_FUNCTION__,
1070 child->sg_str, ifp->name,
1071 up->sg_str);
1072
1073 ch = pim_ifchannel_find(ifp, &child->sg);
1074 if (pim_upstream_evaluate_join_desired_interface(
1075 child, ch, starch)) {
1076 pim_channel_add_oif(child->channel_oil, ifp,
1077 PIM_OIF_FLAG_PROTO_STAR);
1078 pim_upstream_switch(pim, child,
1079 PIM_UPSTREAM_JOINED);
1080 }
1081 }
1082
1083 if (pim->spt.switchover == PIM_SPT_INFINITY) {
1084 if (pim->spt.plist) {
1085 struct prefix_list *plist = prefix_list_lookup(
1086 AFI_IP, pim->spt.plist);
1087 struct prefix g;
1088 g.family = AF_INET;
1089 g.prefixlen = IPV4_MAX_PREFIXLEN;
1090 g.u.prefix4 = up->sg.grp;
1091
1092 if (prefix_list_apply(plist, &g)
1093 == PREFIX_DENY) {
1094 pim_channel_add_oif(
1095 up->channel_oil, pim->regiface,
1096 PIM_OIF_FLAG_PROTO_IGMP);
1097 }
1098 }
1099 } else
1100 pim_channel_add_oif(up->channel_oil, pim->regiface,
1101 PIM_OIF_FLAG_PROTO_IGMP);
1102 }
1103
1104 return 1;
1105 }
1106
1107 void pim_ifchannel_local_membership_del(struct interface *ifp,
1108 struct prefix_sg *sg)
1109 {
1110 struct pim_ifchannel *starch, *ch, *orig;
1111 struct pim_interface *pim_ifp;
1112
1113 /* PIM enabled on interface? */
1114 pim_ifp = ifp->info;
1115 if (!pim_ifp)
1116 return;
1117 if (!PIM_IF_TEST_PIM(pim_ifp->options))
1118 return;
1119
1120 orig = ch = pim_ifchannel_find(ifp, sg);
1121 if (!ch)
1122 return;
1123 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
1124
1125 if (sg->src.s_addr == INADDR_ANY) {
1126 struct pim_upstream *up = pim_upstream_find(pim_ifp->pim, sg);
1127 struct pim_upstream *child;
1128 struct listnode *up_node, *up_nnode;
1129
1130 starch = ch;
1131
1132 for (ALL_LIST_ELEMENTS(up->sources, up_node, up_nnode, child)) {
1133 struct channel_oil *c_oil = child->channel_oil;
1134 struct pim_ifchannel *chchannel =
1135 pim_ifchannel_find(ifp, &child->sg);
1136 struct pim_interface *pim_ifp = ifp->info;
1137
1138 if (PIM_DEBUG_EVENTS)
1139 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1140 __FILE__, __PRETTY_FUNCTION__,
1141 up->sg_str, ifp->name,
1142 child->sg_str);
1143
1144 ch = pim_ifchannel_find(ifp, &child->sg);
1145 if (c_oil
1146 && !pim_upstream_evaluate_join_desired_interface(
1147 child, ch, starch))
1148 pim_channel_del_oif(c_oil, ifp,
1149 PIM_OIF_FLAG_PROTO_STAR);
1150
1151 /*
1152 * If the S,G has no if channel and the c_oil still
1153 * has output here then the *,G was supplying the
1154 * implied
1155 * if channel. So remove it.
1156 */
1157 if (!chchannel && c_oil
1158 && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
1159 pim_channel_del_oif(c_oil, ifp,
1160 PIM_OIF_FLAG_PROTO_STAR);
1161
1162 /* Child node removal/ref count-- will happen as part of
1163 * parent' delete_no_info */
1164 }
1165 }
1166 delete_on_noinfo(orig);
1167 }
1168
1169 void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
1170 {
1171 int old_couldassert =
1172 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
1173 int new_couldassert =
1174 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
1175
1176 if (new_couldassert == old_couldassert)
1177 return;
1178
1179 if (PIM_DEBUG_PIM_EVENTS) {
1180 char src_str[INET_ADDRSTRLEN];
1181 char grp_str[INET_ADDRSTRLEN];
1182 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1183 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1184 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1185 __PRETTY_FUNCTION__, src_str, grp_str,
1186 ch->interface->name, old_couldassert,
1187 new_couldassert);
1188 }
1189
1190 if (new_couldassert) {
1191 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1192 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
1193 } else {
1194 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1195 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
1196
1197 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
1198 assert_action_a4(ch);
1199 }
1200 }
1201
1202 pim_ifchannel_update_my_assert_metric(ch);
1203 }
1204
1205 /*
1206 my_assert_metric may be affected by:
1207
1208 CouldAssert(S,G)
1209 pim_ifp->primary_address
1210 rpf->source_nexthop.mrib_metric_preference;
1211 rpf->source_nexthop.mrib_route_metric;
1212 */
1213 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
1214 {
1215 struct pim_assert_metric my_metric_new =
1216 pim_macro_ch_my_assert_metric_eval(ch);
1217
1218 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
1219 return;
1220
1221 if (PIM_DEBUG_PIM_EVENTS) {
1222 char src_str[INET_ADDRSTRLEN];
1223 char grp_str[INET_ADDRSTRLEN];
1224 char old_addr_str[INET_ADDRSTRLEN];
1225 char new_addr_str[INET_ADDRSTRLEN];
1226 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1227 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1228 pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address,
1229 old_addr_str, sizeof(old_addr_str));
1230 pim_inet4_dump("<new_addr?>", my_metric_new.ip_address,
1231 new_addr_str, sizeof(new_addr_str));
1232 zlog_debug(
1233 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1234 __PRETTY_FUNCTION__, src_str, grp_str,
1235 ch->interface->name,
1236 ch->ifassert_my_metric.rpt_bit_flag,
1237 ch->ifassert_my_metric.metric_preference,
1238 ch->ifassert_my_metric.route_metric, old_addr_str,
1239 my_metric_new.rpt_bit_flag,
1240 my_metric_new.metric_preference,
1241 my_metric_new.route_metric, new_addr_str);
1242 }
1243
1244 ch->ifassert_my_metric = my_metric_new;
1245
1246 if (pim_assert_metric_better(&ch->ifassert_my_metric,
1247 &ch->ifassert_winner_metric)) {
1248 assert_action_a5(ch);
1249 }
1250 }
1251
1252 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
1253 {
1254 int old_atd = PIM_FORCE_BOOLEAN(
1255 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
1256 int new_atd =
1257 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
1258
1259 if (new_atd == old_atd)
1260 return;
1261
1262 if (PIM_DEBUG_PIM_EVENTS) {
1263 char src_str[INET_ADDRSTRLEN];
1264 char grp_str[INET_ADDRSTRLEN];
1265 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1266 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1267 zlog_debug(
1268 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1269 __PRETTY_FUNCTION__, src_str, grp_str,
1270 ch->interface->name, old_atd, new_atd);
1271 }
1272
1273 if (new_atd) {
1274 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1275 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
1276 } else {
1277 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1278 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
1279
1280 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
1281 assert_action_a5(ch);
1282 }
1283 }
1284 }
1285
1286 /*
1287 * If we have a new pim interface, check to
1288 * see if any of the pre-existing channels have
1289 * their upstream out that way and turn on forwarding
1290 * for that ifchannel then.
1291 */
1292 void pim_ifchannel_scan_forward_start(struct interface *new_ifp)
1293 {
1294 struct pim_interface *new_pim_ifp = new_ifp->info;
1295 struct pim_instance *pim = new_pim_ifp->pim;
1296 struct interface *ifp;
1297
1298 FOR_ALL_INTERFACES (pim->vrf, ifp) {
1299 struct pim_interface *loop_pim_ifp = ifp->info;
1300 struct pim_ifchannel *ch;
1301
1302 if (!loop_pim_ifp)
1303 continue;
1304
1305 if (new_pim_ifp == loop_pim_ifp)
1306 continue;
1307
1308 RB_FOREACH (ch, pim_ifchannel_rb, &loop_pim_ifp->ifchannel_rb) {
1309 if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
1310 struct pim_upstream *up = ch->upstream;
1311 if ((!up->channel_oil)
1312 && (up->rpf.source_nexthop
1313 .interface == new_ifp))
1314 pim_forward_start(ch);
1315 }
1316 }
1317 }
1318 }
1319
1320 /*
1321 * Downstream per-interface (S,G,rpt) state machine
1322 * states that we need to move (S,G,rpt) items
1323 * into different states at the start of the
1324 * reception of a *,G join as well, when
1325 * we get End of Message
1326 */
1327 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
1328 uint8_t join)
1329 {
1330 struct pim_ifchannel *child;
1331 struct listnode *ch_node, *nch_node;
1332 struct pim_instance *pim =
1333 ((struct pim_interface *)ch->interface->info)->pim;
1334
1335 if (PIM_DEBUG_PIM_TRACE)
1336 zlog_debug(
1337 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__,
1338 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
1339 ch->sg_str, eom, join);
1340 if (!ch->sources)
1341 return;
1342
1343 for (ALL_LIST_ELEMENTS(ch->sources, ch_node, nch_node, child)) {
1344 if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1345 continue;
1346
1347 switch (child->ifjoin_state) {
1348 case PIM_IFJOIN_NOINFO:
1349 case PIM_IFJOIN_JOIN:
1350 break;
1351 case PIM_IFJOIN_PRUNE:
1352 if (!eom)
1353 child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
1354 break;
1355 case PIM_IFJOIN_PRUNE_PENDING:
1356 if (!eom)
1357 child->ifjoin_state =
1358 PIM_IFJOIN_PRUNE_PENDING_TMP;
1359 break;
1360 case PIM_IFJOIN_PRUNE_TMP:
1361 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1362 if (!eom)
1363 break;
1364
1365 if (child->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING_TMP)
1366 THREAD_OFF(child->t_ifjoin_prune_pending_timer);
1367 THREAD_OFF(child->t_ifjoin_expiry_timer);
1368 struct pim_upstream *parent = child->upstream->parent;
1369
1370 PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
1371 child->ifjoin_state = PIM_IFJOIN_NOINFO;
1372
1373 if (I_am_RP(pim, child->sg.grp)) {
1374 pim_channel_add_oif(
1375 child->upstream->channel_oil,
1376 ch->interface, PIM_OIF_FLAG_PROTO_STAR);
1377 pim_upstream_switch(pim, child->upstream,
1378 PIM_UPSTREAM_JOINED);
1379 pim_jp_agg_single_upstream_send(
1380 &child->upstream->rpf, child->upstream,
1381 true);
1382 }
1383 if (parent)
1384 pim_jp_agg_single_upstream_send(&parent->rpf,
1385 parent, true);
1386
1387 delete_on_noinfo(child);
1388 break;
1389 }
1390 }
1391 }
1392
1393 unsigned int pim_ifchannel_hash_key(void *arg)
1394 {
1395 struct pim_ifchannel *ch = (struct pim_ifchannel *)arg;
1396
1397 return jhash_2words(ch->sg.src.s_addr, ch->sg.grp.s_addr, 0);
1398 }