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