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