]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_ifchannel.c
pimd: convert pim_ifchannel_local_membership_add to use struct prefix sg
[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
20 $QuaggaId: $Format:%an, %ai, %h$ $
21*/
22
23#include <zebra.h>
24
25#include "linklist.h"
26#include "thread.h"
27#include "memory.h"
744d91b3 28#include "if.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"
41
42void pim_ifchannel_free(struct pim_ifchannel *ch)
43{
44 zassert(!ch->t_ifjoin_expiry_timer);
45 zassert(!ch->t_ifjoin_prune_pending_timer);
46 zassert(!ch->t_ifassert_timer);
47
48 XFREE(MTYPE_PIM_IFCHANNEL, ch);
49}
50
51void pim_ifchannel_delete(struct pim_ifchannel *ch)
52{
53 struct pim_interface *pim_ifp;
54
55 pim_ifp = ch->interface->info;
56 zassert(pim_ifp);
57
58 if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
59 pim_upstream_update_join_desired(ch->upstream);
60 }
61
62 pim_upstream_del(ch->upstream);
63
64 THREAD_OFF(ch->t_ifjoin_expiry_timer);
65 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
66 THREAD_OFF(ch->t_ifassert_timer);
67
68 /*
69 notice that listnode_delete() can't be moved
70 into pim_ifchannel_free() because the later is
71 called by list_delete_all_node()
72 */
73 listnode_delete(pim_ifp->pim_ifchannel_list, ch);
74
75 pim_ifchannel_free(ch);
76}
77
78#define IFCHANNEL_NOINFO(ch) \
79 ( \
80 ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO) \
81 && \
82 ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO) \
83 && \
84 ((ch)->ifassert_state == PIM_IFASSERT_NOINFO) \
85 )
86
87static void delete_on_noinfo(struct pim_ifchannel *ch)
88{
89 if (IFCHANNEL_NOINFO(ch)) {
90
91 /* In NOINFO state, timers should have been cleared */
92 zassert(!ch->t_ifjoin_expiry_timer);
93 zassert(!ch->t_ifjoin_prune_pending_timer);
94 zassert(!ch->t_ifassert_timer);
95
96 pim_ifchannel_delete(ch);
97 }
98}
99
100void pim_ifchannel_ifjoin_switch(const char *caller,
101 struct pim_ifchannel *ch,
102 enum pim_ifjoin_state new_state)
103{
104 enum pim_ifjoin_state old_state = ch->ifjoin_state;
105
106 if (old_state == new_state) {
7adf0260
DS
107 if (PIM_DEBUG_PIM_EVENTS) {
108 zlog_debug("%s calledby %s: non-transition on state %d (%s)",
109 __PRETTY_FUNCTION__, caller, new_state,
110 pim_ifchannel_ifjoin_name(new_state));
111 }
12e41d03
DL
112 return;
113 }
114
115 zassert(old_state != new_state);
116
117 ch->ifjoin_state = new_state;
118
119 /* Transition to/from NOINFO ? */
120 if (
121 (old_state == PIM_IFJOIN_NOINFO)
122 ||
123 (new_state == PIM_IFJOIN_NOINFO)
124 ) {
125
126 if (PIM_DEBUG_PIM_EVENTS) {
ee1a477a 127 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
12e41d03 128 ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
ee1a477a 129 pim_str_sg_dump (&ch->sg), ch->interface->name);
12e41d03
DL
130 }
131
132 /*
133 Record uptime of state transition to/from NOINFO
134 */
135 ch->ifjoin_creation = pim_time_monotonic_sec();
136
137 pim_upstream_update_join_desired(ch->upstream);
138 pim_ifchannel_update_could_assert(ch);
139 pim_ifchannel_update_assert_tracking_desired(ch);
140 }
141}
142
143const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state)
144{
145 switch (ifjoin_state) {
146 case PIM_IFJOIN_NOINFO: return "NOINFO";
56638739 147 case PIM_IFJOIN_JOIN_PIMREG: return "REGT";
12e41d03
DL
148 case PIM_IFJOIN_JOIN: return "JOIN";
149 case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP";
150 }
151
152 return "ifjoin_bad_state";
153}
154
155const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
156{
157 switch (ifassert_state) {
158 case PIM_IFASSERT_NOINFO: return "NOINFO";
159 case PIM_IFASSERT_I_AM_WINNER: return "WINNER";
160 case PIM_IFASSERT_I_AM_LOSER: return "LOSER";
161 }
162
163 return "ifassert_bad_state";
164}
165
166/*
167 RFC 4601: 4.6.5. Assert State Macros
168
169 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
170 defaults to Infinity when in the NoInfo state.
171*/
172void reset_ifassert_state(struct pim_ifchannel *ch)
173{
174 THREAD_OFF(ch->t_ifassert_timer);
175
176 pim_ifassert_winner_set(ch,
177 PIM_IFASSERT_NOINFO,
178 qpim_inaddr_any,
179 qpim_infinite_assert_metric);
180}
181
12e41d03 182struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
5074a423 183 struct prefix *sg)
12e41d03
DL
184{
185 struct pim_interface *pim_ifp;
186 struct listnode *ch_node;
187 struct pim_ifchannel *ch;
188
189 zassert(ifp);
190
191 pim_ifp = ifp->info;
192
193 if (!pim_ifp) {
5074a423 194 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
12e41d03 195 __PRETTY_FUNCTION__,
5074a423 196 pim_str_sg_dump (sg),
12e41d03
DL
197 ifp->name);
198 return 0;
199 }
200
201 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
202 if (
5074a423
DS
203 (sg->u.sg.src.s_addr == ch->sg.u.sg.src.s_addr) &&
204 (sg->u.sg.grp.s_addr == ch->sg.u.sg.grp.s_addr)
12e41d03
DL
205 ) {
206 return ch;
207 }
208 }
209
210 return 0;
211}
212
213static void ifmembership_set(struct pim_ifchannel *ch,
214 enum pim_ifmembership membership)
215{
216 if (ch->local_ifmembership == membership)
217 return;
218
7adf0260 219 if (PIM_DEBUG_PIM_EVENTS) {
ee1a477a 220 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
12e41d03 221 __PRETTY_FUNCTION__,
ee1a477a 222 pim_str_sg_dump (&ch->sg),
12e41d03
DL
223 membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
224 ch->interface->name);
225 }
226
227 ch->local_ifmembership = membership;
228
229 pim_upstream_update_join_desired(ch->upstream);
230 pim_ifchannel_update_could_assert(ch);
231 pim_ifchannel_update_assert_tracking_desired(ch);
232}
233
234
235void pim_ifchannel_membership_clear(struct interface *ifp)
236{
237 struct pim_interface *pim_ifp;
238 struct listnode *ch_node;
239 struct pim_ifchannel *ch;
240
241 pim_ifp = ifp->info;
242 zassert(pim_ifp);
243
244 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
245 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
246 }
247}
248
249void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
250{
251 struct pim_interface *pim_ifp;
252 struct listnode *node;
253 struct listnode *next_node;
254 struct pim_ifchannel *ch;
255
256 pim_ifp = ifp->info;
257 zassert(pim_ifp);
258
259 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
260 delete_on_noinfo(ch);
261 }
262}
263
264struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
5074a423 265 struct prefix *sg)
12e41d03 266{
535db05b 267 struct pim_interface *pim_ifp;
12e41d03 268 struct pim_ifchannel *ch;
535db05b 269 struct pim_upstream *up;
12e41d03 270
5074a423 271 ch = pim_ifchannel_find(ifp, sg);
12e41d03
DL
272 if (ch)
273 return ch;
274
535db05b
DS
275 pim_ifp = ifp->info;
276 zassert(pim_ifp);
277
5074a423 278 up = pim_upstream_add(sg, NULL);
535db05b 279 if (!up) {
5074a423 280 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
535db05b 281 __PRETTY_FUNCTION__,
5074a423 282 pim_str_sg_dump (sg), ifp->name);
535db05b
DS
283 return NULL;
284 }
285
286 ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
287 if (!ch) {
5074a423 288 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
535db05b 289 __PRETTY_FUNCTION__,
5074a423 290 pim_str_sg_dump (sg), ifp->name);
12e41d03 291
535db05b
DS
292 return NULL;
293 }
12e41d03 294
535db05b
DS
295 ch->flags = 0;
296 ch->upstream = up;
297 ch->interface = ifp;
5074a423 298 ch->sg = *sg;
535db05b
DS
299 ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
300
301 ch->ifjoin_state = PIM_IFJOIN_NOINFO;
302 ch->t_ifjoin_expiry_timer = NULL;
303 ch->t_ifjoin_prune_pending_timer = NULL;
304 ch->ifjoin_creation = 0;
305
306 ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
307 ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch);
308
309 ch->ifassert_winner.s_addr = 0;
310
311 /* Assert state */
312 ch->t_ifassert_timer = NULL;
313 reset_ifassert_state(ch);
314 if (pim_macro_ch_could_assert_eval(ch))
315 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
316 else
317 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
318
319 if (pim_macro_assert_tracking_desired_eval(ch))
320 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
321 else
322 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
323
324 /* Attach to list */
325 listnode_add(pim_ifp->pim_ifchannel_list, ch);
326
327 zassert(IFCHANNEL_NOINFO(ch));
328
329 return ch;
12e41d03
DL
330}
331
332static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
333{
334 pim_forward_stop(ch);
335 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
336 delete_on_noinfo(ch);
337}
338
339static int on_ifjoin_expiry_timer(struct thread *t)
340{
341 struct pim_ifchannel *ch;
342
343 zassert(t);
344 ch = THREAD_ARG(t);
345 zassert(ch);
346
59ba0ac3 347 ch->t_ifjoin_expiry_timer = NULL;
12e41d03
DL
348
349 zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN);
350
351 ifjoin_to_noinfo(ch);
352 /* ch may have been deleted */
353
354 return 0;
355}
356
12e41d03
DL
357static int on_ifjoin_prune_pending_timer(struct thread *t)
358{
359 struct pim_ifchannel *ch;
360 int send_prune_echo; /* boolean */
361 struct interface *ifp;
362 struct pim_interface *pim_ifp;
05e451f8 363 struct prefix sg;
12e41d03
DL
364
365 zassert(t);
366 ch = THREAD_ARG(t);
367 zassert(ch);
368
59ba0ac3 369 ch->t_ifjoin_prune_pending_timer = NULL;
12e41d03
DL
370
371 zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING);
372
373 /* Send PruneEcho(S,G) ? */
374 ifp = ch->interface;
375 pim_ifp = ifp->info;
376 send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
377
378 /* Save (S,G) */
05e451f8 379 sg = ch->sg;
12e41d03
DL
380
381 ifjoin_to_noinfo(ch);
382 /* from here ch may have been deleted */
383
384 if (send_prune_echo)
64a70f46 385 pim_joinprune_send (ifp, pim_ifp->primary_address,
05e451f8 386 &sg, 0);
12e41d03
DL
387
388 return 0;
389}
390
391static void check_recv_upstream(int is_join,
392 struct interface *recv_ifp,
393 struct in_addr upstream,
394 struct in_addr source_addr,
395 struct in_addr group_addr,
396 uint8_t source_flags,
397 int holdtime)
398{
399 struct pim_upstream *up;
5074a423 400 struct prefix sg;
12e41d03 401
5074a423
DS
402 memset (&sg, 0, sizeof (struct prefix));
403 sg.u.sg.src = source_addr;
404 sg.u.sg.grp = group_addr;
12e41d03 405 /* Upstream (S,G) in Joined state ? */
5074a423 406 up = pim_upstream_find(&sg);
12e41d03
DL
407 if (!up)
408 return;
409 if (up->join_state != PIM_UPSTREAM_JOINED)
410 return;
411
412 /* Upstream (S,G) in Joined state */
413
414 if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
415 /* RPF'(S,G) not found */
416 char src_str[100];
417 char grp_str[100];
418 pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
419 pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
420 zlog_warn("%s %s: RPF'(%s,%s) not found",
421 __FILE__, __PRETTY_FUNCTION__,
422 src_str, grp_str);
423 return;
424 }
425
426 /* upstream directed to RPF'(S,G) ? */
427 if (upstream.s_addr != up->rpf.rpf_addr.s_addr) {
428 char src_str[100];
429 char grp_str[100];
430 char up_str[100];
431 char rpf_str[100];
432 pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
433 pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
434 pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
435 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
436 zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s",
437 __FILE__, __PRETTY_FUNCTION__,
438 src_str, grp_str,
439 up_str, rpf_str, recv_ifp->name);
440 return;
441 }
442 /* upstream directed to RPF'(S,G) */
443
444 if (is_join) {
445 /* Join(S,G) to RPF'(S,G) */
446 pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
447 return;
448 }
449
450 /* Prune to RPF'(S,G) */
451
452 if (source_flags & PIM_RPT_BIT_MASK) {
453 if (source_flags & PIM_WILDCARD_BIT_MASK) {
454 /* Prune(*,G) to RPF'(S,G) */
455 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
456 up, up->rpf.rpf_addr);
457 return;
458 }
459
460 /* Prune(S,G,rpt) to RPF'(S,G) */
461 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
462 up, up->rpf.rpf_addr);
463 return;
464 }
465
466 /* Prune(S,G) to RPF'(S,G) */
467 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up,
468 up->rpf.rpf_addr);
469}
470
471static int nonlocal_upstream(int is_join,
472 struct interface *recv_ifp,
473 struct in_addr upstream,
474 struct in_addr source_addr,
475 struct in_addr group_addr,
476 uint8_t source_flags,
477 uint16_t holdtime)
478{
479 struct pim_interface *recv_pim_ifp;
480 int is_local; /* boolean */
481
482 recv_pim_ifp = recv_ifp->info;
483 zassert(recv_pim_ifp);
484
485 is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
486
487 if (PIM_DEBUG_PIM_TRACE) {
488 char up_str[100];
489 char src_str[100];
490 char grp_str[100];
491 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
492 pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
493 pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
494 zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s",
495 __PRETTY_FUNCTION__,
496 is_join ? "join" : "prune",
497 src_str, grp_str,
498 is_local ? "local" : "non-local",
499 up_str, recv_ifp->name);
500 }
501
502 if (is_local)
503 return 0;
504
505 /*
506 Since recv upstream addr was not directed to our primary
507 address, check if we should react to it in any way.
508 */
509 check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr,
510 source_flags, holdtime);
511
512 return 1; /* non-local */
513}
514
515void pim_ifchannel_join_add(struct interface *ifp,
516 struct in_addr neigh_addr,
517 struct in_addr upstream,
ee1a477a 518 struct prefix *sg,
12e41d03
DL
519 uint8_t source_flags,
520 uint16_t holdtime)
521{
522 struct pim_interface *pim_ifp;
523 struct pim_ifchannel *ch;
524
525 if (nonlocal_upstream(1 /* join */, ifp, upstream,
ee1a477a 526 sg->u.sg.src, sg->u.sg.grp, source_flags, holdtime)) {
12e41d03
DL
527 return;
528 }
529
ee1a477a 530 ch = pim_ifchannel_add(ifp, sg);
12e41d03
DL
531 if (!ch)
532 return;
533
534 /*
535 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
536
537 Transitions from "I am Assert Loser" State
538
539 Receive Join(S,G) on Interface I
540
541 We receive a Join(S,G) that has the Upstream Neighbor Address
542 field set to my primary IP address on interface I. The action is
543 to transition to NoInfo state, delete this (S,G) assert state
544 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
545 to operate.
546
547 Notice: The nonlocal_upstream() test above ensures the upstream
548 address of the join message is our primary address.
549 */
550 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
12e41d03 551 char neigh_str[100];
12e41d03 552 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
5074a423 553 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
12e41d03 554 __PRETTY_FUNCTION__,
ee1a477a 555 pim_str_sg_dump (sg), neigh_str, ifp->name);
12e41d03
DL
556
557 assert_action_a5(ch);
558 }
559
560 pim_ifp = ifp->info;
561 zassert(pim_ifp);
562
563 switch (ch->ifjoin_state) {
564 case PIM_IFJOIN_NOINFO:
565 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
566 if (pim_macro_chisin_oiflist(ch)) {
567 pim_forward_start(ch);
568 }
569 break;
570 case PIM_IFJOIN_JOIN:
571 zassert(!ch->t_ifjoin_prune_pending_timer);
572
573 /*
574 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
575 previously received join message with holdtime=0xFFFF.
576 */
577 if (ch->t_ifjoin_expiry_timer) {
578 unsigned long remain =
579 thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
580 if (remain > holdtime) {
581 /*
582 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
583
584 Transitions from Join State
585
586 The (S,G) downstream state machine on interface I remains in
587 Join state, and the Expiry Timer (ET) is restarted, set to
588 maximum of its current value and the HoldTime from the
589 triggering Join/Prune message.
590
591 Conclusion: Do not change the ET if the current value is
592 higher than the received join holdtime.
593 */
594 return;
595 }
596 }
597 THREAD_OFF(ch->t_ifjoin_expiry_timer);
598 break;
599 case PIM_IFJOIN_PRUNE_PENDING:
600 zassert(!ch->t_ifjoin_expiry_timer);
601 zassert(ch->t_ifjoin_prune_pending_timer);
602 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
603 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
604 break;
56638739
DS
605 case PIM_IFJOIN_JOIN_PIMREG:
606 zlog_warn("Received Incorrect new state");
12e41d03
DL
607 }
608
609 zassert(!IFCHANNEL_NOINFO(ch));
610
611 if (holdtime != 0xFFFF) {
612 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
613 on_ifjoin_expiry_timer,
614 ch, holdtime);
615 }
616}
617
618void pim_ifchannel_prune(struct interface *ifp,
619 struct in_addr upstream,
7bd2c6fa 620 struct prefix *sg,
12e41d03
DL
621 uint8_t source_flags,
622 uint16_t holdtime)
623{
624 struct pim_ifchannel *ch;
625 int jp_override_interval_msec;
626
627 if (nonlocal_upstream(0 /* prune */, ifp, upstream,
7bd2c6fa 628 sg->u.sg.src, sg->u.sg.grp, source_flags, holdtime)) {
12e41d03
DL
629 return;
630 }
631
7bd2c6fa 632 ch = pim_ifchannel_add(ifp, sg);
12e41d03
DL
633 if (!ch)
634 return;
635
636 switch (ch->ifjoin_state) {
637 case PIM_IFJOIN_NOINFO:
638 case PIM_IFJOIN_PRUNE_PENDING:
56638739 639 case PIM_IFJOIN_JOIN_PIMREG:
12e41d03
DL
640 /* nothing to do */
641 break;
642 case PIM_IFJOIN_JOIN:
643 {
644 struct pim_interface *pim_ifp;
645
646 pim_ifp = ifp->info;
647
648 zassert(ch->t_ifjoin_expiry_timer);
649 zassert(!ch->t_ifjoin_prune_pending_timer);
650
651 THREAD_OFF(ch->t_ifjoin_expiry_timer);
652
653 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
654
655 if (listcount(pim_ifp->pim_neighbor_list) > 1) {
656 jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
657 }
658 else {
659 jp_override_interval_msec = 0; /* schedule to expire immediately */
660 /* If we called ifjoin_prune() directly instead, care should
661 be taken not to use "ch" afterwards since it would be
662 deleted. */
663 }
664
665 THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
666 on_ifjoin_prune_pending_timer,
667 ch, jp_override_interval_msec);
668
669 zassert(!ch->t_ifjoin_expiry_timer);
670 zassert(ch->t_ifjoin_prune_pending_timer);
671 }
672 break;
673 }
674
675}
676
677void pim_ifchannel_local_membership_add(struct interface *ifp,
69283639 678 struct prefix *sg)
12e41d03
DL
679{
680 struct pim_ifchannel *ch;
681 struct pim_interface *pim_ifp;
682
683 /* PIM enabled on interface? */
684 pim_ifp = ifp->info;
685 if (!pim_ifp)
686 return;
687 if (!PIM_IF_TEST_PIM(pim_ifp->options))
688 return;
689
69283639 690 ch = pim_ifchannel_add(ifp, sg);
12e41d03
DL
691 if (!ch) {
692 return;
693 }
694
695 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
696
697 zassert(!IFCHANNEL_NOINFO(ch));
698}
699
700void pim_ifchannel_local_membership_del(struct interface *ifp,
701 struct in_addr source_addr,
702 struct in_addr group_addr)
703{
704 struct pim_ifchannel *ch;
705 struct pim_interface *pim_ifp;
5074a423
DS
706 struct prefix sg;
707
708 memset (&sg, 0, sizeof (struct prefix));
709 sg.u.sg.src = source_addr;
710 sg.u.sg.grp = group_addr;
12e41d03
DL
711
712 /* PIM enabled on interface? */
713 pim_ifp = ifp->info;
714 if (!pim_ifp)
715 return;
716 if (!PIM_IF_TEST_PIM(pim_ifp->options))
717 return;
718
5074a423 719 ch = pim_ifchannel_find(ifp, &sg);
12e41d03
DL
720 if (!ch)
721 return;
722
723 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
724
725 delete_on_noinfo(ch);
726}
727
728void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
729{
730 int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
731 int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
732
733 if (new_couldassert == old_couldassert)
734 return;
735
736 if (PIM_DEBUG_PIM_EVENTS) {
737 char src_str[100];
738 char grp_str[100];
99064df9
DS
739 pim_inet4_dump("<src?>", ch->sg.u.sg.src, src_str, sizeof(src_str));
740 pim_inet4_dump("<grp?>", ch->sg.u.sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
741 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
742 __PRETTY_FUNCTION__,
743 src_str, grp_str, ch->interface->name,
744 old_couldassert, new_couldassert);
745 }
746
747 if (new_couldassert) {
748 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
749 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
750 }
751 else {
752 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
753 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
754
755 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
756 assert_action_a4(ch);
757 }
758 }
759
760 pim_ifchannel_update_my_assert_metric(ch);
761}
762
763/*
764 my_assert_metric may be affected by:
765
766 CouldAssert(S,G)
767 pim_ifp->primary_address
768 rpf->source_nexthop.mrib_metric_preference;
769 rpf->source_nexthop.mrib_route_metric;
770 */
771void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
772{
773 struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
774
775 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
776 return;
777
778 if (PIM_DEBUG_PIM_EVENTS) {
779 char src_str[100];
780 char grp_str[100];
781 char old_addr_str[100];
782 char new_addr_str[100];
99064df9
DS
783 pim_inet4_dump("<src?>", ch->sg.u.sg.src, src_str, sizeof(src_str));
784 pim_inet4_dump("<grp?>", ch->sg.u.sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
785 pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
786 pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
787 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
788 __PRETTY_FUNCTION__,
789 src_str, grp_str, ch->interface->name,
790 ch->ifassert_my_metric.rpt_bit_flag,
791 ch->ifassert_my_metric.metric_preference,
792 ch->ifassert_my_metric.route_metric,
793 old_addr_str,
794 my_metric_new.rpt_bit_flag,
795 my_metric_new.metric_preference,
796 my_metric_new.route_metric,
797 new_addr_str);
798 }
799
800 ch->ifassert_my_metric = my_metric_new;
801
802 if (pim_assert_metric_better(&ch->ifassert_my_metric,
803 &ch->ifassert_winner_metric)) {
804 assert_action_a5(ch);
805 }
806}
807
808void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
809{
810 int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
811 int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
812
813 if (new_atd == old_atd)
814 return;
815
816 if (PIM_DEBUG_PIM_EVENTS) {
817 char src_str[100];
818 char grp_str[100];
99064df9
DS
819 pim_inet4_dump("<src?>", ch->sg.u.sg.src, src_str, sizeof(src_str));
820 pim_inet4_dump("<grp?>", ch->sg.u.sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
821 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
822 __PRETTY_FUNCTION__,
823 src_str, grp_str, ch->interface->name,
824 old_atd, new_atd);
825 }
826
827 if (new_atd) {
828 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
829 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
830 }
831 else {
832 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
833 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
834
835 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
836 assert_action_a5(ch);
837 }
838 }
839}