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