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