]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_ifchannel.c
*: reindent
[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 && (ch->upstream->parent->flags
818 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
819 && !(ch->upstream->flags
820 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)) {
821 pim_upstream_ref(ch->upstream,
822 PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
823 __PRETTY_FUNCTION__);
824 pim_upstream_keep_alive_timer_start(
825 ch->upstream, qpim_keep_alive_time);
826 }
827 break;
828 case PIM_IFJOIN_JOIN:
829 zassert(!ch->t_ifjoin_prune_pending_timer);
830
831 /*
832 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
833 a
834 previously received join message with holdtime=0xFFFF.
835 */
836 if (ch->t_ifjoin_expiry_timer) {
837 unsigned long remain = thread_timer_remain_second(
838 ch->t_ifjoin_expiry_timer);
839 if (remain > holdtime) {
840 /*
841 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
842 Messages
843
844 Transitions from Join State
845
846 The (S,G) downstream state machine on
847 interface I remains in
848 Join state, and the Expiry Timer (ET) is
849 restarted, set to
850 maximum of its current value and the HoldTime
851 from the
852 triggering Join/Prune message.
853
854 Conclusion: Do not change the ET if the
855 current value is
856 higher than the received join holdtime.
857 */
858 return;
859 }
860 }
861 THREAD_OFF(ch->t_ifjoin_expiry_timer);
862 break;
863 case PIM_IFJOIN_PRUNE:
864 if (source_flags & PIM_ENCODE_RPT_BIT)
865 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
866 PIM_IFJOIN_NOINFO);
867 break;
868 case PIM_IFJOIN_PRUNE_PENDING:
869 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
870 if (source_flags & PIM_ENCODE_RPT_BIT) {
871 THREAD_OFF(ch->t_ifjoin_expiry_timer);
872 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
873 PIM_IFJOIN_NOINFO);
874 } else
875 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
876 PIM_IFJOIN_JOIN);
877 break;
878 case PIM_IFJOIN_PRUNE_TMP:
879 break;
880 case PIM_IFJOIN_PRUNE_PENDING_TMP:
881 break;
882 }
883
884 if (holdtime != 0xFFFF) {
885 thread_add_timer(master, on_ifjoin_expiry_timer, ch, holdtime,
886 &ch->t_ifjoin_expiry_timer);
887 }
888 }
889
890 void pim_ifchannel_prune(struct interface *ifp, struct in_addr upstream,
891 struct prefix_sg *sg, uint8_t source_flags,
892 uint16_t holdtime)
893 {
894 struct pim_ifchannel *ch;
895 struct pim_interface *pim_ifp;
896 int jp_override_interval_msec;
897
898 if (nonlocal_upstream(0 /* prune */, ifp, upstream, sg, source_flags,
899 holdtime)) {
900 return;
901 }
902
903 ch = pim_ifchannel_find(ifp, sg);
904 if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT)) {
905 if (PIM_DEBUG_TRACE)
906 zlog_debug(
907 "%s: Received prune with no relevant ifchannel %s(%s) state: %d",
908 __PRETTY_FUNCTION__, ifp->name,
909 pim_str_sg_dump(sg), source_flags);
910 return;
911 }
912
913 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
914 if (!ch)
915 return;
916
917 pim_ifp = ifp->info;
918
919 switch (ch->ifjoin_state) {
920 case PIM_IFJOIN_NOINFO:
921 if (source_flags & PIM_ENCODE_RPT_BIT) {
922 if (!(source_flags & PIM_ENCODE_WC_BIT))
923 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
924
925 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
926 if (listcount(pim_ifp->pim_neighbor_list) > 1)
927 jp_override_interval_msec =
928 pim_if_jp_override_interval_msec(ifp);
929 else
930 jp_override_interval_msec =
931 0; /* schedule to expire immediately */
932 /* If we called ifjoin_prune() directly instead, care
933 should
934 be taken not to use "ch" afterwards since it would be
935 deleted. */
936
937 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
938 THREAD_OFF(ch->t_ifjoin_expiry_timer);
939 thread_add_timer_msec(
940 master, on_ifjoin_prune_pending_timer, ch,
941 jp_override_interval_msec,
942 &ch->t_ifjoin_prune_pending_timer);
943 thread_add_timer(master, on_ifjoin_expiry_timer, ch,
944 holdtime, &ch->t_ifjoin_expiry_timer);
945 pim_upstream_update_join_desired(ch->upstream);
946 }
947 break;
948 case PIM_IFJOIN_PRUNE_PENDING:
949 /* nothing to do */
950 break;
951 case PIM_IFJOIN_JOIN:
952 THREAD_OFF(ch->t_ifjoin_expiry_timer);
953
954 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
955 PIM_IFJOIN_PRUNE_PENDING);
956
957 if (listcount(pim_ifp->pim_neighbor_list) > 1)
958 jp_override_interval_msec =
959 pim_if_jp_override_interval_msec(ifp);
960 else
961 jp_override_interval_msec =
962 0; /* schedule to expire immediately */
963 /* If we called ifjoin_prune() directly instead, care should
964 be taken not to use "ch" afterwards since it would be
965 deleted. */
966 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
967 thread_add_timer_msec(master, on_ifjoin_prune_pending_timer, ch,
968 jp_override_interval_msec,
969 &ch->t_ifjoin_prune_pending_timer);
970 break;
971 case PIM_IFJOIN_PRUNE:
972 if (source_flags & PIM_ENCODE_RPT_BIT) {
973 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
974 thread_add_timer(master, on_ifjoin_expiry_timer, ch,
975 holdtime, &ch->t_ifjoin_expiry_timer);
976 }
977 break;
978 case PIM_IFJOIN_PRUNE_TMP:
979 if (source_flags & PIM_ENCODE_RPT_BIT) {
980 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
981 THREAD_OFF(ch->t_ifjoin_expiry_timer);
982 thread_add_timer(master, on_ifjoin_expiry_timer, ch,
983 holdtime, &ch->t_ifjoin_expiry_timer);
984 }
985 break;
986 case PIM_IFJOIN_PRUNE_PENDING_TMP:
987 if (source_flags & PIM_ENCODE_RPT_BIT) {
988 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
989 THREAD_OFF(ch->t_ifjoin_expiry_timer);
990 thread_add_timer(master, on_ifjoin_expiry_timer, ch,
991 holdtime, &ch->t_ifjoin_expiry_timer);
992 }
993 break;
994 }
995 }
996
997 int pim_ifchannel_local_membership_add(struct interface *ifp,
998 struct prefix_sg *sg)
999 {
1000 struct pim_ifchannel *ch, *starch;
1001 struct pim_interface *pim_ifp;
1002
1003 /* PIM enabled on interface? */
1004 pim_ifp = ifp->info;
1005 if (!pim_ifp)
1006 return 0;
1007 if (!PIM_IF_TEST_PIM(pim_ifp->options))
1008 return 0;
1009
1010 /* skip (*,G) ch creation if G is of type SSM */
1011 if (sg->src.s_addr == INADDR_ANY) {
1012 if (pim_is_grp_ssm(sg->grp)) {
1013 if (PIM_DEBUG_PIM_EVENTS)
1014 zlog_debug(
1015 "%s: local membership (S,G)=%s ignored as group is SSM",
1016 __PRETTY_FUNCTION__,
1017 pim_str_sg_dump(sg));
1018 return 1;
1019 }
1020 }
1021
1022 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP);
1023 if (!ch) {
1024 return 0;
1025 }
1026
1027 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
1028
1029 if (sg->src.s_addr == INADDR_ANY) {
1030 struct pim_upstream *up = pim_upstream_find(sg);
1031 struct pim_upstream *child;
1032 struct listnode *up_node;
1033
1034 starch = ch;
1035
1036 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
1037 if (PIM_DEBUG_EVENTS)
1038 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1039 __FILE__, __PRETTY_FUNCTION__,
1040 child->sg_str, ifp->name,
1041 up->sg_str);
1042
1043 ch = pim_ifchannel_find(ifp, &child->sg);
1044 if (pim_upstream_evaluate_join_desired_interface(
1045 child, ch, starch)) {
1046 pim_channel_add_oif(child->channel_oil, ifp,
1047 PIM_OIF_FLAG_PROTO_STAR);
1048 pim_upstream_switch(child, PIM_UPSTREAM_JOINED);
1049 }
1050 }
1051
1052 if (pimg->spt.switchover == PIM_SPT_INFINITY) {
1053 if (pimg->spt.plist) {
1054 struct prefix_list *plist = prefix_list_lookup(
1055 AFI_IP, pimg->spt.plist);
1056 struct prefix g;
1057 g.family = AF_INET;
1058 g.prefixlen = IPV4_MAX_PREFIXLEN;
1059 g.u.prefix4 = up->sg.grp;
1060
1061 if (prefix_list_apply(plist, &g)
1062 == PREFIX_DENY) {
1063 pim_channel_add_oif(
1064 up->channel_oil, pim_regiface,
1065 PIM_OIF_FLAG_PROTO_IGMP);
1066 }
1067 }
1068 } else
1069 pim_channel_add_oif(up->channel_oil, pim_regiface,
1070 PIM_OIF_FLAG_PROTO_IGMP);
1071 }
1072
1073 return 1;
1074 }
1075
1076 void pim_ifchannel_local_membership_del(struct interface *ifp,
1077 struct prefix_sg *sg)
1078 {
1079 struct pim_ifchannel *starch, *ch, *orig;
1080 struct pim_interface *pim_ifp;
1081
1082 /* PIM enabled on interface? */
1083 pim_ifp = ifp->info;
1084 if (!pim_ifp)
1085 return;
1086 if (!PIM_IF_TEST_PIM(pim_ifp->options))
1087 return;
1088
1089 orig = ch = pim_ifchannel_find(ifp, sg);
1090 if (!ch)
1091 return;
1092
1093 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
1094
1095 if (sg->src.s_addr == INADDR_ANY) {
1096 struct pim_upstream *up = pim_upstream_find(sg);
1097 struct pim_upstream *child;
1098 struct listnode *up_node, *up_nnode;
1099
1100 starch = ch;
1101
1102 for (ALL_LIST_ELEMENTS(up->sources, up_node, up_nnode, child)) {
1103 struct channel_oil *c_oil = child->channel_oil;
1104 struct pim_ifchannel *chchannel =
1105 pim_ifchannel_find(ifp, &child->sg);
1106 struct pim_interface *pim_ifp = ifp->info;
1107
1108 if (PIM_DEBUG_EVENTS)
1109 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1110 __FILE__, __PRETTY_FUNCTION__,
1111 up->sg_str, ifp->name,
1112 child->sg_str);
1113
1114 ch = pim_ifchannel_find(ifp, &child->sg);
1115 if (c_oil
1116 && !pim_upstream_evaluate_join_desired_interface(
1117 child, ch, starch))
1118 pim_channel_del_oif(c_oil, ifp,
1119 PIM_OIF_FLAG_PROTO_STAR);
1120
1121 /*
1122 * If the S,G has no if channel and the c_oil still
1123 * has output here then the *,G was supplying the
1124 * implied
1125 * if channel. So remove it.
1126 */
1127 if (!chchannel && c_oil
1128 && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
1129 pim_channel_del_oif(c_oil, ifp,
1130 PIM_OIF_FLAG_PROTO_STAR);
1131
1132 /* Child node removal/ref count-- will happen as part of
1133 * parent' delete_no_info */
1134 }
1135 }
1136 delete_on_noinfo(orig);
1137 }
1138
1139 void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
1140 {
1141 int old_couldassert =
1142 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
1143 int new_couldassert =
1144 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
1145
1146 if (new_couldassert == old_couldassert)
1147 return;
1148
1149 if (PIM_DEBUG_PIM_EVENTS) {
1150 char src_str[INET_ADDRSTRLEN];
1151 char grp_str[INET_ADDRSTRLEN];
1152 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1153 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1154 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1155 __PRETTY_FUNCTION__, src_str, grp_str,
1156 ch->interface->name, old_couldassert,
1157 new_couldassert);
1158 }
1159
1160 if (new_couldassert) {
1161 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1162 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
1163 } else {
1164 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1165 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
1166
1167 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
1168 assert_action_a4(ch);
1169 }
1170 }
1171
1172 pim_ifchannel_update_my_assert_metric(ch);
1173 }
1174
1175 /*
1176 my_assert_metric may be affected by:
1177
1178 CouldAssert(S,G)
1179 pim_ifp->primary_address
1180 rpf->source_nexthop.mrib_metric_preference;
1181 rpf->source_nexthop.mrib_route_metric;
1182 */
1183 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
1184 {
1185 struct pim_assert_metric my_metric_new =
1186 pim_macro_ch_my_assert_metric_eval(ch);
1187
1188 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
1189 return;
1190
1191 if (PIM_DEBUG_PIM_EVENTS) {
1192 char src_str[INET_ADDRSTRLEN];
1193 char grp_str[INET_ADDRSTRLEN];
1194 char old_addr_str[INET_ADDRSTRLEN];
1195 char new_addr_str[INET_ADDRSTRLEN];
1196 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1197 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1198 pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address,
1199 old_addr_str, sizeof(old_addr_str));
1200 pim_inet4_dump("<new_addr?>", my_metric_new.ip_address,
1201 new_addr_str, sizeof(new_addr_str));
1202 zlog_debug(
1203 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1204 __PRETTY_FUNCTION__, src_str, grp_str,
1205 ch->interface->name,
1206 ch->ifassert_my_metric.rpt_bit_flag,
1207 ch->ifassert_my_metric.metric_preference,
1208 ch->ifassert_my_metric.route_metric, old_addr_str,
1209 my_metric_new.rpt_bit_flag,
1210 my_metric_new.metric_preference,
1211 my_metric_new.route_metric, new_addr_str);
1212 }
1213
1214 ch->ifassert_my_metric = my_metric_new;
1215
1216 if (pim_assert_metric_better(&ch->ifassert_my_metric,
1217 &ch->ifassert_winner_metric)) {
1218 assert_action_a5(ch);
1219 }
1220 }
1221
1222 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
1223 {
1224 int old_atd = PIM_FORCE_BOOLEAN(
1225 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
1226 int new_atd =
1227 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
1228
1229 if (new_atd == old_atd)
1230 return;
1231
1232 if (PIM_DEBUG_PIM_EVENTS) {
1233 char src_str[INET_ADDRSTRLEN];
1234 char grp_str[INET_ADDRSTRLEN];
1235 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1236 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1237 zlog_debug(
1238 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1239 __PRETTY_FUNCTION__, src_str, grp_str,
1240 ch->interface->name, old_atd, new_atd);
1241 }
1242
1243 if (new_atd) {
1244 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1245 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
1246 } else {
1247 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1248 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
1249
1250 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
1251 assert_action_a5(ch);
1252 }
1253 }
1254 }
1255
1256 /*
1257 * If we have a new pim interface, check to
1258 * see if any of the pre-existing channels have
1259 * their upstream out that way and turn on forwarding
1260 * for that ifchannel then.
1261 */
1262 void pim_ifchannel_scan_forward_start(struct interface *new_ifp)
1263 {
1264 struct listnode *ifnode;
1265 struct interface *ifp;
1266 struct pim_interface *new_pim_ifp = new_ifp->info;
1267
1268 for (ALL_LIST_ELEMENTS_RO(vrf_iflist(VRF_DEFAULT), ifnode, ifp)) {
1269 struct pim_interface *loop_pim_ifp = ifp->info;
1270 struct listnode *ch_node;
1271 struct pim_ifchannel *ch;
1272
1273 if (!loop_pim_ifp)
1274 continue;
1275
1276 if (new_pim_ifp == loop_pim_ifp)
1277 continue;
1278
1279 for (ALL_LIST_ELEMENTS_RO(loop_pim_ifp->pim_ifchannel_list,
1280 ch_node, ch)) {
1281 if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
1282 struct pim_upstream *up = ch->upstream;
1283 if ((!up->channel_oil)
1284 && (up->rpf.source_nexthop
1285 .interface == new_ifp))
1286 pim_forward_start(ch);
1287 }
1288 }
1289 }
1290 }
1291
1292 /*
1293 * Downstream per-interface (S,G,rpt) state machine
1294 * states that we need to move (S,G,rpt) items
1295 * into different states at the start of the
1296 * reception of a *,G join as well, when
1297 * we get End of Message
1298 */
1299 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
1300 uint8_t source_flags, uint8_t join,
1301 uint8_t starg_alone)
1302 {
1303 struct pim_ifchannel *child;
1304 struct listnode *ch_node;
1305
1306 if (PIM_DEBUG_PIM_TRACE)
1307 zlog_debug(
1308 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__,
1309 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
1310 ch->sg_str, eom, join);
1311 if (!ch->sources)
1312 return;
1313
1314 for (ALL_LIST_ELEMENTS_RO(ch->sources, ch_node, child)) {
1315 /* Only *,G Join received and no (SG-RPT) prune.
1316 eom = 1, only (W,G) join_alone is true, WC and RPT are set.
1317 Scan all S,G associated to G and if any SG-RPT
1318 remove the SG-RPT flag.
1319 */
1320 if (eom && starg_alone && (source_flags & PIM_RPT_BIT_MASK)
1321 && (source_flags & PIM_WILDCARD_BIT_MASK)) {
1322 if (PIM_IF_FLAG_TEST_S_G_RPT(child->flags)) {
1323 struct pim_upstream *up = child->upstream;
1324
1325 PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
1326 if (up) {
1327 if (PIM_DEBUG_TRACE)
1328 zlog_debug(
1329 "%s: SGRpt flag is cleared, add inherit oif to up %s",
1330 __PRETTY_FUNCTION__,
1331 up->sg_str);
1332 pim_channel_add_oif(
1333 up->channel_oil, ch->interface,
1334 PIM_OIF_FLAG_PROTO_STAR);
1335 pim_ifchannel_ifjoin_switch(
1336 __PRETTY_FUNCTION__, child,
1337 PIM_IFJOIN_JOIN);
1338 }
1339 }
1340 }
1341
1342 if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1343 continue;
1344
1345 switch (child->ifjoin_state) {
1346 case PIM_IFJOIN_NOINFO:
1347 case PIM_IFJOIN_JOIN:
1348 break;
1349 case PIM_IFJOIN_PRUNE:
1350 if (!eom)
1351 child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
1352 break;
1353 case PIM_IFJOIN_PRUNE_PENDING:
1354 if (!eom)
1355 child->ifjoin_state =
1356 PIM_IFJOIN_PRUNE_PENDING_TMP;
1357 break;
1358 case PIM_IFJOIN_PRUNE_TMP:
1359 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1360 if (eom)
1361 child->ifjoin_state = PIM_IFJOIN_NOINFO;
1362 break;
1363 }
1364 }
1365 }
1366
1367 unsigned int pim_ifchannel_hash_key(void *arg)
1368 {
1369 struct pim_ifchannel *ch = (struct pim_ifchannel *)arg;
1370
1371 return jhash_2words(ch->sg.src.s_addr, ch->sg.grp.s_addr, 0);
1372 }
1373
1374 int pim_ifchannel_equal(const void *arg1, const void *arg2)
1375 {
1376 const struct pim_ifchannel *ch1 = (const struct pim_ifchannel *)arg1;
1377 const struct pim_ifchannel *ch2 = (const struct pim_ifchannel *)arg2;
1378
1379 if ((ch1->sg.grp.s_addr == ch2->sg.grp.s_addr)
1380 && (ch1->sg.src.s_addr == ch2->sg.src.s_addr))
1381 return 1;
1382
1383 return 0;
1384 }