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