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