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