]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_upstream.c
Merge remote-tracking branch 'origin/master' into bgpafisafi
[mirror_frr.git] / pimd / pim_upstream.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 "zebra/rib.h"
24
25 #include "log.h"
26 #include "zclient.h"
27 #include "memory.h"
28 #include "thread.h"
29 #include "linklist.h"
30 #include "vty.h"
31 #include "plist.h"
32 #include "hash.h"
33 #include "jhash.h"
34 #include "wheel.h"
35
36 #include "pimd.h"
37 #include "pim_pim.h"
38 #include "pim_str.h"
39 #include "pim_time.h"
40 #include "pim_iface.h"
41 #include "pim_join.h"
42 #include "pim_zlookup.h"
43 #include "pim_upstream.h"
44 #include "pim_ifchannel.h"
45 #include "pim_neighbor.h"
46 #include "pim_rpf.h"
47 #include "pim_zebra.h"
48 #include "pim_oil.h"
49 #include "pim_macro.h"
50 #include "pim_rp.h"
51 #include "pim_br.h"
52 #include "pim_register.h"
53 #include "pim_msdp.h"
54
55 struct hash *pim_upstream_hash = NULL;
56 struct list *pim_upstream_list = NULL;
57 struct timer_wheel *pim_upstream_sg_wheel = NULL;
58
59 static void join_timer_start(struct pim_upstream *up);
60 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
61
62 /*
63 * A (*,G) or a (*,*) is going away
64 * remove the parent pointer from
65 * those pointing at us
66 */
67 static void
68 pim_upstream_remove_children (struct pim_upstream *up)
69 {
70 struct pim_upstream *child;
71
72 if (!up->sources)
73 return;
74
75 while (!list_isempty (up->sources))
76 {
77 child = listnode_head (up->sources);
78 child->parent = NULL;
79 listnode_delete (up->sources, child);
80 }
81 }
82
83 /*
84 * A (*,G) or a (*,*) is being created
85 * Find the children that would point
86 * at us.
87 */
88 static void
89 pim_upstream_find_new_children (struct pim_upstream *up)
90 {
91 struct pim_upstream *child;
92 struct listnode *ch_node;
93
94 if ((up->sg.src.s_addr != INADDR_ANY) &&
95 (up->sg.grp.s_addr != INADDR_ANY))
96 return;
97
98 if ((up->sg.src.s_addr == INADDR_ANY) &&
99 (up->sg.grp.s_addr == INADDR_ANY))
100 return;
101
102 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, ch_node, child))
103 {
104 if ((up->sg.grp.s_addr != INADDR_ANY) &&
105 (child->sg.grp.s_addr == up->sg.grp.s_addr) &&
106 (child != up))
107 {
108 child->parent = up;
109 listnode_add_sort (up->sources, child);
110 }
111 }
112 }
113
114 /*
115 * If we have a (*,*) || (S,*) there is no parent
116 * If we have a (S,G), find the (*,G)
117 * If we have a (*,G), find the (*,*)
118 */
119 static struct pim_upstream *
120 pim_upstream_find_parent (struct pim_upstream *child)
121 {
122 struct prefix_sg any = child->sg;
123 struct pim_upstream *up = NULL;
124
125 // (S,G)
126 if ((child->sg.src.s_addr != INADDR_ANY) &&
127 (child->sg.grp.s_addr != INADDR_ANY))
128 {
129 any.src.s_addr = INADDR_ANY;
130 up = pim_upstream_find (&any);
131
132 if (up)
133 listnode_add (up->sources, child);
134
135 return up;
136 }
137
138 return NULL;
139 }
140
141 void pim_upstream_free(struct pim_upstream *up)
142 {
143 XFREE(MTYPE_PIM_UPSTREAM, up);
144 }
145
146 static void upstream_channel_oil_detach(struct pim_upstream *up)
147 {
148 if (up->channel_oil) {
149 pim_channel_oil_del(up->channel_oil);
150 up->channel_oil = NULL;
151 }
152 }
153
154 void
155 pim_upstream_del(struct pim_upstream *up, const char *name)
156 {
157 bool notify_msdp = false;
158
159 if (PIM_DEBUG_TRACE)
160 zlog_debug ("%s(%s): Delete %s ref count: %d",
161 __PRETTY_FUNCTION__, name, up->sg_str, up->ref_count);
162
163 --up->ref_count;
164
165 if (up->ref_count >= 1)
166 return;
167
168 THREAD_OFF(up->t_join_timer);
169 THREAD_OFF(up->t_ka_timer);
170 THREAD_OFF(up->t_rs_timer);
171 THREAD_OFF(up->t_msdp_reg_timer);
172
173 if (up->join_state == PIM_UPSTREAM_JOINED) {
174 pim_joinprune_send (up->rpf.source_nexthop.interface,
175 up->rpf.rpf_addr.u.prefix4,
176 up, 0);
177 if (up->sg.src.s_addr == INADDR_ANY) {
178 /* if a (*, G) entry in the joined state is being deleted we
179 * need to notify MSDP */
180 notify_msdp = true;
181 }
182 }
183
184 if (up->sg.src.s_addr != INADDR_ANY) {
185 wheel_remove_item (pim_upstream_sg_wheel, up);
186 notify_msdp = true;
187 }
188
189 pim_upstream_remove_children (up);
190 pim_mroute_del (up->channel_oil, __PRETTY_FUNCTION__);
191 upstream_channel_oil_detach(up);
192
193 if (up->sources)
194 list_delete (up->sources);
195 up->sources = NULL;
196
197 /*
198 notice that listnode_delete() can't be moved
199 into pim_upstream_free() because the later is
200 called by list_delete_all_node()
201 */
202 if (up->parent)
203 {
204 listnode_delete (up->parent->sources, up);
205 up->parent = NULL;
206 }
207 listnode_delete (pim_upstream_list, up);
208 hash_release (pim_upstream_hash, up);
209
210 if (notify_msdp) {
211 pim_msdp_up_del(&up->sg);
212 }
213 pim_upstream_free(up);
214 }
215
216 void
217 pim_upstream_send_join (struct pim_upstream *up)
218 {
219 if (PIM_DEBUG_TRACE) {
220 char rpf_str[PREFIX_STRLEN];
221 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
222 zlog_debug ("%s: RPF'%s=%s(%s) for Interface %s", __PRETTY_FUNCTION__,
223 up->sg_str, rpf_str, pim_upstream_state2str (up->join_state),
224 up->rpf.source_nexthop.interface->name);
225 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
226 zlog_debug("%s: can't send join upstream: RPF'%s=%s",
227 __PRETTY_FUNCTION__,
228 up->sg_str, rpf_str);
229 /* warning only */
230 }
231 }
232
233 /* send Join(S,G) to the current upstream neighbor */
234 pim_joinprune_send(up->rpf.source_nexthop.interface,
235 up->rpf.rpf_addr.u.prefix4,
236 up,
237 1 /* join */);
238 }
239
240 static int on_join_timer(struct thread *t)
241 {
242 struct pim_upstream *up;
243
244 up = THREAD_ARG(t);
245
246 up->t_join_timer = NULL;
247
248 /*
249 * In the case of a HFR we will not ahve anyone to send this to.
250 */
251 if (PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
252 return 0;
253
254 /*
255 * Don't send the join if the outgoing interface is a loopback
256 * But since this might change leave the join timer running
257 */
258 if (!if_is_loopback (up->rpf.source_nexthop.interface))
259 pim_upstream_send_join (up);
260
261 join_timer_start(up);
262
263 return 0;
264 }
265
266 static void join_timer_start(struct pim_upstream *up)
267 {
268 if (PIM_DEBUG_PIM_EVENTS) {
269 zlog_debug("%s: starting %d sec timer for upstream (S,G)=%s",
270 __PRETTY_FUNCTION__,
271 qpim_t_periodic,
272 up->sg_str);
273 }
274
275 THREAD_OFF (up->t_join_timer);
276 THREAD_TIMER_ON(master, up->t_join_timer,
277 on_join_timer,
278 up, qpim_t_periodic);
279 }
280
281 void pim_upstream_join_timer_restart(struct pim_upstream *up)
282 {
283 THREAD_OFF(up->t_join_timer);
284 join_timer_start(up);
285 }
286
287 static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
288 int interval_msec)
289 {
290 if (PIM_DEBUG_PIM_EVENTS) {
291 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=%s",
292 __PRETTY_FUNCTION__,
293 interval_msec,
294 up->sg_str);
295 }
296
297 THREAD_OFF(up->t_join_timer);
298 THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
299 on_join_timer,
300 up, interval_msec);
301 }
302
303 void pim_upstream_join_suppress(struct pim_upstream *up,
304 struct in_addr rpf_addr,
305 int holdtime)
306 {
307 long t_joinsuppress_msec;
308 long join_timer_remain_msec;
309
310 t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
311 1000 * holdtime);
312
313 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
314
315 if (PIM_DEBUG_TRACE) {
316 char rpf_str[INET_ADDRSTRLEN];
317 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
318 zlog_debug("%s %s: detected Join%s to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
319 __FILE__, __PRETTY_FUNCTION__,
320 up->sg_str,
321 rpf_str,
322 join_timer_remain_msec, t_joinsuppress_msec);
323 }
324
325 if (join_timer_remain_msec < t_joinsuppress_msec) {
326 if (PIM_DEBUG_TRACE) {
327 zlog_debug("%s %s: suppressing Join(S,G)=%s for %ld msec",
328 __FILE__, __PRETTY_FUNCTION__,
329 up->sg_str, t_joinsuppress_msec);
330 }
331
332 pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
333 }
334 }
335
336 void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
337 struct pim_upstream *up,
338 struct in_addr rpf_addr)
339 {
340 long join_timer_remain_msec;
341 int t_override_msec;
342
343 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
344 t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
345
346 if (PIM_DEBUG_TRACE) {
347 char rpf_str[INET_ADDRSTRLEN];
348 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
349 zlog_debug("%s: to RPF'%s=%s: join_timer=%ld msec t_override=%d msec",
350 debug_label,
351 up->sg_str, rpf_str,
352 join_timer_remain_msec, t_override_msec);
353 }
354
355 if (join_timer_remain_msec > t_override_msec) {
356 if (PIM_DEBUG_TRACE) {
357 zlog_debug("%s: decreasing (S,G)=%s join timer to t_override=%d msec",
358 debug_label,
359 up->sg_str,
360 t_override_msec);
361 }
362
363 pim_upstream_join_timer_restart_msec(up, t_override_msec);
364 }
365 }
366
367 static void forward_on(struct pim_upstream *up)
368 {
369 struct listnode *chnode;
370 struct listnode *chnextnode;
371 struct pim_interface *pim_ifp;
372 struct pim_ifchannel *ch;
373
374 /* scan (S,G) state */
375 for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
376 pim_ifp = ch->interface->info;
377 if (!pim_ifp)
378 continue;
379
380 if (ch->upstream != up)
381 continue;
382
383 if (pim_macro_chisin_oiflist(ch))
384 pim_forward_start(ch);
385
386 } /* scan iface channel list */
387 }
388
389 static void forward_off(struct pim_upstream *up)
390 {
391 struct listnode *chnode;
392 struct listnode *chnextnode;
393 struct pim_interface *pim_ifp;
394 struct pim_ifchannel *ch;
395
396 /* scan per-interface (S,G) state */
397 for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
398 pim_ifp = ch->interface->info;
399 if (!pim_ifp)
400 continue;
401
402 if (ch->upstream != up)
403 continue;
404
405 pim_forward_stop(ch);
406
407 } /* scan iface channel list */
408 }
409
410 static int
411 pim_upstream_could_register (struct pim_upstream *up)
412 {
413 struct pim_interface *pim_ifp = up->rpf.source_nexthop.interface->info;
414
415 if (pim_ifp && PIM_I_am_DR (pim_ifp) &&
416 pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src))
417 return 1;
418
419 return 0;
420 }
421
422 void
423 pim_upstream_switch(struct pim_upstream *up,
424 enum pim_upstream_state new_state)
425 {
426 enum pim_upstream_state old_state = up->join_state;
427
428 if (PIM_DEBUG_PIM_EVENTS) {
429 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G) old: %s new: %s",
430 __PRETTY_FUNCTION__,
431 up->sg_str,
432 pim_upstream_state2str (up->join_state),
433 pim_upstream_state2str (new_state));
434 }
435
436 /*
437 * This code still needs work.
438 */
439 switch (up->join_state)
440 {
441 case PIM_UPSTREAM_PRUNE:
442 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
443 {
444 up->join_state = new_state;
445 up->state_transition = pim_time_monotonic_sec ();
446 }
447 break;
448 case PIM_UPSTREAM_JOIN_PENDING:
449 break;
450 case PIM_UPSTREAM_NOTJOINED:
451 case PIM_UPSTREAM_JOINED:
452 up->join_state = new_state;
453 if (old_state != new_state)
454 up->state_transition = pim_time_monotonic_sec();
455
456 break;
457 }
458
459 pim_upstream_update_assert_tracking_desired(up);
460
461 if (new_state == PIM_UPSTREAM_JOINED) {
462 if (old_state != PIM_UPSTREAM_JOINED)
463 {
464 int old_fhr = PIM_UPSTREAM_FLAG_TEST_FHR(up->flags);
465 forward_on(up);
466 pim_msdp_up_join_state_changed(up);
467 if (pim_upstream_could_register (up))
468 {
469 PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
470 if (!old_fhr && PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
471 {
472 pim_upstream_keep_alive_timer_start (up, qpim_keep_alive_time);
473 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
474 }
475 }
476 else
477 {
478 pim_upstream_send_join (up);
479 join_timer_start (up);
480 }
481 }
482 else
483 {
484 forward_on (up);
485 }
486 }
487 else {
488 forward_off(up);
489 if (old_state == PIM_UPSTREAM_JOINED)
490 pim_msdp_up_join_state_changed(up);
491 pim_joinprune_send(up->rpf.source_nexthop.interface,
492 up->rpf.rpf_addr.u.prefix4,
493 up,
494 0 /* prune */);
495 if (up->t_join_timer)
496 THREAD_OFF(up->t_join_timer);
497 }
498 }
499
500 static int
501 pim_upstream_compare (void *arg1, void *arg2)
502 {
503 const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
504 const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
505
506 if (ntohl(up1->sg.grp.s_addr) < ntohl(up2->sg.grp.s_addr))
507 return -1;
508
509 if (ntohl(up1->sg.grp.s_addr) > ntohl(up2->sg.grp.s_addr))
510 return 1;
511
512 if (ntohl(up1->sg.src.s_addr) < ntohl(up2->sg.src.s_addr))
513 return -1;
514
515 if (ntohl(up1->sg.src.s_addr) > ntohl(up2->sg.src.s_addr))
516 return 1;
517
518 return 0;
519 }
520
521 static struct pim_upstream *
522 pim_upstream_new (struct prefix_sg *sg,
523 struct interface *incoming,
524 int flags)
525 {
526 enum pim_rpf_result rpf_result;
527 struct pim_interface *pim_ifp;
528 struct pim_upstream *up;
529
530 up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
531 if (!up) {
532 zlog_err("%s: PIM XCALLOC(%zu) failure",
533 __PRETTY_FUNCTION__, sizeof(*up));
534 return NULL;
535 }
536
537 up->sg = *sg;
538 pim_str_sg_set (sg, up->sg_str);
539 up = hash_get (pim_upstream_hash, up, hash_alloc_intern);
540 if (!pim_rp_set_upstream_addr (&up->upstream_addr, sg->src, sg->grp))
541 {
542 if (PIM_DEBUG_TRACE)
543 zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__);
544
545 hash_release (pim_upstream_hash, up);
546 XFREE (MTYPE_PIM_UPSTREAM, up);
547 return NULL;
548 }
549
550 up->parent = pim_upstream_find_parent (up);
551 if (up->sg.src.s_addr == INADDR_ANY)
552 {
553 up->sources = list_new ();
554 up->sources->cmp = pim_upstream_compare;
555 }
556 else
557 up->sources = NULL;
558
559 pim_upstream_find_new_children (up);
560 up->flags = flags;
561 up->ref_count = 1;
562 up->t_join_timer = NULL;
563 up->t_ka_timer = NULL;
564 up->t_rs_timer = NULL;
565 up->t_msdp_reg_timer = NULL;
566 up->join_state = 0;
567 up->state_transition = pim_time_monotonic_sec();
568 up->channel_oil = NULL;
569 up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
570
571 up->rpf.source_nexthop.interface = NULL;
572 up->rpf.source_nexthop.mrib_nexthop_addr.family = AF_INET;
573 up->rpf.source_nexthop.mrib_nexthop_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
574 up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
575 up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
576 up->rpf.rpf_addr.family = AF_INET;
577 up->rpf.rpf_addr.u.prefix4.s_addr = PIM_NET_INADDR_ANY;
578
579 if (up->sg.src.s_addr != INADDR_ANY)
580 wheel_add_item (pim_upstream_sg_wheel, up);
581
582 rpf_result = pim_rpf_update(up, NULL);
583 if (rpf_result == PIM_RPF_FAILURE) {
584 if (PIM_DEBUG_TRACE)
585 zlog_debug ("%s: Attempting to create upstream(%s), Unable to RPF for source", __PRETTY_FUNCTION__,
586 up->sg_str);
587
588 if (up->parent)
589 {
590 listnode_delete (up->parent->sources, up);
591 up->parent = NULL;
592 }
593
594 if (up->sg.src.s_addr != INADDR_ANY)
595 wheel_remove_item (pim_upstream_sg_wheel, up);
596
597 pim_upstream_remove_children (up);
598 if (up->sources)
599 list_delete (up->sources);
600
601 hash_release (pim_upstream_hash, up);
602 XFREE(MTYPE_PIM_UPSTREAM, up);
603 return NULL;
604 }
605
606 pim_ifp = up->rpf.source_nexthop.interface->info;
607 if (pim_ifp)
608 up->channel_oil = pim_channel_oil_add(&up->sg, pim_ifp->mroute_vif_index);
609
610 listnode_add_sort(pim_upstream_list, up);
611
612 if (PIM_DEBUG_TRACE)
613 zlog_debug ("%s: Created Upstream %s", __PRETTY_FUNCTION__, up->sg_str);
614
615 return up;
616 }
617
618 struct pim_upstream *pim_upstream_find(struct prefix_sg *sg)
619 {
620 struct pim_upstream lookup;
621 struct pim_upstream *up = NULL;
622
623 lookup.sg = *sg;
624 up = hash_lookup (pim_upstream_hash, &lookup);
625 return up;
626 }
627
628 static void pim_upstream_ref(struct pim_upstream *up, int flags)
629 {
630 up->flags |= flags;
631 ++up->ref_count;
632 }
633
634 struct pim_upstream *pim_upstream_add(struct prefix_sg *sg,
635 struct interface *incoming,
636 int flags, const char *name)
637 {
638 struct pim_upstream *up = NULL;
639 int found = 0;
640 up = pim_upstream_find(sg);
641 if (up) {
642 pim_upstream_ref(up, flags);
643 found = 1;
644 }
645 else {
646 up = pim_upstream_new(sg, incoming, flags);
647 }
648
649 if (PIM_DEBUG_TRACE)
650 {
651 if (up)
652 zlog_debug("%s(%s): %s, found: %d: ref_count: %d",
653 __PRETTY_FUNCTION__, name,
654 up->sg_str, found,
655 up->ref_count);
656 else
657 zlog_debug("%s(%s): (%s) failure to create",
658 __PRETTY_FUNCTION__, name,
659 pim_str_sg_dump (sg));
660 }
661
662 return up;
663 }
664
665 static int
666 pim_upstream_evaluate_join_desired_interface (struct pim_upstream *up,
667 struct pim_ifchannel *ch)
668 {
669 struct pim_upstream *parent = up->parent;
670
671 if (ch->upstream == up)
672 {
673 if (!pim_macro_ch_lost_assert(ch) && pim_macro_chisin_joins_or_include(ch))
674 return 1;
675
676 if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
677 return 0;
678 }
679
680 /*
681 * joins (*,G)
682 */
683 if (parent && ch->upstream == parent)
684 {
685 if (!pim_macro_ch_lost_assert (ch) && pim_macro_chisin_joins_or_include (ch))
686 return 1;
687 }
688
689 return 0;
690 }
691
692 /*
693 Evaluate JoinDesired(S,G):
694
695 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
696 in the set:
697
698 inherited_olist(S,G) =
699 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
700
701 JoinDesired(S,G) may be affected by changes in the following:
702
703 pim_ifp->primary_address
704 pim_ifp->pim_dr_addr
705 ch->ifassert_winner_metric
706 ch->ifassert_winner
707 ch->local_ifmembership
708 ch->ifjoin_state
709 ch->upstream->rpf.source_nexthop.mrib_metric_preference
710 ch->upstream->rpf.source_nexthop.mrib_route_metric
711 ch->upstream->rpf.source_nexthop.interface
712
713 See also pim_upstream_update_join_desired() below.
714 */
715 int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
716 {
717 struct listnode *chnode;
718 struct listnode *chnextnode;
719 struct pim_interface *pim_ifp;
720 struct pim_ifchannel *ch;
721 int ret = 0;
722
723 /* scan per-interface (S,G) state */
724 for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch))
725 {
726 pim_ifp = ch->interface->info;
727 if (!pim_ifp)
728 continue;
729
730 ret += pim_upstream_evaluate_join_desired_interface (up, ch);
731 } /* scan iface channel list */
732
733 return ret; /* false */
734 }
735
736 /*
737 See also pim_upstream_evaluate_join_desired() above.
738 */
739 void pim_upstream_update_join_desired(struct pim_upstream *up)
740 {
741 int was_join_desired; /* boolean */
742 int is_join_desired; /* boolean */
743
744 was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
745
746 is_join_desired = pim_upstream_evaluate_join_desired(up);
747 if (is_join_desired)
748 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
749 else
750 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
751
752 /* switched from false to true */
753 if (is_join_desired && !was_join_desired) {
754 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
755 return;
756 }
757
758 /* switched from true to false */
759 if (!is_join_desired && was_join_desired) {
760 pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
761 return;
762 }
763 }
764
765 /*
766 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
767 Transitions from Joined State
768 RPF'(S,G) GenID changes
769
770 The upstream (S,G) state machine remains in Joined state. If the
771 Join Timer is set to expire in more than t_override seconds, reset
772 it so that it expires after t_override seconds.
773 */
774 void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
775 {
776 struct listnode *up_node;
777 struct listnode *up_nextnode;
778 struct pim_upstream *up;
779
780 /*
781 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
782 */
783 for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) {
784
785 if (PIM_DEBUG_TRACE) {
786 char neigh_str[INET_ADDRSTRLEN];
787 char rpf_addr_str[PREFIX_STRLEN];
788 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
789 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
790 zlog_debug("%s: matching neigh=%s against upstream (S,G)=%s joined=%d rpf_addr=%s",
791 __PRETTY_FUNCTION__,
792 neigh_str, up->sg_str,
793 up->join_state == PIM_UPSTREAM_JOINED,
794 rpf_addr_str);
795 }
796
797 /* consider only (S,G) upstream in Joined state */
798 if (up->join_state != PIM_UPSTREAM_JOINED)
799 continue;
800
801 /* match RPF'(S,G)=neigh_addr */
802 if (up->rpf.rpf_addr.u.prefix4.s_addr != neigh_addr.s_addr)
803 continue;
804
805 pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
806 up, neigh_addr);
807 }
808 }
809
810
811 void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
812 struct interface *old_rpf_ifp)
813 {
814 struct listnode *chnode;
815 struct listnode *chnextnode;
816 struct pim_ifchannel *ch;
817 struct pim_interface *pim_ifp;
818
819 /* search all ifchannels */
820 for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
821
822 pim_ifp = ch->interface->info;
823 if (!pim_ifp)
824 continue;
825
826 if (ch->upstream != up)
827 continue;
828
829 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
830 if (
831 /* RPF_interface(S) was NOT I */
832 (old_rpf_ifp == ch->interface)
833 &&
834 /* RPF_interface(S) stopped being I */
835 (ch->upstream->rpf.source_nexthop.interface != ch->interface)
836 ) {
837 assert_action_a5(ch);
838 }
839 } /* PIM_IFASSERT_I_AM_LOSER */
840
841 pim_ifchannel_update_assert_tracking_desired(ch);
842 }
843 }
844
845 void pim_upstream_update_could_assert(struct pim_upstream *up)
846 {
847 struct listnode *chnode;
848 struct listnode *chnextnode;
849 struct pim_interface *pim_ifp;
850 struct pim_ifchannel *ch;
851
852 /* scan per-interface (S,G) state */
853 for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
854 pim_ifp = ch->interface->info;
855 if (!pim_ifp)
856 continue;
857
858 if (ch->upstream != up)
859 continue;
860
861 pim_ifchannel_update_could_assert(ch);
862 } /* scan iface channel list */
863 }
864
865 void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
866 {
867 struct listnode *chnode;
868 struct listnode *chnextnode;
869 struct pim_interface *pim_ifp;
870 struct pim_ifchannel *ch;
871
872 /* scan per-interface (S,G) state */
873 for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
874 pim_ifp = ch->interface->info;
875 if (!pim_ifp)
876 continue;
877
878 if (ch->upstream != up)
879 continue;
880
881 pim_ifchannel_update_my_assert_metric(ch);
882
883 } /* scan iface channel list */
884 }
885
886 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
887 {
888 struct listnode *chnode;
889 struct listnode *chnextnode;
890 struct pim_interface *pim_ifp;
891 struct pim_ifchannel *ch;
892
893 /* scan per-interface (S,G) state */
894 for (ALL_LIST_ELEMENTS(pim_ifchannel_list, chnode, chnextnode, ch)) {
895 pim_ifp = ch->interface->info;
896 if (!pim_ifp)
897 continue;
898
899 if (ch->upstream != up)
900 continue;
901
902 pim_ifchannel_update_assert_tracking_desired(ch);
903
904 } /* scan iface channel list */
905 }
906
907 /* When kat is stopped CouldRegister goes to false so we need to
908 * transition the (S, G) on FHR to NI state and remove reg tunnel
909 * from the OIL */
910 static void pim_upstream_fhr_kat_expiry(struct pim_upstream *up)
911 {
912 if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags))
913 return;
914
915 if (PIM_DEBUG_TRACE)
916 zlog_debug ("kat expired on %s; clear fhr reg state", up->sg_str);
917
918 /* stop reg-stop timer */
919 THREAD_OFF(up->t_rs_timer);
920 /* remove regiface from the OIL if it is there*/
921 pim_channel_del_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
922 /* move to "not-joined" */
923 up->join_state = PIM_UPSTREAM_NOTJOINED;
924 PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags);
925 }
926
927 /* When kat is started CouldRegister can go to true. And if it does we
928 * need to transition the (S, G) on FHR to JOINED state and add reg tunnel
929 * to the OIL */
930 static void pim_upstream_fhr_kat_start(struct pim_upstream *up)
931 {
932 if (pim_upstream_could_register(up)) {
933 if (PIM_DEBUG_TRACE)
934 zlog_debug ("kat started on %s; set fhr reg state to joined", up->sg_str);
935
936 PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
937 if (up->join_state == PIM_UPSTREAM_NOTJOINED) {
938 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
939 up->join_state = PIM_UPSTREAM_JOINED;
940 }
941 }
942 }
943
944 /*
945 * On an RP, the PMBR value must be cleared when the
946 * Keepalive Timer expires
947 * KAT expiry indicates that flow is inactive. If the flow was created or
948 * maintained by activity now is the time to deref it.
949 */
950 static int
951 pim_upstream_keep_alive_timer (struct thread *t)
952 {
953 struct pim_upstream *up;
954
955 up = THREAD_ARG(t);
956 up->t_ka_timer = NULL;
957
958 if (I_am_RP (up->sg.grp))
959 {
960 pim_br_clear_pmbr (&up->sg);
961 /*
962 * We need to do more here :)
963 * But this is the start.
964 */
965 }
966
967 /* source is no longer active - pull the SA from MSDP's cache */
968 pim_msdp_sa_local_del(&up->sg);
969
970 /* if entry was created because of activity we need to deref it */
971 if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
972 {
973 pim_upstream_fhr_kat_expiry(up);
974 if (PIM_DEBUG_TRACE)
975 zlog_debug ("kat expired on %s; remove stream reference", up->sg_str);
976 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(up->flags);
977 pim_upstream_del(up, __PRETTY_FUNCTION__);
978 }
979
980 return 0;
981 }
982
983 void
984 pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
985 uint32_t time)
986 {
987 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) {
988 if (PIM_DEBUG_TRACE)
989 zlog_debug ("kat start on %s with no stream reference", up->sg_str);
990 }
991 THREAD_OFF (up->t_ka_timer);
992 THREAD_TIMER_ON (master,
993 up->t_ka_timer,
994 pim_upstream_keep_alive_timer,
995 up, time);
996
997 /* any time keepalive is started against a SG we will have to
998 * re-evaluate our active source database */
999 pim_msdp_sa_local_update(up);
1000 }
1001
1002 /* MSDP on RP needs to know if a source is registerable to this RP */
1003 static int
1004 pim_upstream_msdp_reg_timer(struct thread *t)
1005 {
1006 struct pim_upstream *up;
1007
1008 up = THREAD_ARG(t);
1009 up->t_msdp_reg_timer = NULL;
1010
1011 /* source is no longer active - pull the SA from MSDP's cache */
1012 pim_msdp_sa_local_del(&up->sg);
1013 return 1;
1014 }
1015 void
1016 pim_upstream_msdp_reg_timer_start(struct pim_upstream *up)
1017 {
1018 THREAD_OFF(up->t_msdp_reg_timer);
1019 THREAD_TIMER_ON(master, up->t_msdp_reg_timer,
1020 pim_upstream_msdp_reg_timer, up, PIM_MSDP_REG_RXED_PERIOD);
1021
1022 pim_msdp_sa_local_update(up);
1023 }
1024
1025 /*
1026 * 4.2.1 Last-Hop Switchover to the SPT
1027 *
1028 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
1029 * RP. Once traffic from sources to joined groups arrives at a last-hop
1030 * router, it has the option of switching to receive the traffic on a
1031 * shortest path tree (SPT).
1032 *
1033 * The decision for a router to switch to the SPT is controlled as
1034 * follows:
1035 *
1036 * void
1037 * CheckSwitchToSpt(S,G) {
1038 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
1039 * (+) pim_include(S,G) != NULL )
1040 * AND SwitchToSptDesired(S,G) ) {
1041 * # Note: Restarting the KAT will result in the SPT switch
1042 * set KeepaliveTimer(S,G) to Keepalive_Period
1043 * }
1044 * }
1045 *
1046 * SwitchToSptDesired(S,G) is a policy function that is implementation
1047 * defined. An "infinite threshold" policy can be implemented by making
1048 * SwitchToSptDesired(S,G) return false all the time. A "switch on
1049 * first packet" policy can be implemented by making
1050 * SwitchToSptDesired(S,G) return true once a single packet has been
1051 * received for the source and group.
1052 */
1053 int
1054 pim_upstream_switch_to_spt_desired (struct prefix_sg *sg)
1055 {
1056 if (I_am_RP (sg->grp))
1057 return 1;
1058
1059 return 0;
1060 }
1061
1062 int
1063 pim_upstream_is_sg_rpt (struct pim_upstream *up)
1064 {
1065 struct listnode *chnode;
1066 struct pim_ifchannel *ch;
1067
1068 for (ALL_LIST_ELEMENTS_RO(pim_ifchannel_list, chnode, ch))
1069 {
1070 if ((ch->upstream == up) &&
1071 (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)))
1072 return 1;
1073 }
1074
1075 return 0;
1076 }
1077 /*
1078 * After receiving a packet set SPTbit:
1079 * void
1080 * Update_SPTbit(S,G,iif) {
1081 * if ( iif == RPF_interface(S)
1082 * AND JoinDesired(S,G) == TRUE
1083 * AND ( DirectlyConnected(S) == TRUE
1084 * OR RPF_interface(S) != RPF_interface(RP(G))
1085 * OR inherited_olist(S,G,rpt) == NULL
1086 * OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1087 * ( RPF'(S,G) != NULL ) )
1088 * OR ( I_Am_Assert_Loser(S,G,iif) ) {
1089 * Set SPTbit(S,G) to TRUE
1090 * }
1091 * }
1092 */
1093 void
1094 pim_upstream_set_sptbit (struct pim_upstream *up, struct interface *incoming)
1095 {
1096 struct pim_rpf *grpf = NULL;
1097
1098 // iif == RPF_interfvace(S)
1099 if (up->rpf.source_nexthop.interface != incoming)
1100 {
1101 if (PIM_DEBUG_TRACE)
1102 zlog_debug ("%s: Incoming Interface: %s is different than RPF_interface(S) %s",
1103 __PRETTY_FUNCTION__, incoming->name, up->rpf.source_nexthop.interface->name);
1104 return;
1105 }
1106
1107 // AND JoinDesired(S,G) == TRUE
1108 // FIXME
1109
1110 // DirectlyConnected(S) == TRUE
1111 if (pim_if_connected_to_source (up->rpf.source_nexthop.interface, up->sg.src))
1112 {
1113 if (PIM_DEBUG_TRACE)
1114 zlog_debug ("%s: %s is directly connected to the source", __PRETTY_FUNCTION__,
1115 up->sg_str);
1116 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
1117 return;
1118 }
1119
1120 // OR RPF_interface(S) != RPF_interface(RP(G))
1121 grpf = RP(up->sg.grp);
1122 if (!grpf || up->rpf.source_nexthop.interface != grpf->source_nexthop.interface)
1123 {
1124 if (PIM_DEBUG_TRACE)
1125 zlog_debug ("%s: %s RPF_interface(S) != RPF_interface(RP(G))",
1126 __PRETTY_FUNCTION__, up->sg_str);
1127 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
1128 return;
1129 }
1130
1131 // OR inherited_olist(S,G,rpt) == NULL
1132 if (pim_upstream_is_sg_rpt(up) && pim_upstream_empty_inherited_olist(up))
1133 {
1134 if (PIM_DEBUG_TRACE)
1135 zlog_debug ("%s: %s OR inherited_olist(S,G,rpt) == NULL", __PRETTY_FUNCTION__,
1136 up->sg_str);
1137 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
1138 return;
1139 }
1140
1141 // OR ( ( RPF'(S,G) == RPF'(*,G) ) AND
1142 // ( RPF'(S,G) != NULL ) )
1143 if (up->parent && pim_rpf_is_same (&up->rpf, &up->parent->rpf))
1144 {
1145 if (PIM_DEBUG_TRACE)
1146 zlog_debug ("%s: %s RPF'(S,G) is the same as RPF'(*,G)", __PRETTY_FUNCTION__,
1147 up->sg_str);
1148 up->sptbit = PIM_UPSTREAM_SPTBIT_TRUE;
1149 return;
1150 }
1151
1152 return;
1153 }
1154
1155 const char *
1156 pim_upstream_state2str (enum pim_upstream_state join_state)
1157 {
1158 switch (join_state)
1159 {
1160 case PIM_UPSTREAM_NOTJOINED:
1161 return "NotJoined";
1162 break;
1163 case PIM_UPSTREAM_JOINED:
1164 return "Joined";
1165 break;
1166 case PIM_UPSTREAM_JOIN_PENDING:
1167 return "JoinPending";
1168 break;
1169 case PIM_UPSTREAM_PRUNE:
1170 return "Prune";
1171 break;
1172 }
1173 return "Unknown";
1174 }
1175
1176 static int
1177 pim_upstream_register_stop_timer (struct thread *t)
1178 {
1179 struct pim_interface *pim_ifp;
1180 struct pim_upstream *up;
1181 struct pim_rpf *rpg;
1182 struct ip ip_hdr;
1183 up = THREAD_ARG (t);
1184
1185 up->t_rs_timer = NULL;
1186
1187 if (PIM_DEBUG_TRACE)
1188 {
1189 zlog_debug ("%s: (S,G)=%s upstream register stop timer %s",
1190 __PRETTY_FUNCTION__, up->sg_str,
1191 pim_upstream_state2str(up->join_state));
1192 }
1193
1194 switch (up->join_state)
1195 {
1196 case PIM_UPSTREAM_JOIN_PENDING:
1197 up->join_state = PIM_UPSTREAM_JOINED;
1198 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_PIM);
1199 break;
1200 case PIM_UPSTREAM_JOINED:
1201 break;
1202 case PIM_UPSTREAM_PRUNE:
1203 pim_ifp = up->rpf.source_nexthop.interface->info;
1204 if (!pim_ifp)
1205 {
1206 if (PIM_DEBUG_TRACE)
1207 zlog_debug ("%s: Interface: %s is not configured for pim",
1208 __PRETTY_FUNCTION__, up->rpf.source_nexthop.interface->name);
1209 return 0;
1210 }
1211 up->join_state = PIM_UPSTREAM_JOIN_PENDING;
1212 pim_upstream_start_register_stop_timer (up, 1);
1213
1214 if (((up->channel_oil->cc.lastused/100) > PIM_KEEPALIVE_PERIOD) &&
1215 (I_am_RP (up->sg.grp)))
1216 {
1217 if (PIM_DEBUG_TRACE)
1218 zlog_debug ("%s: Stop sending the register, because I am the RP and we haven't seen a packet in a while", __PRETTY_FUNCTION__);
1219 return 0;
1220 }
1221 rpg = RP (up->sg.grp);
1222 memset (&ip_hdr, 0, sizeof (struct ip));
1223 ip_hdr.ip_p = PIM_IP_PROTO_PIM;
1224 ip_hdr.ip_hl = 5;
1225 ip_hdr.ip_v = 4;
1226 ip_hdr.ip_src = up->sg.src;
1227 ip_hdr.ip_dst = up->sg.grp;
1228 ip_hdr.ip_len = htons (20);
1229 // checksum is broken
1230 pim_register_send ((uint8_t *)&ip_hdr, sizeof (struct ip),
1231 pim_ifp->primary_address, rpg, 1, up);
1232 break;
1233 default:
1234 break;
1235 }
1236
1237 return 0;
1238 }
1239
1240 void
1241 pim_upstream_start_register_stop_timer (struct pim_upstream *up, int null_register)
1242 {
1243 uint32_t time;
1244
1245 if (up->t_rs_timer)
1246 {
1247 THREAD_TIMER_OFF (up->t_rs_timer);
1248 up->t_rs_timer = NULL;
1249 }
1250
1251 if (!null_register)
1252 {
1253 uint32_t lower = (0.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
1254 uint32_t upper = (1.5 * PIM_REGISTER_SUPPRESSION_PERIOD);
1255 time = lower + (random () % (upper - lower + 1)) - PIM_REGISTER_PROBE_PERIOD;
1256 }
1257 else
1258 time = PIM_REGISTER_PROBE_PERIOD;
1259
1260 if (PIM_DEBUG_TRACE)
1261 {
1262 zlog_debug ("%s: (S,G)=%s Starting upstream register stop timer %d",
1263 __PRETTY_FUNCTION__, up->sg_str, time);
1264 }
1265 THREAD_TIMER_ON (master, up->t_rs_timer,
1266 pim_upstream_register_stop_timer,
1267 up, time);
1268 }
1269
1270 int
1271 pim_upstream_inherited_olist_decide (struct pim_upstream *up)
1272 {
1273 struct pim_interface *pim_ifp;
1274 struct listnode *chnextnode;
1275 struct pim_ifchannel *ch;
1276 struct listnode *chnode;
1277 int output_intf = 0;
1278
1279 pim_ifp = up->rpf.source_nexthop.interface->info;
1280 if (pim_ifp && !up->channel_oil)
1281 up->channel_oil = pim_channel_oil_add (&up->sg, pim_ifp->mroute_vif_index);
1282
1283 for (ALL_LIST_ELEMENTS (pim_ifchannel_list, chnode, chnextnode, ch))
1284 {
1285 pim_ifp = ch->interface->info;
1286 if (!pim_ifp)
1287 continue;
1288
1289 if (pim_upstream_evaluate_join_desired_interface (up, ch))
1290 {
1291 pim_channel_add_oif (up->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_PIM);
1292 output_intf++;
1293 }
1294 }
1295
1296 return output_intf;
1297 }
1298
1299 /*
1300 * For a given upstream, determine the inherited_olist
1301 * and apply it.
1302 *
1303 * inherited_olist(S,G,rpt) =
1304 * ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
1305 * (+) ( pim_include(*,G) (-) pim_exclude(S,G))
1306 * (-) ( lost_assert(*,G) (+) lost_assert(S,G,rpt) )
1307 *
1308 * inherited_olist(S,G) =
1309 * inherited_olist(S,G,rpt) (+)
1310 * joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
1311 *
1312 * return 1 if there are any output interfaces
1313 * return 0 if there are not any output interfaces
1314 */
1315 int
1316 pim_upstream_inherited_olist (struct pim_upstream *up)
1317 {
1318 int output_intf = pim_upstream_inherited_olist_decide (up);
1319
1320 /*
1321 * If we have output_intf switch state to Join and work like normal
1322 * If we don't have an output_intf that means we are probably a
1323 * switch on a stick so turn on forwarding to just accept the
1324 * incoming packets so we don't bother the other stuff!
1325 */
1326 if (output_intf)
1327 pim_upstream_switch (up, PIM_UPSTREAM_JOINED);
1328 else
1329 forward_on (up);
1330
1331 return output_intf;
1332 }
1333
1334 int
1335 pim_upstream_empty_inherited_olist (struct pim_upstream *up)
1336 {
1337 return pim_channel_oil_empty (up->channel_oil);
1338 }
1339
1340 /*
1341 * When we have a new neighbor,
1342 * find upstreams that don't have their rpf_addr
1343 * set and see if the new neighbor allows
1344 * the join to be sent
1345 */
1346 void
1347 pim_upstream_find_new_rpf (void)
1348 {
1349 struct listnode *up_node;
1350 struct listnode *up_nextnode;
1351 struct pim_upstream *up;
1352
1353 /*
1354 * Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
1355 */
1356 for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up))
1357 {
1358 if (pim_rpf_addr_is_inaddr_any(&up->rpf))
1359 {
1360 if (PIM_DEBUG_TRACE)
1361 zlog_debug ("Upstream %s without a path to send join, checking",
1362 up->sg_str);
1363 pim_rpf_update (up, NULL);
1364 }
1365 }
1366 }
1367
1368 static unsigned int
1369 pim_upstream_hash_key (void *arg)
1370 {
1371 struct pim_upstream *up = (struct pim_upstream *)arg;
1372
1373 return jhash_2words (up->sg.src.s_addr, up->sg.grp.s_addr, 0);
1374 }
1375
1376 void pim_upstream_terminate (void)
1377 {
1378 if (pim_upstream_list)
1379 list_delete (pim_upstream_list);
1380 pim_upstream_list = NULL;
1381
1382 if (pim_upstream_hash)
1383 hash_free (pim_upstream_hash);
1384 pim_upstream_hash = NULL;
1385 }
1386
1387 static int
1388 pim_upstream_equal (const void *arg1, const void *arg2)
1389 {
1390 const struct pim_upstream *up1 = (const struct pim_upstream *)arg1;
1391 const struct pim_upstream *up2 = (const struct pim_upstream *)arg2;
1392
1393 if ((up1->sg.grp.s_addr == up2->sg.grp.s_addr) &&
1394 (up1->sg.src.s_addr == up2->sg.src.s_addr))
1395 return 1;
1396
1397 return 0;
1398 }
1399
1400 /* rfc4601:section-4.2:"Data Packet Forwarding Rules" defines
1401 * the cases where kat has to be restarted on rxing traffic -
1402 *
1403 * if( DirectlyConnected(S) == TRUE AND iif == RPF_interface(S) ) {
1404 * set KeepaliveTimer(S,G) to Keepalive_Period
1405 * # Note: a register state transition or UpstreamJPState(S,G)
1406 * # transition may happen as a result of restarting
1407 * # KeepaliveTimer, and must be dealt with here.
1408 * }
1409 * if( iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined AND
1410 * inherited_olist(S,G) != NULL ) {
1411 * set KeepaliveTimer(S,G) to Keepalive_Period
1412 * }
1413 */
1414 static bool pim_upstream_kat_start_ok(struct pim_upstream *up)
1415 {
1416 /* "iif == RPF_interface(S)" check has to be done by the kernel or hw
1417 * so we will skip that here */
1418 if (pim_if_connected_to_source(up->rpf.source_nexthop.interface,
1419 up->sg.src)) {
1420 return true;
1421 }
1422
1423 if ((up->join_state == PIM_UPSTREAM_JOINED) &&
1424 !pim_upstream_empty_inherited_olist(up)) {
1425 /* XXX: I have added this RP check just for 3.2 and it's a digression from
1426 * what rfc-4601 says. Till now we were only running KAT on FHR and RP and
1427 * there is some angst around making the change to run it all routers that
1428 * maintain the (S, G) state. This is tracked via CM-13601 and MUST be
1429 * removed to handle spt turn-arounds correctly in a 3-tier clos */
1430 if (I_am_RP (up->sg.grp))
1431 return true;
1432 }
1433
1434 return false;
1435 }
1436
1437 /*
1438 * Code to check and see if we've received packets on a S,G mroute
1439 * and if so to set the SPT bit appropriately
1440 */
1441 static void
1442 pim_upstream_sg_running (void *arg)
1443 {
1444 struct pim_upstream *up = (struct pim_upstream *)arg;
1445
1446 // No packet can have arrived here if this is the case
1447 if (!up->channel_oil || !up->channel_oil->installed)
1448 {
1449 if (PIM_DEBUG_TRACE)
1450 zlog_debug ("%s: %s is not installed in mroute",
1451 __PRETTY_FUNCTION__, up->sg_str);
1452 return;
1453 }
1454
1455 /*
1456 * This is a bit of a hack
1457 * We've noted that we should rescan but
1458 * we've missed the window for doing so in
1459 * pim_zebra.c for some reason. I am
1460 * only doing this at this point in time
1461 * to get us up and working for the moment
1462 */
1463 if (up->channel_oil->oil_inherited_rescan)
1464 {
1465 if (PIM_DEBUG_TRACE)
1466 zlog_debug ("%s: Handling unscanned inherited_olist for %s", __PRETTY_FUNCTION__, up->sg_str);
1467 pim_upstream_inherited_olist_decide (up);
1468 up->channel_oil->oil_inherited_rescan = 0;
1469 }
1470 pim_mroute_update_counters (up->channel_oil);
1471
1472 // Have we seen packets?
1473 if ((up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt) &&
1474 (up->channel_oil->cc.lastused/100 > 30))
1475 {
1476 if (PIM_DEBUG_TRACE)
1477 {
1478 zlog_debug ("%s: %s old packet count is equal or lastused is greater than 30, (%ld,%ld,%lld)",
1479 __PRETTY_FUNCTION__, up->sg_str,
1480 up->channel_oil->cc.oldpktcnt,
1481 up->channel_oil->cc.pktcnt,
1482 up->channel_oil->cc.lastused/100);
1483 }
1484 return;
1485 }
1486
1487 if (pim_upstream_kat_start_ok(up)) {
1488 /* Add a source reference to the stream if
1489 * one doesn't already exist */
1490 if (!PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags))
1491 {
1492 if (PIM_DEBUG_TRACE)
1493 zlog_debug ("source reference created on kat restart %s", up->sg_str);
1494
1495 pim_upstream_ref(up, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM);
1496 PIM_UPSTREAM_FLAG_SET_SRC_STREAM(up->flags);
1497 pim_upstream_fhr_kat_start(up);
1498 }
1499 pim_upstream_keep_alive_timer_start(up, qpim_keep_alive_time);
1500 }
1501
1502 if (up->sptbit != PIM_UPSTREAM_SPTBIT_TRUE)
1503 {
1504 pim_upstream_set_sptbit(up, up->rpf.source_nexthop.interface);
1505 }
1506 return;
1507 }
1508
1509 void
1510 pim_upstream_init (void)
1511 {
1512 pim_upstream_sg_wheel = wheel_init (master, 31000, 100,
1513 pim_upstream_hash_key,
1514 pim_upstream_sg_running);
1515 pim_upstream_hash = hash_create_size (8192, pim_upstream_hash_key,
1516 pim_upstream_equal);
1517
1518 pim_upstream_list = list_new ();
1519 pim_upstream_list->del = (void (*)(void *)) pim_upstream_free;
1520 pim_upstream_list->cmp = pim_upstream_compare;
1521
1522 }