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