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