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