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