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