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