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