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