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