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