]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_ifchannel.c
pimd: Convert pim_ifchannel_join_add to struct prefix *sg
[mirror_frr.git] / pimd / pim_ifchannel.c
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
42 void 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
51 void 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
87 static 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
100 void 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
143 const 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
155 const 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 */
172 void 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
182 struct 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
213 static 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
235 void 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
249 void 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
264 struct 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
332 static 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
339 static 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
357 static 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
391 static 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
471 static 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
515 void 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
618 void pim_ifchannel_prune(struct interface *ifp,
619 struct in_addr upstream,
620 struct in_addr source_addr,
621 struct in_addr group_addr,
622 uint8_t source_flags,
623 uint16_t holdtime)
624 {
625 struct pim_ifchannel *ch;
626 int jp_override_interval_msec;
627 struct prefix sg;
628
629 memset (&sg, 0, sizeof (struct prefix));
630 sg.u.sg.src = source_addr;
631 sg.u.sg.grp = group_addr;
632
633 if (nonlocal_upstream(0 /* prune */, ifp, upstream,
634 source_addr, group_addr, source_flags, holdtime)) {
635 return;
636 }
637
638 ch = pim_ifchannel_add(ifp, &sg);
639 if (!ch)
640 return;
641
642 switch (ch->ifjoin_state) {
643 case PIM_IFJOIN_NOINFO:
644 case PIM_IFJOIN_PRUNE_PENDING:
645 case PIM_IFJOIN_JOIN_PIMREG:
646 /* nothing to do */
647 break;
648 case PIM_IFJOIN_JOIN:
649 {
650 struct pim_interface *pim_ifp;
651
652 pim_ifp = ifp->info;
653
654 zassert(ch->t_ifjoin_expiry_timer);
655 zassert(!ch->t_ifjoin_prune_pending_timer);
656
657 THREAD_OFF(ch->t_ifjoin_expiry_timer);
658
659 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
660
661 if (listcount(pim_ifp->pim_neighbor_list) > 1) {
662 jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
663 }
664 else {
665 jp_override_interval_msec = 0; /* schedule to expire immediately */
666 /* If we called ifjoin_prune() directly instead, care should
667 be taken not to use "ch" afterwards since it would be
668 deleted. */
669 }
670
671 THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
672 on_ifjoin_prune_pending_timer,
673 ch, jp_override_interval_msec);
674
675 zassert(!ch->t_ifjoin_expiry_timer);
676 zassert(ch->t_ifjoin_prune_pending_timer);
677 }
678 break;
679 }
680
681 }
682
683 void pim_ifchannel_local_membership_add(struct interface *ifp,
684 struct in_addr source_addr,
685 struct in_addr group_addr)
686 {
687 struct pim_ifchannel *ch;
688 struct pim_interface *pim_ifp;
689 struct prefix sg;
690
691 memset (&sg, 0, sizeof (struct prefix));
692 sg.u.sg.src = source_addr;
693 sg.u.sg.grp = group_addr;
694
695 /* PIM enabled on interface? */
696 pim_ifp = ifp->info;
697 if (!pim_ifp)
698 return;
699 if (!PIM_IF_TEST_PIM(pim_ifp->options))
700 return;
701
702 ch = pim_ifchannel_add(ifp, &sg);
703 if (!ch) {
704 return;
705 }
706
707 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
708
709 zassert(!IFCHANNEL_NOINFO(ch));
710 }
711
712 void pim_ifchannel_local_membership_del(struct interface *ifp,
713 struct in_addr source_addr,
714 struct in_addr group_addr)
715 {
716 struct pim_ifchannel *ch;
717 struct pim_interface *pim_ifp;
718 struct prefix sg;
719
720 memset (&sg, 0, sizeof (struct prefix));
721 sg.u.sg.src = source_addr;
722 sg.u.sg.grp = group_addr;
723
724 /* PIM enabled on interface? */
725 pim_ifp = ifp->info;
726 if (!pim_ifp)
727 return;
728 if (!PIM_IF_TEST_PIM(pim_ifp->options))
729 return;
730
731 ch = pim_ifchannel_find(ifp, &sg);
732 if (!ch)
733 return;
734
735 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
736
737 delete_on_noinfo(ch);
738 }
739
740 void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
741 {
742 int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
743 int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
744
745 if (new_couldassert == old_couldassert)
746 return;
747
748 if (PIM_DEBUG_PIM_EVENTS) {
749 char src_str[100];
750 char grp_str[100];
751 pim_inet4_dump("<src?>", ch->sg.u.sg.src, src_str, sizeof(src_str));
752 pim_inet4_dump("<grp?>", ch->sg.u.sg.grp, grp_str, sizeof(grp_str));
753 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
754 __PRETTY_FUNCTION__,
755 src_str, grp_str, ch->interface->name,
756 old_couldassert, new_couldassert);
757 }
758
759 if (new_couldassert) {
760 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
761 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
762 }
763 else {
764 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
765 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
766
767 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
768 assert_action_a4(ch);
769 }
770 }
771
772 pim_ifchannel_update_my_assert_metric(ch);
773 }
774
775 /*
776 my_assert_metric may be affected by:
777
778 CouldAssert(S,G)
779 pim_ifp->primary_address
780 rpf->source_nexthop.mrib_metric_preference;
781 rpf->source_nexthop.mrib_route_metric;
782 */
783 void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
784 {
785 struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
786
787 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
788 return;
789
790 if (PIM_DEBUG_PIM_EVENTS) {
791 char src_str[100];
792 char grp_str[100];
793 char old_addr_str[100];
794 char new_addr_str[100];
795 pim_inet4_dump("<src?>", ch->sg.u.sg.src, src_str, sizeof(src_str));
796 pim_inet4_dump("<grp?>", ch->sg.u.sg.grp, grp_str, sizeof(grp_str));
797 pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
798 pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
799 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
800 __PRETTY_FUNCTION__,
801 src_str, grp_str, ch->interface->name,
802 ch->ifassert_my_metric.rpt_bit_flag,
803 ch->ifassert_my_metric.metric_preference,
804 ch->ifassert_my_metric.route_metric,
805 old_addr_str,
806 my_metric_new.rpt_bit_flag,
807 my_metric_new.metric_preference,
808 my_metric_new.route_metric,
809 new_addr_str);
810 }
811
812 ch->ifassert_my_metric = my_metric_new;
813
814 if (pim_assert_metric_better(&ch->ifassert_my_metric,
815 &ch->ifassert_winner_metric)) {
816 assert_action_a5(ch);
817 }
818 }
819
820 void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
821 {
822 int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
823 int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
824
825 if (new_atd == old_atd)
826 return;
827
828 if (PIM_DEBUG_PIM_EVENTS) {
829 char src_str[100];
830 char grp_str[100];
831 pim_inet4_dump("<src?>", ch->sg.u.sg.src, src_str, sizeof(src_str));
832 pim_inet4_dump("<grp?>", ch->sg.u.sg.grp, grp_str, sizeof(grp_str));
833 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
834 __PRETTY_FUNCTION__,
835 src_str, grp_str, ch->interface->name,
836 old_atd, new_atd);
837 }
838
839 if (new_atd) {
840 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
841 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
842 }
843 else {
844 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
845 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
846
847 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
848 assert_action_a5(ch);
849 }
850 }
851 }