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