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