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