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