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