]> git.proxmox.com Git - mirror_frr.git/blame_incremental - pimd/pim_ifchannel.c
pimd: convert pim_ifchannel_local_membership_add to use struct prefix sg
[mirror_frr.git] / pimd / pim_ifchannel.c
... / ...
CommitLineData
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"
28#include "if.h"
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) {
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 }
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) {
127 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
128 ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
129 pim_str_sg_dump (&ch->sg), ch->interface->name);
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";
147 case PIM_IFJOIN_JOIN_PIMREG: return "REGT";
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
182struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
183 struct prefix *sg)
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) {
194 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
195 __PRETTY_FUNCTION__,
196 pim_str_sg_dump (sg),
197 ifp->name);
198 return 0;
199 }
200
201 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
202 if (
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)
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
219 if (PIM_DEBUG_PIM_EVENTS) {
220 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
221 __PRETTY_FUNCTION__,
222 pim_str_sg_dump (&ch->sg),
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,
265 struct prefix *sg)
266{
267 struct pim_interface *pim_ifp;
268 struct pim_ifchannel *ch;
269 struct pim_upstream *up;
270
271 ch = pim_ifchannel_find(ifp, sg);
272 if (ch)
273 return ch;
274
275 pim_ifp = ifp->info;
276 zassert(pim_ifp);
277
278 up = pim_upstream_add(sg, NULL);
279 if (!up) {
280 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
281 __PRETTY_FUNCTION__,
282 pim_str_sg_dump (sg), ifp->name);
283 return NULL;
284 }
285
286 ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
287 if (!ch) {
288 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
289 __PRETTY_FUNCTION__,
290 pim_str_sg_dump (sg), ifp->name);
291
292 return NULL;
293 }
294
295 ch->flags = 0;
296 ch->upstream = up;
297 ch->interface = ifp;
298 ch->sg = *sg;
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;
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
347 ch->t_ifjoin_expiry_timer = NULL;
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
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;
363 struct prefix sg;
364
365 zassert(t);
366 ch = THREAD_ARG(t);
367 zassert(ch);
368
369 ch->t_ifjoin_prune_pending_timer = NULL;
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) */
379 sg = ch->sg;
380
381 ifjoin_to_noinfo(ch);
382 /* from here ch may have been deleted */
383
384 if (send_prune_echo)
385 pim_joinprune_send (ifp, pim_ifp->primary_address,
386 &sg, 0);
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;
400 struct prefix sg;
401
402 memset (&sg, 0, sizeof (struct prefix));
403 sg.u.sg.src = source_addr;
404 sg.u.sg.grp = group_addr;
405 /* Upstream (S,G) in Joined state ? */
406 up = pim_upstream_find(&sg);
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,
518 struct prefix *sg,
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,
526 sg->u.sg.src, sg->u.sg.grp, source_flags, holdtime)) {
527 return;
528 }
529
530 ch = pim_ifchannel_add(ifp, sg);
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) {
551 char neigh_str[100];
552 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
553 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
554 __PRETTY_FUNCTION__,
555 pim_str_sg_dump (sg), neigh_str, ifp->name);
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;
605 case PIM_IFJOIN_JOIN_PIMREG:
606 zlog_warn("Received Incorrect new state");
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,
620 struct prefix *sg,
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,
628 sg->u.sg.src, sg->u.sg.grp, source_flags, holdtime)) {
629 return;
630 }
631
632 ch = pim_ifchannel_add(ifp, sg);
633 if (!ch)
634 return;
635
636 switch (ch->ifjoin_state) {
637 case PIM_IFJOIN_NOINFO:
638 case PIM_IFJOIN_PRUNE_PENDING:
639 case PIM_IFJOIN_JOIN_PIMREG:
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,
678 struct prefix *sg)
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
690 ch = pim_ifchannel_add(ifp, sg);
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;
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;
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
719 ch = pim_ifchannel_find(ifp, &sg);
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];
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));
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];
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));
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];
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));
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}