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