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