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