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