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