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