]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_ifchannel.c
Merge pull request #4603 from opensourcerouting/vty-recursion
[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 zlog_warn("%s: Avoiding deletion of upstream with ref_count %d "
196 "from ifchannel(%s): %s", __PRETTY_FUNCTION__,
197 ch->upstream->ref_count, ch->interface->name,
198 ch->sg_str);
199
200 ch->upstream = NULL;
201
202 THREAD_OFF(ch->t_ifjoin_expiry_timer);
203 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
204 THREAD_OFF(ch->t_ifassert_timer);
205
206 if (ch->parent) {
207 listnode_delete(ch->parent->sources, ch);
208 ch->parent = NULL;
209 }
210
211 RB_REMOVE(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
212
213 if (PIM_DEBUG_PIM_TRACE)
214 zlog_debug("%s: ifchannel entry %s is deleted ",
215 __PRETTY_FUNCTION__, ch->sg_str);
216
217 XFREE(MTYPE_PIM_IFCHANNEL, ch);
218 }
219
220 void pim_ifchannel_delete_all(struct interface *ifp)
221 {
222 struct pim_interface *pim_ifp;
223 struct pim_ifchannel *ch;
224
225 pim_ifp = ifp->info;
226 if (!pim_ifp)
227 return;
228
229 while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
230 ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
231
232 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__,
233 ch, PIM_IFJOIN_NOINFO);
234 pim_ifchannel_delete(ch);
235 }
236 }
237
238 static void delete_on_noinfo(struct pim_ifchannel *ch)
239 {
240 if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO
241 && ch->ifjoin_state == PIM_IFJOIN_NOINFO
242 && ch->t_ifjoin_expiry_timer == NULL)
243 pim_ifchannel_delete(ch);
244 }
245
246 void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch,
247 enum pim_ifjoin_state new_state)
248 {
249 enum pim_ifjoin_state old_state = ch->ifjoin_state;
250 struct pim_interface *pim_ifp = ch->interface->info;
251
252 if (PIM_DEBUG_PIM_EVENTS)
253 zlog_debug(
254 "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
255 ch->interface->name, ch->sg_str,
256 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
257 pim_ifchannel_ifjoin_name(new_state, 0));
258
259
260 if (old_state == new_state) {
261 if (PIM_DEBUG_PIM_EVENTS) {
262 zlog_debug(
263 "%s calledby %s: non-transition on state %d (%s)",
264 __PRETTY_FUNCTION__, caller, new_state,
265 pim_ifchannel_ifjoin_name(new_state, 0));
266 }
267 return;
268 }
269
270 ch->ifjoin_state = new_state;
271
272 if (ch->sg.src.s_addr == INADDR_ANY) {
273 struct pim_upstream *up = ch->upstream;
274 struct pim_upstream *child;
275 struct listnode *up_node;
276
277 if (up) {
278 if (ch->ifjoin_state == PIM_IFJOIN_NOINFO) {
279 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
280 child)) {
281 struct channel_oil *c_oil =
282 child->channel_oil;
283
284 if (PIM_DEBUG_PIM_TRACE)
285 zlog_debug(
286 "%s %s: Prune(S,G)=%s from %s",
287 __FILE__,
288 __PRETTY_FUNCTION__,
289 child->sg_str,
290 up->sg_str);
291 if (!c_oil)
292 continue;
293
294 if (!pim_upstream_evaluate_join_desired(
295 pim_ifp->pim, child)) {
296 pim_channel_del_oif(
297 c_oil, ch->interface,
298 PIM_OIF_FLAG_PROTO_STAR);
299 pim_upstream_update_join_desired(
300 pim_ifp->pim, child);
301 }
302
303 /*
304 * If the S,G has no if channel and the
305 * c_oil still
306 * has output here then the *,G was
307 * supplying the implied
308 * if channel. So remove it.
309 * I think this is dead code now. is it?
310 */
311 if (c_oil->oil.mfcc_ttls
312 [pim_ifp->mroute_vif_index])
313 pim_channel_del_oif(
314 c_oil, ch->interface,
315 PIM_OIF_FLAG_PROTO_STAR);
316 }
317 }
318 if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
319 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
320 child)) {
321 if (PIM_DEBUG_PIM_TRACE)
322 zlog_debug(
323 "%s %s: Join(S,G)=%s from %s",
324 __FILE__,
325 __PRETTY_FUNCTION__,
326 child->sg_str,
327 up->sg_str);
328
329 if (pim_upstream_evaluate_join_desired(
330 pim_ifp->pim, child)) {
331 pim_channel_add_oif(
332 child->channel_oil,
333 ch->interface,
334 PIM_OIF_FLAG_PROTO_STAR);
335 pim_upstream_update_join_desired(
336 pim_ifp->pim, child);
337 }
338 }
339 }
340 }
341 }
342 /* Transition to/from NOINFO ? */
343 if ((old_state == PIM_IFJOIN_NOINFO)
344 || (new_state == PIM_IFJOIN_NOINFO)) {
345
346 if (PIM_DEBUG_PIM_EVENTS) {
347 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
348 ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN"
349 : "UP"),
350 ch->sg_str, ch->interface->name);
351 }
352
353 /*
354 Record uptime of state transition to/from NOINFO
355 */
356 ch->ifjoin_creation = pim_time_monotonic_sec();
357
358 pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
359 pim_ifchannel_update_could_assert(ch);
360 pim_ifchannel_update_assert_tracking_desired(ch);
361 }
362 }
363
364 const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state,
365 int flags)
366 {
367 switch (ifjoin_state) {
368 case PIM_IFJOIN_NOINFO:
369 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
370 return "SGRpt(NI)";
371 else
372 return "NOINFO";
373 break;
374 case PIM_IFJOIN_JOIN:
375 return "JOIN";
376 break;
377 case PIM_IFJOIN_PRUNE:
378 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
379 return "SGRpt(P)";
380 else
381 return "PRUNE";
382 break;
383 case PIM_IFJOIN_PRUNE_PENDING:
384 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
385 return "SGRpt(PP)";
386 else
387 return "PRUNEP";
388 break;
389 case PIM_IFJOIN_PRUNE_TMP:
390 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
391 return "SGRpt(P')";
392 else
393 return "PRUNET";
394 break;
395 case PIM_IFJOIN_PRUNE_PENDING_TMP:
396 if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
397 return "SGRpt(PP')";
398 else
399 return "PRUNEPT";
400 break;
401 }
402
403 return "ifjoin_bad_state";
404 }
405
406 const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
407 {
408 switch (ifassert_state) {
409 case PIM_IFASSERT_NOINFO:
410 return "NOINFO";
411 case PIM_IFASSERT_I_AM_WINNER:
412 return "WINNER";
413 case PIM_IFASSERT_I_AM_LOSER:
414 return "LOSER";
415 }
416
417 return "ifassert_bad_state";
418 }
419
420 /*
421 RFC 4601: 4.6.5. Assert State Macros
422
423 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
424 defaults to Infinity when in the NoInfo state.
425 */
426 void reset_ifassert_state(struct pim_ifchannel *ch)
427 {
428 struct in_addr any = {.s_addr = INADDR_ANY};
429
430 THREAD_OFF(ch->t_ifassert_timer);
431
432 pim_ifassert_winner_set(ch, PIM_IFASSERT_NOINFO, any,
433 router->infinite_assert_metric);
434 }
435
436 struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
437 struct prefix_sg *sg)
438 {
439 struct pim_interface *pim_ifp;
440 struct pim_ifchannel *ch;
441 struct pim_ifchannel lookup;
442
443 pim_ifp = ifp->info;
444
445 if (!pim_ifp) {
446 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
447 __PRETTY_FUNCTION__, pim_str_sg_dump(sg), ifp->name);
448 return NULL;
449 }
450
451 lookup.sg = *sg;
452 lookup.interface = ifp;
453 ch = RB_FIND(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, &lookup);
454
455 return ch;
456 }
457
458 static void ifmembership_set(struct pim_ifchannel *ch,
459 enum pim_ifmembership membership)
460 {
461 struct pim_interface *pim_ifp = ch->interface->info;
462
463 if (ch->local_ifmembership == membership)
464 return;
465
466 if (PIM_DEBUG_PIM_EVENTS) {
467 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
468 __PRETTY_FUNCTION__, ch->sg_str,
469 membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE"
470 : "NOINFO",
471 ch->interface->name);
472 }
473
474 ch->local_ifmembership = membership;
475
476 pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
477 pim_ifchannel_update_could_assert(ch);
478 pim_ifchannel_update_assert_tracking_desired(ch);
479 }
480
481
482 void pim_ifchannel_membership_clear(struct interface *ifp)
483 {
484 struct pim_interface *pim_ifp;
485 struct pim_ifchannel *ch;
486
487 pim_ifp = ifp->info;
488 zassert(pim_ifp);
489
490 RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb)
491 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
492 }
493
494 void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
495 {
496 struct pim_interface *pim_ifp;
497 struct pim_ifchannel *ch, *ch_tmp;
498
499 pim_ifp = ifp->info;
500 zassert(pim_ifp);
501
502 RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch_tmp)
503 delete_on_noinfo(ch);
504 }
505
506 /*
507 * For a given Interface, if we are given a S,G
508 * Find the *,G (If we have it).
509 * If we are passed a *,G, find the *,* ifchannel
510 * if we have it.
511 */
512 static struct pim_ifchannel *pim_ifchannel_find_parent(struct pim_ifchannel *ch)
513 {
514 struct prefix_sg parent_sg = ch->sg;
515 struct pim_ifchannel *parent = NULL;
516
517 // (S,G)
518 if ((parent_sg.src.s_addr != INADDR_ANY)
519 && (parent_sg.grp.s_addr != INADDR_ANY)) {
520 parent_sg.src.s_addr = INADDR_ANY;
521 parent = pim_ifchannel_find(ch->interface, &parent_sg);
522
523 if (parent)
524 listnode_add(parent->sources, ch);
525 return parent;
526 }
527
528 return NULL;
529 }
530
531 struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
532 struct prefix_sg *sg,
533 uint8_t source_flags, int up_flags)
534 {
535 struct pim_interface *pim_ifp;
536 struct pim_ifchannel *ch;
537 struct pim_upstream *up;
538
539 ch = pim_ifchannel_find(ifp, sg);
540 if (ch)
541 return ch;
542
543 pim_ifp = ifp->info;
544
545 ch = XCALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
546
547 ch->flags = 0;
548 if ((source_flags & PIM_ENCODE_RPT_BIT)
549 && !(source_flags & PIM_ENCODE_WC_BIT))
550 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
551
552 ch->interface = ifp;
553 ch->sg = *sg;
554 pim_str_sg_set(sg, ch->sg_str);
555 ch->parent = pim_ifchannel_find_parent(ch);
556 if (ch->sg.src.s_addr == INADDR_ANY) {
557 ch->sources = list_new();
558 ch->sources->cmp =
559 (int (*)(void *, void *))pim_ifchannel_compare;
560 } else
561 ch->sources = NULL;
562
563 pim_ifchannel_find_new_children(ch);
564 ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
565
566 ch->ifjoin_state = PIM_IFJOIN_NOINFO;
567 ch->t_ifjoin_expiry_timer = NULL;
568 ch->t_ifjoin_prune_pending_timer = NULL;
569 ch->ifjoin_creation = 0;
570
571 RB_INSERT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
572
573 up = pim_upstream_add(pim_ifp->pim, sg, NULL, up_flags,
574 __PRETTY_FUNCTION__, ch);
575
576 ch->upstream = up;
577
578 listnode_add_sort(up->ifchannels, ch);
579
580 ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
581 ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval(ch);
582
583 ch->ifassert_winner.s_addr = 0;
584
585 /* Assert state */
586 ch->t_ifassert_timer = NULL;
587 ch->ifassert_state = PIM_IFASSERT_NOINFO;
588 reset_ifassert_state(ch);
589 if (pim_macro_ch_could_assert_eval(ch))
590 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
591 else
592 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
593
594 if (pim_macro_assert_tracking_desired_eval(ch))
595 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
596 else
597 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
598
599 if (PIM_DEBUG_PIM_TRACE)
600 zlog_debug("%s: ifchannel %s is created ", __PRETTY_FUNCTION__,
601 ch->sg_str);
602
603 return ch;
604 }
605
606 static void ifjoin_to_noinfo(struct pim_ifchannel *ch, bool ch_del)
607 {
608 pim_forward_stop(ch, !ch_del);
609 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
610 if (ch_del)
611 delete_on_noinfo(ch);
612 }
613
614 static int on_ifjoin_expiry_timer(struct thread *t)
615 {
616 struct pim_ifchannel *ch;
617
618 ch = THREAD_ARG(t);
619
620 if (PIM_DEBUG_TRACE)
621 zlog_debug("%s: ifchannel %s expiry timer", __PRETTY_FUNCTION__,
622 ch->sg_str);
623
624 ifjoin_to_noinfo(ch, true);
625 /* ch may have been deleted */
626
627 return 0;
628 }
629
630 static int on_ifjoin_prune_pending_timer(struct thread *t)
631 {
632 struct pim_ifchannel *ch;
633 int send_prune_echo; /* boolean */
634 struct interface *ifp;
635 struct pim_interface *pim_ifp;
636
637 ch = THREAD_ARG(t);
638
639 if (PIM_DEBUG_TRACE)
640 zlog_debug(
641 "%s: IFCHANNEL%s %s Prune Pending Timer Popped",
642 __PRETTY_FUNCTION__, pim_str_sg_dump(&ch->sg),
643 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags));
644
645 if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) {
646 ifp = ch->interface;
647 pim_ifp = ifp->info;
648 if (!PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) {
649 /* Send PruneEcho(S,G) ? */
650 send_prune_echo =
651 (listcount(pim_ifp->pim_neighbor_list) > 1);
652
653 if (send_prune_echo) {
654 struct pim_rpf rpf;
655
656 rpf.source_nexthop.interface = ifp;
657 rpf.rpf_addr.u.prefix4 =
658 pim_ifp->primary_address;
659 pim_jp_agg_single_upstream_send(
660 &rpf, ch->upstream, 0);
661 }
662
663 ifjoin_to_noinfo(ch, true);
664 } else {
665 /* If SGRpt flag is set on ifchannel, Trigger SGRpt
666 * message on RP path upon prune timer expiry.
667 */
668 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
669 if (ch->upstream) {
670 struct pim_upstream *parent =
671 ch->upstream->parent;
672
673 pim_upstream_update_join_desired(pim_ifp->pim,
674 ch->upstream);
675
676 pim_jp_agg_single_upstream_send(&parent->rpf,
677 parent, true);
678 }
679 }
680 /* from here ch may have been deleted */
681 }
682
683 return 0;
684 }
685
686 static void check_recv_upstream(int is_join, struct interface *recv_ifp,
687 struct in_addr upstream, struct prefix_sg *sg,
688 uint8_t source_flags, int holdtime)
689 {
690 struct pim_upstream *up;
691 struct pim_interface *pim_ifp = recv_ifp->info;
692
693 /* Upstream (S,G) in Joined state ? */
694 up = pim_upstream_find(pim_ifp->pim, sg);
695 if (!up)
696 return;
697 if (up->join_state != PIM_UPSTREAM_JOINED)
698 return;
699
700 /* Upstream (S,G) in Joined state */
701
702 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
703 /* RPF'(S,G) not found */
704 zlog_warn("%s %s: RPF'%s not found", __FILE__,
705 __PRETTY_FUNCTION__, up->sg_str);
706 return;
707 }
708
709 /* upstream directed to RPF'(S,G) ? */
710 if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) {
711 char up_str[INET_ADDRSTRLEN];
712 char rpf_str[PREFIX_STRLEN];
713 pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
714 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str,
715 sizeof(rpf_str));
716 zlog_warn(
717 "%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
718 __FILE__, __PRETTY_FUNCTION__, up->sg_str, up_str,
719 rpf_str, recv_ifp->name);
720 return;
721 }
722 /* upstream directed to RPF'(S,G) */
723
724 if (is_join) {
725 /* Join(S,G) to RPF'(S,G) */
726 pim_upstream_join_suppress(up, up->rpf.rpf_addr.u.prefix4,
727 holdtime);
728 return;
729 }
730
731 /* Prune to RPF'(S,G) */
732
733 if (source_flags & PIM_RPT_BIT_MASK) {
734 if (source_flags & PIM_WILDCARD_BIT_MASK) {
735 /* Prune(*,G) to RPF'(S,G) */
736 pim_upstream_join_timer_decrease_to_t_override(
737 "Prune(*,G)", up);
738 return;
739 }
740
741 /* Prune(S,G,rpt) to RPF'(S,G) */
742 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
743 up);
744 return;
745 }
746
747 /* Prune(S,G) to RPF'(S,G) */
748 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
749 }
750
751 static int nonlocal_upstream(int is_join, struct interface *recv_ifp,
752 struct in_addr upstream, struct prefix_sg *sg,
753 uint8_t source_flags, uint16_t holdtime)
754 {
755 struct pim_interface *recv_pim_ifp;
756 int is_local; /* boolean */
757
758 recv_pim_ifp = recv_ifp->info;
759 zassert(recv_pim_ifp);
760
761 is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
762
763 if (is_local)
764 return 0;
765
766 if (PIM_DEBUG_PIM_TRACE_DETAIL) {
767 char up_str[INET_ADDRSTRLEN];
768 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
769 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
770 __PRETTY_FUNCTION__, is_join ? "join" : "prune",
771 pim_str_sg_dump(sg), up_str, recv_ifp->name);
772 }
773
774 /*
775 * Since recv upstream addr was not directed to our primary
776 * address, check if we should react to it in any way.
777 */
778 check_recv_upstream(is_join, recv_ifp, upstream, sg, source_flags,
779 holdtime);
780
781 return 1; /* non-local */
782 }
783
784 void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr,
785 struct in_addr upstream, struct prefix_sg *sg,
786 uint8_t source_flags, uint16_t holdtime)
787 {
788 struct pim_interface *pim_ifp;
789 struct pim_ifchannel *ch;
790
791 if (nonlocal_upstream(1 /* join */, ifp, upstream, sg, source_flags,
792 holdtime)) {
793 return;
794 }
795
796 ch = pim_ifchannel_add(ifp, sg, source_flags,
797 PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
798 if (!ch)
799 return;
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_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 if (!ch)
961 return;
962
963 pim_ifp = ifp->info;
964
965 switch (ch->ifjoin_state) {
966 case PIM_IFJOIN_NOINFO:
967 if (source_flags & PIM_ENCODE_RPT_BIT) {
968 if (!(source_flags & PIM_ENCODE_WC_BIT))
969 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
970
971 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
972 if (listcount(pim_ifp->pim_neighbor_list) > 1)
973 jp_override_interval_msec =
974 pim_if_jp_override_interval_msec(ifp);
975 else
976 jp_override_interval_msec =
977 0; /* schedule to expire immediately */
978 /* If we called ifjoin_prune() directly instead, care
979 should
980 be taken not to use "ch" afterwards since it would be
981 deleted. */
982
983 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
984 THREAD_OFF(ch->t_ifjoin_expiry_timer);
985 thread_add_timer_msec(
986 router->master, on_ifjoin_prune_pending_timer,
987 ch, jp_override_interval_msec,
988 &ch->t_ifjoin_prune_pending_timer);
989 thread_add_timer(router->master, on_ifjoin_expiry_timer,
990 ch, holdtime,
991 &ch->t_ifjoin_expiry_timer);
992 pim_upstream_update_join_desired(pim_ifp->pim,
993 ch->upstream);
994 }
995 break;
996 case PIM_IFJOIN_PRUNE_PENDING:
997 /* nothing to do */
998 break;
999 case PIM_IFJOIN_JOIN:
1000 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1001
1002 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch,
1003 PIM_IFJOIN_PRUNE_PENDING);
1004
1005 if (listcount(pim_ifp->pim_neighbor_list) > 1)
1006 jp_override_interval_msec =
1007 pim_if_jp_override_interval_msec(ifp);
1008 else
1009 jp_override_interval_msec =
1010 0; /* schedule to expire immediately */
1011 /* If we called ifjoin_prune() directly instead, care should
1012 be taken not to use "ch" afterwards since it would be
1013 deleted. */
1014 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1015 thread_add_timer_msec(router->master,
1016 on_ifjoin_prune_pending_timer, ch,
1017 jp_override_interval_msec,
1018 &ch->t_ifjoin_prune_pending_timer);
1019 break;
1020 case PIM_IFJOIN_PRUNE:
1021 if (source_flags & PIM_ENCODE_RPT_BIT) {
1022 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1023 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1024 ch, holdtime,
1025 &ch->t_ifjoin_expiry_timer);
1026 }
1027 break;
1028 case PIM_IFJOIN_PRUNE_TMP:
1029 if (source_flags & PIM_ENCODE_RPT_BIT) {
1030 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
1031 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1032 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1033 ch, holdtime,
1034 &ch->t_ifjoin_expiry_timer);
1035 }
1036 break;
1037 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1038 if (source_flags & PIM_ENCODE_RPT_BIT) {
1039 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
1040 THREAD_OFF(ch->t_ifjoin_expiry_timer);
1041 thread_add_timer(router->master, on_ifjoin_expiry_timer,
1042 ch, holdtime,
1043 &ch->t_ifjoin_expiry_timer);
1044 }
1045 break;
1046 }
1047 }
1048
1049 int pim_ifchannel_local_membership_add(struct interface *ifp,
1050 struct prefix_sg *sg)
1051 {
1052 struct pim_ifchannel *ch, *starch;
1053 struct pim_interface *pim_ifp;
1054 struct pim_instance *pim;
1055
1056 /* PIM enabled on interface? */
1057 pim_ifp = ifp->info;
1058 if (!pim_ifp) {
1059 if (PIM_DEBUG_EVENTS)
1060 zlog_debug("%s:%s Expected pim interface setup for %s",
1061 __PRETTY_FUNCTION__,
1062 pim_str_sg_dump(sg), ifp->name);
1063 return 0;
1064 }
1065
1066 if (!PIM_IF_TEST_PIM(pim_ifp->options)) {
1067 if (PIM_DEBUG_EVENTS)
1068 zlog_debug("%s:%s PIM is not configured on this interface %s",
1069 __PRETTY_FUNCTION__,
1070 pim_str_sg_dump(sg), ifp->name);
1071 return 0;
1072 }
1073
1074 pim = pim_ifp->pim;
1075
1076 /* skip (*,G) ch creation if G is of type SSM */
1077 if (sg->src.s_addr == INADDR_ANY) {
1078 if (pim_is_grp_ssm(pim, sg->grp)) {
1079 if (PIM_DEBUG_PIM_EVENTS)
1080 zlog_debug(
1081 "%s: local membership (S,G)=%s ignored as group is SSM",
1082 __PRETTY_FUNCTION__,
1083 pim_str_sg_dump(sg));
1084 return 1;
1085 }
1086 }
1087
1088 ch = pim_ifchannel_add(ifp, sg, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP);
1089 if (!ch) {
1090 if (PIM_DEBUG_EVENTS)
1091 zlog_debug("%s:%s Unable to add ifchannel",
1092 __PRETTY_FUNCTION__,
1093 pim_str_sg_dump(sg));
1094 return 0;
1095 }
1096
1097 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
1098
1099 if (sg->src.s_addr == INADDR_ANY) {
1100 struct pim_upstream *up = pim_upstream_find(pim, sg);
1101 struct pim_upstream *child;
1102 struct listnode *up_node;
1103
1104 starch = ch;
1105
1106 for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
1107 if (PIM_DEBUG_EVENTS)
1108 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1109 __FILE__, __PRETTY_FUNCTION__,
1110 child->sg_str, ifp->name,
1111 up->sg_str);
1112
1113 ch = pim_ifchannel_find(ifp, &child->sg);
1114 if (pim_upstream_evaluate_join_desired_interface(
1115 child, ch, starch)) {
1116 pim_channel_add_oif(child->channel_oil, ifp,
1117 PIM_OIF_FLAG_PROTO_STAR);
1118 pim_upstream_switch(pim, child,
1119 PIM_UPSTREAM_JOINED);
1120 }
1121 }
1122
1123 if (pim->spt.switchover == PIM_SPT_INFINITY) {
1124 if (pim->spt.plist) {
1125 struct prefix_list *plist = prefix_list_lookup(
1126 AFI_IP, pim->spt.plist);
1127 struct prefix g;
1128 g.family = AF_INET;
1129 g.prefixlen = IPV4_MAX_PREFIXLEN;
1130 g.u.prefix4 = up->sg.grp;
1131
1132 if (prefix_list_apply(plist, &g)
1133 == PREFIX_DENY) {
1134 pim_channel_add_oif(
1135 up->channel_oil, pim->regiface,
1136 PIM_OIF_FLAG_PROTO_IGMP);
1137 }
1138 }
1139 } else
1140 pim_channel_add_oif(up->channel_oil, pim->regiface,
1141 PIM_OIF_FLAG_PROTO_IGMP);
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
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
1203 /* Child node removal/ref count-- will happen as part of
1204 * parent' delete_no_info */
1205 }
1206 }
1207 delete_on_noinfo(orig);
1208 }
1209
1210 void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
1211 {
1212 int old_couldassert =
1213 PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
1214 int new_couldassert =
1215 PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
1216
1217 if (new_couldassert == old_couldassert)
1218 return;
1219
1220 if (PIM_DEBUG_PIM_EVENTS) {
1221 char src_str[INET_ADDRSTRLEN];
1222 char grp_str[INET_ADDRSTRLEN];
1223 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1224 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1225 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1226 __PRETTY_FUNCTION__, src_str, grp_str,
1227 ch->interface->name, old_couldassert,
1228 new_couldassert);
1229 }
1230
1231 if (new_couldassert) {
1232 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1233 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
1234 } else {
1235 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1236 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
1237
1238 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
1239 assert_action_a4(ch);
1240 }
1241 }
1242
1243 pim_ifchannel_update_my_assert_metric(ch);
1244 }
1245
1246 /*
1247 my_assert_metric may be affected by:
1248
1249 CouldAssert(S,G)
1250 pim_ifp->primary_address
1251 rpf->source_nexthop.mrib_metric_preference;
1252 rpf->source_nexthop.mrib_route_metric;
1253 */
1254 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
1255 {
1256 struct pim_assert_metric my_metric_new =
1257 pim_macro_ch_my_assert_metric_eval(ch);
1258
1259 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
1260 return;
1261
1262 if (PIM_DEBUG_PIM_EVENTS) {
1263 char src_str[INET_ADDRSTRLEN];
1264 char grp_str[INET_ADDRSTRLEN];
1265 char old_addr_str[INET_ADDRSTRLEN];
1266 char new_addr_str[INET_ADDRSTRLEN];
1267 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1268 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1269 pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address,
1270 old_addr_str, sizeof(old_addr_str));
1271 pim_inet4_dump("<new_addr?>", my_metric_new.ip_address,
1272 new_addr_str, sizeof(new_addr_str));
1273 zlog_debug(
1274 "%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1275 __PRETTY_FUNCTION__, src_str, grp_str,
1276 ch->interface->name,
1277 ch->ifassert_my_metric.rpt_bit_flag,
1278 ch->ifassert_my_metric.metric_preference,
1279 ch->ifassert_my_metric.route_metric, old_addr_str,
1280 my_metric_new.rpt_bit_flag,
1281 my_metric_new.metric_preference,
1282 my_metric_new.route_metric, new_addr_str);
1283 }
1284
1285 ch->ifassert_my_metric = my_metric_new;
1286
1287 if (pim_assert_metric_better(&ch->ifassert_my_metric,
1288 &ch->ifassert_winner_metric)) {
1289 assert_action_a5(ch);
1290 }
1291 }
1292
1293 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
1294 {
1295 int old_atd = PIM_FORCE_BOOLEAN(
1296 PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
1297 int new_atd =
1298 PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
1299
1300 if (new_atd == old_atd)
1301 return;
1302
1303 if (PIM_DEBUG_PIM_EVENTS) {
1304 char src_str[INET_ADDRSTRLEN];
1305 char grp_str[INET_ADDRSTRLEN];
1306 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1307 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
1308 zlog_debug(
1309 "%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1310 __PRETTY_FUNCTION__, src_str, grp_str,
1311 ch->interface->name, old_atd, new_atd);
1312 }
1313
1314 if (new_atd) {
1315 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1316 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
1317 } else {
1318 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1319 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
1320
1321 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
1322 assert_action_a5(ch);
1323 }
1324 }
1325 }
1326
1327 /*
1328 * If we have a new pim interface, check to
1329 * see if any of the pre-existing channels have
1330 * their upstream out that way and turn on forwarding
1331 * for that ifchannel then.
1332 */
1333 void pim_ifchannel_scan_forward_start(struct interface *new_ifp)
1334 {
1335 struct pim_interface *new_pim_ifp = new_ifp->info;
1336 struct pim_instance *pim = new_pim_ifp->pim;
1337 struct interface *ifp;
1338
1339 FOR_ALL_INTERFACES (pim->vrf, ifp) {
1340 struct pim_interface *loop_pim_ifp = ifp->info;
1341 struct pim_ifchannel *ch;
1342
1343 if (!loop_pim_ifp)
1344 continue;
1345
1346 if (new_pim_ifp == loop_pim_ifp)
1347 continue;
1348
1349 RB_FOREACH (ch, pim_ifchannel_rb, &loop_pim_ifp->ifchannel_rb) {
1350 if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
1351 struct pim_upstream *up = ch->upstream;
1352 if ((!up->channel_oil)
1353 && (up->rpf.source_nexthop
1354 .interface == new_ifp))
1355 pim_forward_start(ch);
1356 }
1357 }
1358 }
1359 }
1360
1361 /*
1362 * Downstream per-interface (S,G,rpt) state machine
1363 * states that we need to move (S,G,rpt) items
1364 * into different states at the start of the
1365 * reception of a *,G join as well, when
1366 * we get End of Message
1367 */
1368 void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
1369 uint8_t join)
1370 {
1371 bool send_upstream_starg = false;
1372 struct pim_ifchannel *child;
1373 struct listnode *ch_node, *nch_node;
1374 struct pim_instance *pim =
1375 ((struct pim_interface *)ch->interface->info)->pim;
1376 struct pim_upstream *starup = ch->upstream;
1377
1378 if (PIM_DEBUG_PIM_TRACE)
1379 zlog_debug(
1380 "%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__,
1381 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
1382 ch->sg_str, eom, join);
1383 if (!ch->sources)
1384 return;
1385
1386 for (ALL_LIST_ELEMENTS(ch->sources, ch_node, nch_node, child)) {
1387 if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1388 continue;
1389
1390 switch (child->ifjoin_state) {
1391 case PIM_IFJOIN_NOINFO:
1392 case PIM_IFJOIN_JOIN:
1393 break;
1394 case PIM_IFJOIN_PRUNE:
1395 if (!eom)
1396 child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
1397 break;
1398 case PIM_IFJOIN_PRUNE_PENDING:
1399 if (!eom)
1400 child->ifjoin_state =
1401 PIM_IFJOIN_PRUNE_PENDING_TMP;
1402 break;
1403 case PIM_IFJOIN_PRUNE_TMP:
1404 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1405 if (!eom)
1406 break;
1407
1408 if (child->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING_TMP)
1409 THREAD_OFF(child->t_ifjoin_prune_pending_timer);
1410 THREAD_OFF(child->t_ifjoin_expiry_timer);
1411
1412 PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
1413 child->ifjoin_state = PIM_IFJOIN_NOINFO;
1414
1415 if ((I_am_RP(pim, child->sg.grp)) &&
1416 (!pim_upstream_empty_inherited_olist(
1417 child->upstream))) {
1418 pim_channel_add_oif(
1419 child->upstream->channel_oil,
1420 ch->interface, PIM_OIF_FLAG_PROTO_STAR);
1421 pim_upstream_switch(pim, child->upstream,
1422 PIM_UPSTREAM_JOINED);
1423 pim_jp_agg_single_upstream_send(
1424 &child->upstream->rpf, child->upstream,
1425 true);
1426 }
1427 send_upstream_starg = true;
1428
1429 delete_on_noinfo(child);
1430 break;
1431 }
1432 }
1433
1434 if (send_upstream_starg)
1435 pim_jp_agg_single_upstream_send(&starup->rpf, starup, true);
1436 }
1437
1438 unsigned int pim_ifchannel_hash_key(const void *arg)
1439 {
1440 const struct pim_ifchannel *ch = arg;
1441
1442 return jhash_2words(ch->sg.src.s_addr, ch->sg.grp.s_addr, 0);
1443 }