]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_upstream.c
ospfd: Don't leave stale RouterLSA's when changing areaID
[mirror_frr.git] / pimd / pim_upstream.c
CommitLineData
12e41d03
DL
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 $QuaggaId: $Format:%an, %ai, %h$ $
21*/
22
23#include <zebra.h>
24
25#include "zebra/rib.h"
26
27#include "log.h"
28#include "zclient.h"
29#include "memory.h"
30#include "thread.h"
31#include "linklist.h"
32
33#include "pimd.h"
34#include "pim_pim.h"
35#include "pim_str.h"
36#include "pim_time.h"
37#include "pim_iface.h"
38#include "pim_join.h"
39#include "pim_zlookup.h"
40#include "pim_upstream.h"
41#include "pim_ifchannel.h"
42#include "pim_neighbor.h"
43#include "pim_rpf.h"
44#include "pim_zebra.h"
45#include "pim_oil.h"
46#include "pim_macro.h"
8f5f5e91 47#include "pim_rp.h"
f14248dd 48#include "pim_br.h"
12e41d03
DL
49
50static void join_timer_start(struct pim_upstream *up);
51static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
52
53void pim_upstream_free(struct pim_upstream *up)
54{
55 XFREE(MTYPE_PIM_UPSTREAM, up);
56}
57
58static void upstream_channel_oil_detach(struct pim_upstream *up)
59{
60 if (up->channel_oil) {
61 pim_channel_oil_del(up->channel_oil);
62 up->channel_oil = 0;
63 }
64}
65
66void pim_upstream_delete(struct pim_upstream *up)
67{
68 THREAD_OFF(up->t_join_timer);
f14248dd 69 THREAD_OFF(up->t_ka_timer);
12e41d03
DL
70
71 upstream_channel_oil_detach(up);
72
73 /*
74 notice that listnode_delete() can't be moved
75 into pim_upstream_free() because the later is
76 called by list_delete_all_node()
77 */
78 listnode_delete(qpim_upstream_list, up);
79
80 pim_upstream_free(up);
81}
82
83static void send_join(struct pim_upstream *up)
84{
85 zassert(up->join_state == PIM_UPSTREAM_JOINED);
86
87
88 if (PIM_DEBUG_PIM_TRACE) {
89 if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
90 char src_str[100];
91 char grp_str[100];
92 char rpf_str[100];
93 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
94 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
95 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
96 zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
97 __PRETTY_FUNCTION__,
98 src_str, grp_str, rpf_str);
99 /* warning only */
100 }
101 }
102
103 /* send Join(S,G) to the current upstream neighbor */
104 pim_joinprune_send(up->rpf.source_nexthop.interface,
105 up->rpf.rpf_addr,
106 up->source_addr,
107 up->group_addr,
108 1 /* join */);
109}
110
111static int on_join_timer(struct thread *t)
112{
113 struct pim_upstream *up;
114
115 zassert(t);
116 up = THREAD_ARG(t);
117 zassert(up);
118
119 send_join(up);
120
4a4c4a07 121 up->t_join_timer = NULL;
12e41d03
DL
122 join_timer_start(up);
123
124 return 0;
125}
126
127static void join_timer_start(struct pim_upstream *up)
128{
129 if (PIM_DEBUG_PIM_EVENTS) {
130 char src_str[100];
131 char grp_str[100];
132 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
133 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
134 zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
135 __PRETTY_FUNCTION__,
136 qpim_t_periodic,
137 src_str, grp_str);
138 }
139
140 zassert(!up->t_join_timer);
141
142 THREAD_TIMER_ON(master, up->t_join_timer,
143 on_join_timer,
144 up, qpim_t_periodic);
145}
146
147void pim_upstream_join_timer_restart(struct pim_upstream *up)
148{
149 THREAD_OFF(up->t_join_timer);
150 join_timer_start(up);
151}
152
153static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
154 int interval_msec)
155{
156 if (PIM_DEBUG_PIM_EVENTS) {
157 char src_str[100];
158 char grp_str[100];
159 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
160 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
161 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
162 __PRETTY_FUNCTION__,
163 interval_msec,
164 src_str, grp_str);
165 }
166
167 THREAD_OFF(up->t_join_timer);
168 THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
169 on_join_timer,
170 up, interval_msec);
171}
172
173void pim_upstream_join_suppress(struct pim_upstream *up,
174 struct in_addr rpf_addr,
175 int holdtime)
176{
177 long t_joinsuppress_msec;
178 long join_timer_remain_msec;
179
180 t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
181 1000 * holdtime);
182
183 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
184
185 if (PIM_DEBUG_PIM_TRACE) {
186 char src_str[100];
187 char grp_str[100];
188 char rpf_str[100];
189 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
190 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
191 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
192 zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
193 __FILE__, __PRETTY_FUNCTION__,
194 src_str, grp_str,
195 rpf_str,
196 join_timer_remain_msec, t_joinsuppress_msec);
197 }
198
199 if (join_timer_remain_msec < t_joinsuppress_msec) {
200 if (PIM_DEBUG_PIM_TRACE) {
201 char src_str[100];
202 char grp_str[100];
203 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
204 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
205 zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
206 __FILE__, __PRETTY_FUNCTION__,
207 src_str, grp_str, t_joinsuppress_msec);
208 }
209
210 pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
211 }
212}
213
214void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
215 struct pim_upstream *up,
216 struct in_addr rpf_addr)
217{
218 long join_timer_remain_msec;
219 int t_override_msec;
220
221 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
222 t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
223
224 if (PIM_DEBUG_PIM_TRACE) {
225 char src_str[100];
226 char grp_str[100];
227 char rpf_str[100];
228 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
229 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
230 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
231 zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
232 debug_label,
233 src_str, grp_str, rpf_str,
234 join_timer_remain_msec, t_override_msec);
235 }
236
237 if (join_timer_remain_msec > t_override_msec) {
238 if (PIM_DEBUG_PIM_TRACE) {
239 char src_str[100];
240 char grp_str[100];
241 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
242 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
243 zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
244 debug_label,
245 src_str, grp_str,
246 t_override_msec);
247 }
248
249 pim_upstream_join_timer_restart_msec(up, t_override_msec);
250 }
251}
252
253static void forward_on(struct pim_upstream *up)
254{
255 struct listnode *ifnode;
256 struct listnode *ifnextnode;
257 struct listnode *chnode;
258 struct listnode *chnextnode;
259 struct interface *ifp;
260 struct pim_interface *pim_ifp;
261 struct pim_ifchannel *ch;
262
263 /* scan all interfaces */
469351b3 264 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
265 pim_ifp = ifp->info;
266 if (!pim_ifp)
267 continue;
268
269 /* scan per-interface (S,G) state */
270 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
271
272 if (ch->upstream != up)
273 continue;
274
275 if (pim_macro_chisin_oiflist(ch))
276 pim_forward_start(ch);
277
278 } /* scan iface channel list */
279 } /* scan iflist */
280}
281
282static void forward_off(struct pim_upstream *up)
283{
284 struct listnode *ifnode;
285 struct listnode *ifnextnode;
286 struct listnode *chnode;
287 struct listnode *chnextnode;
288 struct interface *ifp;
289 struct pim_interface *pim_ifp;
290 struct pim_ifchannel *ch;
291
292 /* scan all interfaces */
469351b3 293 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
294 pim_ifp = ifp->info;
295 if (!pim_ifp)
296 continue;
297
298 /* scan per-interface (S,G) state */
299 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
300
301 if (ch->upstream != up)
302 continue;
303
304 pim_forward_stop(ch);
305
306 } /* scan iface channel list */
307 } /* scan iflist */
308}
309
310static void pim_upstream_switch(struct pim_upstream *up,
311 enum pim_upstream_state new_state)
312{
313 enum pim_upstream_state old_state = up->join_state;
314
315 zassert(old_state != new_state);
316
317 up->join_state = new_state;
318 up->state_transition = pim_time_monotonic_sec();
319
320 if (PIM_DEBUG_PIM_EVENTS) {
321 char src_str[100];
322 char grp_str[100];
323 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
324 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
325 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
326 __PRETTY_FUNCTION__,
327 ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
328 src_str, grp_str);
329 }
330
331 pim_upstream_update_assert_tracking_desired(up);
332
333 if (new_state == PIM_UPSTREAM_JOINED) {
334 forward_on(up);
335 send_join(up);
336 join_timer_start(up);
337 }
338 else {
339 forward_off(up);
340 pim_joinprune_send(up->rpf.source_nexthop.interface,
341 up->rpf.rpf_addr,
342 up->source_addr,
343 up->group_addr,
344 0 /* prune */);
345 zassert(up->t_join_timer);
346 THREAD_OFF(up->t_join_timer);
347 }
348
349}
350
8f5f5e91 351
12e41d03
DL
352static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
353 struct in_addr group_addr)
354{
355 struct pim_upstream *up;
2f702571 356 enum pim_rpf_result rpf_result;
12e41d03
DL
357
358 up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
359 if (!up) {
360 zlog_err("%s: PIM XMALLOC(%zu) failure",
361 __PRETTY_FUNCTION__, sizeof(*up));
8f5f5e91 362 return NULL;
12e41d03
DL
363 }
364
365 up->source_addr = source_addr;
8f5f5e91
DS
366 if (!pim_rp_set_upstream_addr (&up->upstream_addr, source_addr))
367 {
368 if (PIM_DEBUG_PIM_TRACE)
369 zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__);
370
371 XFREE (MTYPE_PIM_UPSTREAM, up);
372 return NULL;
373 }
374
12e41d03
DL
375 up->group_addr = group_addr;
376 up->flags = 0;
377 up->ref_count = 1;
4a4c4a07
DS
378 up->t_join_timer = NULL;
379 up->t_ka_timer = NULL;
12e41d03
DL
380 up->join_state = 0;
381 up->state_transition = pim_time_monotonic_sec();
4a4c4a07 382 up->channel_oil = NULL;
f9e0ab5b 383 up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
12e41d03
DL
384
385 up->rpf.source_nexthop.interface = 0;
386 up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
387 up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
388 up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
389 up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY;
390
2f702571
DS
391 rpf_result = pim_rpf_update(up, 0);
392 if (rpf_result == PIM_RPF_FAILURE) {
393 XFREE(MTYPE_PIM_UPSTREAM, up);
394 return NULL;
395 }
12e41d03
DL
396
397 listnode_add(qpim_upstream_list, up);
398
399 return up;
400}
401
402struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
403 struct in_addr group_addr)
404{
405 struct listnode *up_node;
406 struct pim_upstream *up;
407
408 for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
d8cd119b
DS
409 if (group_addr.s_addr == up->group_addr.s_addr) {
410 if ((up->source_addr.s_addr == INADDR_ANY) ||
411 (source_addr.s_addr == up->source_addr.s_addr)) {
412 return up;
413 }
12e41d03
DL
414 }
415 }
416
417 return 0;
418}
419
420struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
421 struct in_addr group_addr)
422{
423 struct pim_upstream *up;
424
425 up = pim_upstream_find(source_addr, group_addr);
426 if (up) {
427 ++up->ref_count;
428 }
429 else {
430 up = pim_upstream_new(source_addr, group_addr);
431 }
432
433 return up;
434}
435
436void pim_upstream_del(struct pim_upstream *up)
437{
438 --up->ref_count;
439
440 if (up->ref_count < 1) {
441 pim_upstream_delete(up);
442 }
443}
444
445/*
446 Evaluate JoinDesired(S,G):
447
448 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
449 in the set:
450
451 inherited_olist(S,G) =
452 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
453
454 JoinDesired(S,G) may be affected by changes in the following:
455
456 pim_ifp->primary_address
457 pim_ifp->pim_dr_addr
458 ch->ifassert_winner_metric
459 ch->ifassert_winner
460 ch->local_ifmembership
461 ch->ifjoin_state
462 ch->upstream->rpf.source_nexthop.mrib_metric_preference
463 ch->upstream->rpf.source_nexthop.mrib_route_metric
464 ch->upstream->rpf.source_nexthop.interface
465
466 See also pim_upstream_update_join_desired() below.
467 */
468int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
469{
470 struct listnode *ifnode;
471 struct listnode *ifnextnode;
472 struct listnode *chnode;
473 struct listnode *chnextnode;
474 struct interface *ifp;
475 struct pim_interface *pim_ifp;
476 struct pim_ifchannel *ch;
477
478 /* scan all interfaces */
469351b3 479 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
480 pim_ifp = ifp->info;
481 if (!pim_ifp)
482 continue;
483
484 /* scan per-interface (S,G) state */
485 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
486 if (ch->upstream != up)
487 continue;
488
489 if (pim_macro_ch_lost_assert(ch))
490 continue; /* keep searching */
491
492 if (pim_macro_chisin_joins_or_include(ch))
493 return 1; /* true */
494 } /* scan iface channel list */
495 } /* scan iflist */
496
497 return 0; /* false */
498}
499
500/*
501 See also pim_upstream_evaluate_join_desired() above.
502*/
503void pim_upstream_update_join_desired(struct pim_upstream *up)
504{
505 int was_join_desired; /* boolean */
506 int is_join_desired; /* boolean */
507
508 was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
509
510 is_join_desired = pim_upstream_evaluate_join_desired(up);
511 if (is_join_desired)
512 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
513 else
514 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
515
516 /* switched from false to true */
517 if (is_join_desired && !was_join_desired) {
518 zassert(up->join_state == PIM_UPSTREAM_NOTJOINED);
519 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
520 return;
521 }
522
523 /* switched from true to false */
524 if (!is_join_desired && was_join_desired) {
525 zassert(up->join_state == PIM_UPSTREAM_JOINED);
526 pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
527 return;
528 }
529}
530
531/*
532 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
533 Transitions from Joined State
534 RPF'(S,G) GenID changes
535
536 The upstream (S,G) state machine remains in Joined state. If the
537 Join Timer is set to expire in more than t_override seconds, reset
538 it so that it expires after t_override seconds.
539*/
540void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
541{
542 struct listnode *up_node;
543 struct listnode *up_nextnode;
544 struct pim_upstream *up;
545
546 /*
547 Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
548 */
549 for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
550
551 if (PIM_DEBUG_PIM_TRACE) {
552 char neigh_str[100];
553 char src_str[100];
554 char grp_str[100];
555 char rpf_addr_str[100];
556 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
557 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
558 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
559 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
560 zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
561 __PRETTY_FUNCTION__,
562 neigh_str, src_str, grp_str,
563 up->join_state == PIM_UPSTREAM_JOINED,
564 rpf_addr_str);
565 }
566
567 /* consider only (S,G) upstream in Joined state */
568 if (up->join_state != PIM_UPSTREAM_JOINED)
569 continue;
570
571 /* match RPF'(S,G)=neigh_addr */
572 if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr)
573 continue;
574
575 pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
576 up, neigh_addr);
577 }
578}
579
580
581void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
582 struct interface *old_rpf_ifp)
583{
584 struct listnode *ifnode;
585 struct listnode *ifnextnode;
586 struct interface *ifp;
587
588 /* scan all interfaces */
469351b3 589 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
590 struct listnode *chnode;
591 struct listnode *chnextnode;
592 struct pim_ifchannel *ch;
593 struct pim_interface *pim_ifp;
594
595 pim_ifp = ifp->info;
596 if (!pim_ifp)
597 continue;
598
599 /* search all ifchannels */
600 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
601 if (ch->upstream != up)
602 continue;
603
604 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
605 if (
606 /* RPF_interface(S) was NOT I */
607 (old_rpf_ifp == ch->interface)
608 &&
609 /* RPF_interface(S) stopped being I */
610 (ch->upstream->rpf.source_nexthop.interface != ch->interface)
611 ) {
612 assert_action_a5(ch);
613 }
614 } /* PIM_IFASSERT_I_AM_LOSER */
615
616 pim_ifchannel_update_assert_tracking_desired(ch);
617 }
618 }
619}
620
621void pim_upstream_update_could_assert(struct pim_upstream *up)
622{
623 struct listnode *ifnode;
624 struct listnode *ifnextnode;
625 struct listnode *chnode;
626 struct listnode *chnextnode;
627 struct interface *ifp;
628 struct pim_interface *pim_ifp;
629 struct pim_ifchannel *ch;
630
631 /* scan all interfaces */
469351b3 632 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
633 pim_ifp = ifp->info;
634 if (!pim_ifp)
635 continue;
636
637 /* scan per-interface (S,G) state */
638 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
639
640 if (ch->upstream != up)
641 continue;
642
643 pim_ifchannel_update_could_assert(ch);
644
645 } /* scan iface channel list */
646 } /* scan iflist */
647}
648
649void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
650{
651 struct listnode *ifnode;
652 struct listnode *ifnextnode;
653 struct listnode *chnode;
654 struct listnode *chnextnode;
655 struct interface *ifp;
656 struct pim_interface *pim_ifp;
657 struct pim_ifchannel *ch;
658
659 /* scan all interfaces */
469351b3 660 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
661 pim_ifp = ifp->info;
662 if (!pim_ifp)
663 continue;
664
665 /* scan per-interface (S,G) state */
666 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
667
668 if (ch->upstream != up)
669 continue;
670
671 pim_ifchannel_update_my_assert_metric(ch);
672
673 } /* scan iface channel list */
674 } /* scan iflist */
675}
676
677static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
678{
679 struct listnode *ifnode;
680 struct listnode *ifnextnode;
681 struct listnode *chnode;
682 struct listnode *chnextnode;
683 struct interface *ifp;
684 struct pim_interface *pim_ifp;
685 struct pim_ifchannel *ch;
686
687 /* scan all interfaces */
469351b3 688 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
689 pim_ifp = ifp->info;
690 if (!pim_ifp)
691 continue;
692
693 /* scan per-interface (S,G) state */
694 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
695
696 if (ch->upstream != up)
697 continue;
698
699 pim_ifchannel_update_assert_tracking_desired(ch);
700
701 } /* scan iface channel list */
702 } /* scan iflist */
703}
f14248dd
DS
704
705/*
706 * On an RP, the PMBR value must be cleared when the
707 * Keepalive Timer expires
708 */
709static int
710pim_upstream_keep_alive_timer (struct thread *t)
711{
712 struct pim_upstream *up;
713
714 up = THREAD_ARG(t);
715
716 pim_br_clear_pmbr (up->source_addr, up->group_addr);
717 /*
718 * We need to do more here :)
719 * But this is the start.
720 */
721
cb40b272 722 return 1;
f14248dd
DS
723}
724
f14248dd
DS
725void
726pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
727 uint32_t time)
728{
729 THREAD_TIMER_ON (master,
730 up->t_ka_timer,
731 pim_upstream_keep_alive_timer,
732 up, time);
733}
cb40b272
DS
734
735/*
736 * 4.2.1 Last-Hop Switchover to the SPT
737 *
738 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
739 * RP. Once traffic from sources to joined groups arrives at a last-hop
740 * router, it has the option of switching to receive the traffic on a
741 * shortest path tree (SPT).
742 *
743 * The decision for a router to switch to the SPT is controlled as
744 * follows:
745 *
746 * void
747 * CheckSwitchToSpt(S,G) {
748 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
749 * (+) pim_include(S,G) != NULL )
750 * AND SwitchToSptDesired(S,G) ) {
751 * # Note: Restarting the KAT will result in the SPT switch
752 * set KeepaliveTimer(S,G) to Keepalive_Period
753 * }
754 * }
755 *
756 * SwitchToSptDesired(S,G) is a policy function that is implementation
757 * defined. An "infinite threshold" policy can be implemented by making
758 * SwitchToSptDesired(S,G) return false all the time. A "switch on
759 * first packet" policy can be implemented by making
760 * SwitchToSptDesired(S,G) return true once a single packet has been
761 * received for the source and group.
762 */
763int
764pim_upstream_switch_to_spt_desired (struct in_addr source, struct in_addr group)
765{
766 return 0;
767}