]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_ifchannel.c
pimd: update add_oif and del_oif debugs to print caller
[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
256 if (PIM_DEBUG_PIM_EVENTS)
257 zlog_debug(
258 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
259 ch->interface->name, ch->sg_str,
260 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
261 pim_ifchannel_ifjoin_name(new_state, 0));
262
263
264 if (old_state == new_state) {
265 if (PIM_DEBUG_PIM_EVENTS) {
266 zlog_debug(
267 "%s calledby %s: non-transition on state %d (%s)",
268 __PRETTY_FUNCTION__, caller, new_state,
269 pim_ifchannel_ifjoin_name(new_state, 0));
270 }
271 return;
272 }
273
274 ch->ifjoin_state = new_state;
275
276 if (ch->sg.src.s_addr == INADDR_ANY) {
277 struct pim_upstream *up = ch->upstream;
278 struct pim_upstream *child;
279 struct listnode *up_node;
280
281 if (up) {
282 if (ch->ifjoin_state == PIM_IFJOIN_NOINFO) {
283 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
284 child)) {
285 struct channel_oil *c_oil =
286 child->channel_oil;
287
288 if (PIM_DEBUG_PIM_TRACE)
289 zlog_debug(
290 "%s %s: Prune(S,G)=%s from %s",
291 __FILE__,
292 __PRETTY_FUNCTION__,
293 child->sg_str,
294 up->sg_str);
295 if (!c_oil)
296 continue;
297
298 if (!pim_upstream_evaluate_join_desired(
299 pim_ifp->pim, child)) {
300 pim_channel_del_oif(
301 c_oil, ch->interface,
302 PIM_OIF_FLAG_PROTO_STAR,
303 __func__);
304 pim_upstream_update_join_desired(
305 pim_ifp->pim, child);
306 }
307
308 /*
309 * If the S,G has no if channel and the
310 * c_oil still
311 * has output here then the *,G was
312 * supplying the implied
313 * if channel. So remove it.
314 * I think this is dead code now. is it?
315 */
316 if (c_oil->oil.mfcc_ttls
317 [pim_ifp->mroute_vif_index])
318 pim_channel_del_oif(
319 c_oil, ch->interface,
320 PIM_OIF_FLAG_PROTO_STAR,
321 __func__);
322 }
323 }
324 if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
325 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
326 child)) {
327 if (PIM_DEBUG_PIM_TRACE)
328 zlog_debug(
329 "%s %s: Join(S,G)=%s from %s",
330 __FILE__,
331 __PRETTY_FUNCTION__,
332 child->sg_str,
333 up->sg_str);
334
335 if (pim_upstream_evaluate_join_desired(
336 pim_ifp->pim, child)) {
337 pim_channel_add_oif(
338 child->channel_oil,
339 ch->interface,
340 PIM_OIF_FLAG_PROTO_STAR,
341 __func__);
342 pim_upstream_update_join_desired(
343 pim_ifp->pim, child);
344 }
345 }
346 }
347 }
348 }
349 /* Transition to/from NOINFO ? */
350 if ((old_state == PIM_IFJOIN_NOINFO)
351 || (new_state == PIM_IFJOIN_NOINFO)) {
352
353 if (PIM_DEBUG_PIM_EVENTS) {
354 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
355 ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN"
356 : "UP"),
357 ch->sg_str, ch->interface->name);
358 }
359
360 /*
361 Record uptime of state transition to/from NOINFO
362 */
363 ch->ifjoin_creation = pim_time_monotonic_sec();
364
365 pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
366 pim_ifchannel_update_could_assert(ch);
367 pim_ifchannel_update_assert_tracking_desired(ch);
368 }
369 }
370
371 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state,
372 int flags)
373 {
374 switch (ifjoin_state) {
375 case PIM_IFJOIN_NOINFO:
376 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
377 return "SGRpt(NI)";
378 else
379 return "NOINFO";
380 break;
381 case PIM_IFJOIN_JOIN:
382 return "JOIN";
383 break;
384 case PIM_IFJOIN_PRUNE:
385 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
386 return "SGRpt(P)";
387 else
388 return "PRUNE";
389 break;
390 case PIM_IFJOIN_PRUNE_PENDING:
391 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
392 return "SGRpt(PP)";
393 else
394 return "PRUNEP";
395 break;
396 case PIM_IFJOIN_PRUNE_TMP:
397 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
398 return "SGRpt(P')";
399 else
400 return "PRUNET";
401 break;
402 case PIM_IFJOIN_PRUNE_PENDING_TMP:
403 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
404 return "SGRpt(PP')";
405 else
406 return "PRUNEPT";
407 break;
408 }
409
410 return "ifjoin_bad_state";
411 }
412
413 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
414 {
415 switch (ifassert_state) {
416 case PIM_IFASSERT_NOINFO:
417 return "NOINFO";
418 case PIM_IFASSERT_I_AM_WINNER:
419 return "WINNER";
420 case PIM_IFASSERT_I_AM_LOSER:
421 return "LOSER";
422 }
423
424 return "ifassert_bad_state";
425 }
426
427 /*
428 RFC 4601: 4.6.5. Assert State Macros
429
430 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
431 defaults to Infinity when in the NoInfo state.
432 */
433 void reset_ifassert_state(struct pim_ifchannel *ch)
434 {
435 struct in_addr any = {.s_addr = INADDR_ANY};
436
437 THREAD_OFF(ch->t_ifassert_timer);
438
439 pim_ifassert_winner_set(ch, PIM_IFASSERT_NOINFO, any,
440 router->infinite_assert_metric);
441 }
442
443 struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
444 struct prefix_sg *sg)
445 {
446 struct pim_interface *pim_ifp;
447 struct pim_ifchannel *ch;
448 struct pim_ifchannel lookup;
449
450 pim_ifp = ifp->info;
451
452 if (!pim_ifp) {
453 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
454 __PRETTY_FUNCTION__, pim_str_sg_dump(sg), ifp->name);
455 return NULL;
456 }
457
458 lookup.sg = *sg;
459 lookup.interface = ifp;
460 ch = RB_FIND(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, &lookup);
461
462 return ch;
463 }
464
465 static void ifmembership_set(struct pim_ifchannel *ch,
466 enum pim_ifmembership membership)
467 {
468 struct pim_interface *pim_ifp = ch->interface->info;
469
470 if (ch->local_ifmembership == membership)
471 return;
472
473 if (PIM_DEBUG_PIM_EVENTS) {
474 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
475 __PRETTY_FUNCTION__, ch->sg_str,
476 membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE"
477 : "NOINFO",
478 ch->interface->name);
479 }
480
481 ch->local_ifmembership = membership;
482
483 pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
484 pim_ifchannel_update_could_assert(ch);
485 pim_ifchannel_update_assert_tracking_desired(ch);
486 }
487
488
489 void pim_ifchannel_membership_clear(struct interface *ifp)
490 {
491 struct pim_interface *pim_ifp;
492 struct pim_ifchannel *ch;
493
494 pim_ifp = ifp->info;
495 zassert(pim_ifp);
496
497 RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb)
498 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
499 }
500
501 void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
502 {
503 struct pim_interface *pim_ifp;
504 struct pim_ifchannel *ch, *ch_tmp;
505
506 pim_ifp = ifp->info;
507 zassert(pim_ifp);
508
509 RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch_tmp)
510 delete_on_noinfo(ch);
511 }
512
513 /*
514 * For a given Interface, if we are given a S,G
515 * Find the *,G (If we have it).
516 * If we are passed a *,G, find the *,* ifchannel
517 * if we have it.
518 */
519 static struct pim_ifchannel *pim_ifchannel_find_parent(struct pim_ifchannel *ch)
520 {
521 struct prefix_sg parent_sg = ch->sg;
522 struct pim_ifchannel *parent = NULL;
523
524 // (S,G)
525 if ((parent_sg.src.s_addr != INADDR_ANY)
526 && (parent_sg.grp.s_addr != INADDR_ANY)) {
527 parent_sg.src.s_addr = INADDR_ANY;
528 parent = pim_ifchannel_find(ch->interface, &parent_sg);
529
530 if (parent)
531 listnode_add(parent->sources, ch);
532 return parent;
533 }
534
535 return NULL;
536 }
537
538 struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
539 struct prefix_sg *sg,
540 uint8_t source_flags, int up_flags)
541 {
542 struct pim_interface *pim_ifp;
543 struct pim_ifchannel *ch;
544 struct pim_upstream *up;
545
546 ch = pim_ifchannel_find(ifp, sg);
547 if (ch)
548 return ch;
549
550 pim_ifp = ifp->info;
551
552 ch = XCALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
553
554 ch->flags = 0;
555 if ((source_flags & PIM_ENCODE_RPT_BIT)
556 && !(source_flags & PIM_ENCODE_WC_BIT))
557 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
558
559 ch->interface = ifp;
560 ch->sg = *sg;
561 pim_str_sg_set(sg, ch->sg_str);
562 ch->parent = pim_ifchannel_find_parent(ch);
563 if (ch->sg.src.s_addr == INADDR_ANY) {
564 ch->sources = list_new();
565 ch->sources->cmp =
566 (int (*)(void *, void *))pim_ifchannel_compare;
567 } else
568 ch->sources = NULL;
569
570 pim_ifchannel_find_new_children(ch);
571 ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
572
573 ch->ifjoin_state = PIM_IFJOIN_NOINFO;
574 ch->t_ifjoin_expiry_timer = NULL;
575 ch->t_ifjoin_prune_pending_timer = NULL;
576 ch->ifjoin_creation = 0;
577
578 RB_INSERT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
579
580 up = pim_upstream_add(pim_ifp->pim, sg, NULL, up_flags,
581 __PRETTY_FUNCTION__, ch);
582
583 ch->upstream = up;
584
585 listnode_add_sort(up->ifchannels, ch);
586
587 ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
588 ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval(ch);
589
590 ch->ifassert_winner.s_addr = 0;
591
592 /* Assert state */
593 ch->t_ifassert_timer = NULL;
594 ch->ifassert_state = PIM_IFASSERT_NOINFO;
595 reset_ifassert_state(ch);
596 if (pim_macro_ch_could_assert_eval(ch))
597 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
598 else
599 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
600
601 if (pim_macro_assert_tracking_desired_eval(ch))
602 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
603 else
604 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
605
606 if (PIM_DEBUG_PIM_TRACE)
607 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__,
608 ch->sg_str);
609
610 return ch;
611 }
612
613 static void ifjoin_to_noinfo(struct pim_ifchannel *ch, bool ch_del)
614 {
615 pim_forward_stop(ch, !ch_del);
616 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
617 if (ch_del)
618 delete_on_noinfo(ch);
619 }
620
621 static int on_ifjoin_expiry_timer(struct thread *t)
622 {
623 struct pim_ifchannel *ch;
624
625 ch = THREAD_ARG(t);
626
627 if (PIM_DEBUG_PIM_TRACE)
628 zlog_debug("%s: ifchannel %s expiry timer", __PRETTY_FUNCTION__,
629 ch->sg_str);
630
631 ifjoin_to_noinfo(ch, true);
632 /* ch may have been deleted */
633
634 return 0;
635 }
636
637 static int on_ifjoin_prune_pending_timer(struct thread *t)
638 {
639 struct pim_ifchannel *ch;
640 int send_prune_echo; /* boolean */
641 struct interface *ifp;
642 struct pim_interface *pim_ifp;
643
644 ch = THREAD_ARG(t);
645
646 if (PIM_DEBUG_PIM_TRACE)
647 zlog_debug(
648 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
649 __PRETTY_FUNCTION__, pim_str_sg_dump(&ch->sg),
650 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags));
651
652 if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) {
653 ifp = ch->interface;
654 pim_ifp = ifp->info;
655 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) {
656 /* Send PruneEcho(S,G) ? */
657 send_prune_echo =
658 (listcount(pim_ifp->pim_neighbor_list) > 1);
659
660 if (send_prune_echo) {
661 struct pim_rpf rpf;
662
663 rpf.source_nexthop.interface = ifp;
664 rpf.rpf_addr.u.prefix4 =
665 pim_ifp->primary_address;
666 pim_jp_agg_single_upstream_send(
667 &rpf, ch->upstream, 0);
668 }
669
670 ifjoin_to_noinfo(ch, true);
671 } else {
672 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
673 * message on RP path upon prune timer expiry.
674 */
675 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
676 if (ch->upstream) {
677 struct pim_upstream *parent =
678 ch->upstream->parent;
679
680 pim_upstream_update_join_desired(pim_ifp->pim,
681 ch->upstream);
682
683 pim_jp_agg_single_upstream_send(&parent->rpf,
684 parent, true);
685 }
686 }
687 /* from here ch may have been deleted */
688 }
689
690 return 0;
691 }
692
693 static void check_recv_upstream(int is_join, struct interface *recv_ifp,
694 struct in_addr upstream, struct prefix_sg *sg,
695 uint8_t source_flags, int holdtime)
696 {
697 struct pim_upstream *up;
698 struct pim_interface *pim_ifp = recv_ifp->info;
699
700 /* Upstream (S,G) in Joined state ? */
701 up = pim_upstream_find(pim_ifp->pim, sg);
702 if (!up)
703 return;
704 if (up->join_state != PIM_UPSTREAM_JOINED)
705 return;
706
707 /* Upstream (S,G) in Joined state */
708
709 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
710 /* RPF'(S,G) not found */
711 zlog_warn("%s %s: RPF'%s not found", __FILE__,
712 __PRETTY_FUNCTION__, up->sg_str);
713 return;
714 }
715
716 /* upstream directed to RPF'(S,G) ? */
717 if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) {
718 char up_str[INET_ADDRSTRLEN];
719 char rpf_str[PREFIX_STRLEN];
720 pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
721 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str,
722 sizeof(rpf_str));
723 zlog_warn(
724 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
725 __FILE__, __PRETTY_FUNCTION__, up->sg_str, up_str,
726 rpf_str, recv_ifp->name);
727 return;
728 }
729 /* upstream directed to RPF'(S,G) */
730
731 if (is_join) {
732 /* Join(S,G) to RPF'(S,G) */
733 pim_upstream_join_suppress(up, up->rpf.rpf_addr.u.prefix4,
734 holdtime);
735 return;
736 }
737
738 /* Prune to RPF'(S,G) */
739
740 if (source_flags & PIM_RPT_BIT_MASK) {
741 if (source_flags & PIM_WILDCARD_BIT_MASK) {
742 /* Prune(*,G) to RPF'(S,G) */
743 pim_upstream_join_timer_decrease_to_t_override(
744 "Prune(*,G)", up);
745 return;
746 }
747
748 /* Prune(S,G,rpt) to RPF'(S,G) */
749 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
750 up);
751 return;
752 }
753
754 /* Prune(S,G) to RPF'(S,G) */
755 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
756 }
757
758 static int nonlocal_upstream(int is_join, struct interface *recv_ifp,
759 struct in_addr upstream, struct prefix_sg *sg,
760 uint8_t source_flags, uint16_t holdtime)
761 {
762 struct pim_interface *recv_pim_ifp;
763 int is_local; /* boolean */
764
765 recv_pim_ifp = recv_ifp->info;
766 zassert(recv_pim_ifp);
767
768 is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
769
770 if (is_local)
771 return 0;
772
773 if (PIM_DEBUG_PIM_TRACE_DETAIL) {
774 char up_str[INET_ADDRSTRLEN];
775 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
776 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
777 __PRETTY_FUNCTION__, is_join ? "join" : "prune",
778 pim_str_sg_dump(sg), up_str, recv_ifp->name);
779 }
780
781 /*
782 * Since recv upstream addr was not directed to our primary
783 * address, check if we should react to it in any way.
784 */
785 check_recv_upstream(is_join, recv_ifp, upstream, sg, source_flags,
786 holdtime);
787
788 return 1; /* non-local */
789 }
790
791 void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr,
792 struct in_addr upstream, struct prefix_sg *sg,
793 uint8_t source_flags, uint16_t holdtime)
794 {
795 struct pim_interface *pim_ifp;
796 struct pim_ifchannel *ch;
797
798 if (nonlocal_upstream(1 /* join */, ifp, upstream, sg, source_flags,
799 holdtime)) {
800 return;
801 }
802
803 ch = pim_ifchannel_add(ifp, sg, source_flags,
804 PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
805
806 /*
807 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
808
809 Transitions from "I am Assert Loser" State
810
811 Receive Join(S,G) on Interface I
812
813 We receive a Join(S,G) that has the Upstream Neighbor Address
814 field set to my primary IP address on interface I. The action is
815 to transition to NoInfo state, delete this (S,G) assert state
816 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
817 to operate.
818
819 Notice: The nonlocal_upstream() test above ensures the upstream
820 address of the join message is our primary address.
821 */
822 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
823 char neigh_str[INET_ADDRSTRLEN];
824 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str,
825 sizeof(neigh_str));
826 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
827 __PRETTY_FUNCTION__, ch->sg_str, neigh_str,
828 ifp->name);
829
830 assert_action_a5(ch);
831 }
832
833 pim_ifp = ifp->info;
834 zassert(pim_ifp);
835
836 switch (ch->ifjoin_state) {
837 case PIM_IFJOIN_NOINFO:
838 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
839 PIM_IFJOIN_JOIN);
840 if (pim_macro_chisin_oiflist(ch)) {
841 pim_upstream_inherited_olist(pim_ifp->pim,
842 ch->upstream);
843 pim_forward_start(ch);
844 }
845 /*
846 * If we are going to be a LHR, we need to note it
847 */
848 if (ch->upstream->parent && (ch->upstream->parent->flags
849 & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
850 && !(ch->upstream->flags
851 & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)) {
852 pim_upstream_ref(ch->upstream,
853 PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
854 __PRETTY_FUNCTION__);
855 pim_upstream_keep_alive_timer_start(
856 ch->upstream, pim_ifp->pim->keep_alive_time);
857 }
858 break;
859 case PIM_IFJOIN_JOIN:
860 zassert(!ch->t_ifjoin_prune_pending_timer);
861
862 /*
863 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
864 a
865 previously received join message with holdtime=0xFFFF.
866 */
867 if (ch->t_ifjoin_expiry_timer) {
868 unsigned long remain = thread_timer_remain_second(
869 ch->t_ifjoin_expiry_timer);
870 if (remain > holdtime) {
871 /*
872 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
873 Messages
874
875 Transitions from Join State
876
877 The (S,G) downstream state machine on
878 interface I remains in
879 Join state, and the Expiry Timer (ET) is
880 restarted, set to
881 maximum of its current value and the HoldTime
882 from the
883 triggering Join/Prune message.
884
885 Conclusion: Do not change the ET if the
886 current value is
887 higher than the received join holdtime.
888 */
889 return;
890 }
891 }
892 THREAD_OFF(ch->t_ifjoin_expiry_timer);
893 break;
894 case PIM_IFJOIN_PRUNE:
895 if (source_flags & PIM_ENCODE_RPT_BIT)
896 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
897 PIM_IFJOIN_NOINFO);
898 else {
899 /*
900 * We have received a S,G join and we are in
901 * S,G RPT Prune state. Which means we need
902 * to transition to Join state and setup
903 * state as appropriate.
904 */
905 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
906 PIM_IFJOIN_JOIN);
907 PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags);
908 if (pim_upstream_evaluate_join_desired(pim_ifp->pim,
909 ch->upstream)) {
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_switch(pim, child,
1117 PIM_UPSTREAM_JOINED);
1118 }
1119 }
1120
1121 if (pim->spt.switchover == PIM_SPT_INFINITY) {
1122 if (pim->spt.plist) {
1123 struct prefix_list *plist = prefix_list_lookup(
1124 AFI_IP, pim->spt.plist);
1125 struct prefix g;
1126 g.family = AF_INET;
1127 g.prefixlen = IPV4_MAX_PREFIXLEN;
1128 g.u.prefix4 = up->sg.grp;
1129
1130 if (prefix_list_apply(plist, &g)
1131 == PREFIX_DENY) {
1132 pim_channel_add_oif(
1133 up->channel_oil, pim->regiface,
1134 PIM_OIF_FLAG_PROTO_IGMP,
1135 __func__);
1136 }
1137 }
1138 } else
1139 pim_channel_add_oif(up->channel_oil, pim->regiface,
1140 PIM_OIF_FLAG_PROTO_IGMP,
1141 __func__);
1142 }
1143
1144 return 1;
1145 }
1146
1147 void pim_ifchannel_local_membership_del(struct interface *ifp,
1148 struct prefix_sg *sg)
1149 {
1150 struct pim_ifchannel *starch, *ch, *orig;
1151 struct pim_interface *pim_ifp;
1152
1153 /* PIM enabled on interface? */
1154 pim_ifp = ifp->info;
1155 if (!pim_ifp)
1156 return;
1157 if (!PIM_IF_TEST_PIM(pim_ifp->options))
1158 return;
1159
1160 orig = ch = pim_ifchannel_find(ifp, sg);
1161 if (!ch)
1162 return;
1163 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
1164
1165 if (sg->src.s_addr == INADDR_ANY) {
1166 struct pim_upstream *up = pim_upstream_find(pim_ifp->pim, sg);
1167 struct pim_upstream *child;
1168 struct listnode *up_node, *up_nnode;
1169
1170 starch = ch;
1171
1172 for (ALL_LIST_ELEMENTS(up->sources, up_node, up_nnode, child)) {
1173 struct channel_oil *c_oil = child->channel_oil;
1174 struct pim_ifchannel *chchannel =
1175 pim_ifchannel_find(ifp, &child->sg);
1176
1177 pim_ifp = ifp->info;
1178
1179 if (PIM_DEBUG_EVENTS)
1180 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1181 __FILE__, __PRETTY_FUNCTION__,
1182 up->sg_str, ifp->name,
1183 child->sg_str);
1184
1185 ch = pim_ifchannel_find(ifp, &child->sg);
1186 if (c_oil
1187 && !pim_upstream_evaluate_join_desired_interface(
1188 child, ch, starch))
1189 pim_channel_del_oif(c_oil, ifp,
1190 PIM_OIF_FLAG_PROTO_STAR,
1191 __func__);
1192
1193 /*
1194 * If the S,G has no if channel and the c_oil still
1195 * has output here then the *,G was supplying the
1196 * implied
1197 * if channel. So remove it.
1198 */
1199 if (!chchannel && c_oil
1200 && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
1201 pim_channel_del_oif(c_oil, ifp,
1202 PIM_OIF_FLAG_PROTO_STAR,
1203 __func__);
1204
1205 /* Child node removal/ref count-- will happen as part of
1206 * parent' delete_no_info */
1207 }
1208 }
1209 delete_on_noinfo(orig);
1210 }
1211
1212 void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
1213 {
1214 int old_couldassert =
1215 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
1216 int new_couldassert =
1217 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
1218
1219 if (new_couldassert == old_couldassert)
1220 return;
1221
1222 if (PIM_DEBUG_PIM_EVENTS) {
1223 char src_str[INET_ADDRSTRLEN];
1224 char grp_str[INET_ADDRSTRLEN];
1225 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1226 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1227 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1228 __PRETTY_FUNCTION__, src_str, grp_str,
1229 ch->interface->name, old_couldassert,
1230 new_couldassert);
1231 }
1232
1233 if (new_couldassert) {
1234 /* CouldAssert(S,G,I) switched from false to true */
1235 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
1236 } else {
1237 /* CouldAssert(S,G,I) switched from true to false */
1238 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
1239
1240 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
1241 assert_action_a4(ch);
1242 }
1243 }
1244
1245 pim_ifchannel_update_my_assert_metric(ch);
1246 }
1247
1248 /*
1249 my_assert_metric may be affected by:
1250
1251 CouldAssert(S,G)
1252 pim_ifp->primary_address
1253 rpf->source_nexthop.mrib_metric_preference;
1254 rpf->source_nexthop.mrib_route_metric;
1255 */
1256 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
1257 {
1258 struct pim_assert_metric my_metric_new =
1259 pim_macro_ch_my_assert_metric_eval(ch);
1260
1261 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
1262 return;
1263
1264 if (PIM_DEBUG_PIM_EVENTS) {
1265 char src_str[INET_ADDRSTRLEN];
1266 char grp_str[INET_ADDRSTRLEN];
1267 char old_addr_str[INET_ADDRSTRLEN];
1268 char new_addr_str[INET_ADDRSTRLEN];
1269 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1270 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1271 pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address,
1272 old_addr_str, sizeof(old_addr_str));
1273 pim_inet4_dump("<new_addr?>", my_metric_new.ip_address,
1274 new_addr_str, sizeof(new_addr_str));
1275 zlog_debug(
1276 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1277 __PRETTY_FUNCTION__, src_str, grp_str,
1278 ch->interface->name,
1279 ch->ifassert_my_metric.rpt_bit_flag,
1280 ch->ifassert_my_metric.metric_preference,
1281 ch->ifassert_my_metric.route_metric, old_addr_str,
1282 my_metric_new.rpt_bit_flag,
1283 my_metric_new.metric_preference,
1284 my_metric_new.route_metric, new_addr_str);
1285 }
1286
1287 ch->ifassert_my_metric = my_metric_new;
1288
1289 if (pim_assert_metric_better(&ch->ifassert_my_metric,
1290 &ch->ifassert_winner_metric)) {
1291 assert_action_a5(ch);
1292 }
1293 }
1294
1295 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
1296 {
1297 int old_atd = PIM_FORCE_BOOLEAN(
1298 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
1299 int new_atd =
1300 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
1301
1302 if (new_atd == old_atd)
1303 return;
1304
1305 if (PIM_DEBUG_PIM_EVENTS) {
1306 char src_str[INET_ADDRSTRLEN];
1307 char grp_str[INET_ADDRSTRLEN];
1308 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1309 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1310 zlog_debug(
1311 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1312 __PRETTY_FUNCTION__, src_str, grp_str,
1313 ch->interface->name, old_atd, new_atd);
1314 }
1315
1316 if (new_atd) {
1317 /* AssertTrackingDesired(S,G,I) switched from false to true */
1318 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
1319 } else {
1320 /* AssertTrackingDesired(S,G,I) switched from true to false */
1321 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
1322
1323 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
1324 assert_action_a5(ch);
1325 }
1326 }
1327 }
1328
1329 /*
1330 * If we have a new pim interface, check to
1331 * see if any of the pre-existing channels have
1332 * their upstream out that way and turn on forwarding
1333 * for that ifchannel then.
1334 */
1335 void pim_ifchannel_scan_forward_start(struct interface *new_ifp)
1336 {
1337 struct pim_interface *new_pim_ifp = new_ifp->info;
1338 struct pim_instance *pim = new_pim_ifp->pim;
1339 struct interface *ifp;
1340
1341 FOR_ALL_INTERFACES (pim->vrf, ifp) {
1342 struct pim_interface *loop_pim_ifp = ifp->info;
1343 struct pim_ifchannel *ch;
1344
1345 if (!loop_pim_ifp)
1346 continue;
1347
1348 if (new_pim_ifp == loop_pim_ifp)
1349 continue;
1350
1351 RB_FOREACH (ch, pim_ifchannel_rb, &loop_pim_ifp->ifchannel_rb) {
1352 if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
1353 struct pim_upstream *up = ch->upstream;
1354 if ((!up->channel_oil)
1355 && (up->rpf.source_nexthop
1356 .interface == new_ifp))
1357 pim_forward_start(ch);
1358 }
1359 }
1360 }
1361 }
1362
1363 /*
1364 * Downstream per-interface (S,G,rpt) state machine
1365 * states that we need to move (S,G,rpt) items
1366 * into different states at the start of the
1367 * reception of a *,G join as well, when
1368 * we get End of Message
1369 */
1370 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
1371 uint8_t join)
1372 {
1373 bool send_upstream_starg = false;
1374 struct pim_ifchannel *child;
1375 struct listnode *ch_node, *nch_node;
1376 struct pim_instance *pim =
1377 ((struct pim_interface *)ch->interface->info)->pim;
1378 struct pim_upstream *starup = ch->upstream;
1379
1380 if (PIM_DEBUG_PIM_TRACE)
1381 zlog_debug(
1382 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__,
1383 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
1384 ch->sg_str, eom, join);
1385 if (!ch->sources)
1386 return;
1387
1388 for (ALL_LIST_ELEMENTS(ch->sources, ch_node, nch_node, child)) {
1389 if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1390 continue;
1391
1392 switch (child->ifjoin_state) {
1393 case PIM_IFJOIN_NOINFO:
1394 case PIM_IFJOIN_JOIN:
1395 break;
1396 case PIM_IFJOIN_PRUNE:
1397 if (!eom)
1398 child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
1399 break;
1400 case PIM_IFJOIN_PRUNE_PENDING:
1401 if (!eom)
1402 child->ifjoin_state =
1403 PIM_IFJOIN_PRUNE_PENDING_TMP;
1404 break;
1405 case PIM_IFJOIN_PRUNE_TMP:
1406 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1407 if (!eom)
1408 break;
1409
1410 if (child->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING_TMP)
1411 THREAD_OFF(child->t_ifjoin_prune_pending_timer);
1412 THREAD_OFF(child->t_ifjoin_expiry_timer);
1413
1414 PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
1415 child->ifjoin_state = PIM_IFJOIN_NOINFO;
1416
1417 if ((I_am_RP(pim, child->sg.grp)) &&
1418 (!pim_upstream_empty_inherited_olist(
1419 child->upstream))) {
1420 pim_channel_add_oif(
1421 child->upstream->channel_oil,
1422 ch->interface, PIM_OIF_FLAG_PROTO_STAR,
1423 __func__);
1424 pim_upstream_switch(pim, child->upstream,
1425 PIM_UPSTREAM_JOINED);
1426 pim_jp_agg_single_upstream_send(
1427 &child->upstream->rpf, child->upstream,
1428 true);
1429 }
1430 send_upstream_starg = true;
1431
1432 delete_on_noinfo(child);
1433 break;
1434 }
1435 }
1436
1437 if (send_upstream_starg)
1438 pim_jp_agg_single_upstream_send(&starup->rpf, starup, true);
1439 }
1440
1441 unsigned int pim_ifchannel_hash_key(const void *arg)
1442 {
1443 const struct pim_ifchannel *ch = arg;
1444
1445 return jhash_2words(ch->sg.src.s_addr, ch->sg.grp.s_addr, 0);
1446 }