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