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