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