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