]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_upstream.c
ospf6d: fix decimal area ID cli
[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
12e41d03
DL
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
31#include "pimd.h"
32#include "pim_pim.h"
33#include "pim_str.h"
34#include "pim_time.h"
35#include "pim_iface.h"
36#include "pim_join.h"
37#include "pim_zlookup.h"
38#include "pim_upstream.h"
39#include "pim_ifchannel.h"
40#include "pim_neighbor.h"
41#include "pim_rpf.h"
42#include "pim_zebra.h"
43#include "pim_oil.h"
44#include "pim_macro.h"
8f5f5e91 45#include "pim_rp.h"
f14248dd 46#include "pim_br.h"
12e41d03
DL
47
48static void join_timer_start(struct pim_upstream *up);
49static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
50
51void pim_upstream_free(struct pim_upstream *up)
52{
53 XFREE(MTYPE_PIM_UPSTREAM, up);
54}
55
56static void upstream_channel_oil_detach(struct pim_upstream *up)
57{
58 if (up->channel_oil) {
59 pim_channel_oil_del(up->channel_oil);
25a335e0 60 up->channel_oil = NULL;
12e41d03
DL
61 }
62}
63
64void pim_upstream_delete(struct pim_upstream *up)
65{
66 THREAD_OFF(up->t_join_timer);
f14248dd 67 THREAD_OFF(up->t_ka_timer);
12e41d03
DL
68
69 upstream_channel_oil_detach(up);
70
71 /*
72 notice that listnode_delete() can't be moved
73 into pim_upstream_free() because the later is
74 called by list_delete_all_node()
75 */
76 listnode_delete(qpim_upstream_list, up);
77
78 pim_upstream_free(up);
79}
80
81static void send_join(struct pim_upstream *up)
82{
83 zassert(up->join_state == PIM_UPSTREAM_JOINED);
84
85
86 if (PIM_DEBUG_PIM_TRACE) {
87 if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
88 char src_str[100];
89 char grp_str[100];
90 char rpf_str[100];
91 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
92 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
93 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
94 zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
95 __PRETTY_FUNCTION__,
96 src_str, grp_str, rpf_str);
97 /* warning only */
98 }
99 }
100
101 /* send Join(S,G) to the current upstream neighbor */
102 pim_joinprune_send(up->rpf.source_nexthop.interface,
103 up->rpf.rpf_addr,
104 up->source_addr,
105 up->group_addr,
106 1 /* join */);
107}
108
109static int on_join_timer(struct thread *t)
110{
111 struct pim_upstream *up;
112
113 zassert(t);
114 up = THREAD_ARG(t);
115 zassert(up);
116
117 send_join(up);
118
4a4c4a07 119 up->t_join_timer = NULL;
12e41d03
DL
120 join_timer_start(up);
121
122 return 0;
123}
124
125static void join_timer_start(struct pim_upstream *up)
126{
127 if (PIM_DEBUG_PIM_EVENTS) {
128 char src_str[100];
129 char grp_str[100];
130 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
131 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
132 zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
133 __PRETTY_FUNCTION__,
134 qpim_t_periodic,
135 src_str, grp_str);
136 }
137
138 zassert(!up->t_join_timer);
139
140 THREAD_TIMER_ON(master, up->t_join_timer,
141 on_join_timer,
142 up, qpim_t_periodic);
143}
144
145void pim_upstream_join_timer_restart(struct pim_upstream *up)
146{
147 THREAD_OFF(up->t_join_timer);
148 join_timer_start(up);
149}
150
151static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
152 int interval_msec)
153{
154 if (PIM_DEBUG_PIM_EVENTS) {
155 char src_str[100];
156 char grp_str[100];
157 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
158 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
159 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
160 __PRETTY_FUNCTION__,
161 interval_msec,
162 src_str, grp_str);
163 }
164
165 THREAD_OFF(up->t_join_timer);
166 THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
167 on_join_timer,
168 up, interval_msec);
169}
170
171void pim_upstream_join_suppress(struct pim_upstream *up,
172 struct in_addr rpf_addr,
173 int holdtime)
174{
175 long t_joinsuppress_msec;
176 long join_timer_remain_msec;
177
178 t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
179 1000 * holdtime);
180
181 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
182
183 if (PIM_DEBUG_PIM_TRACE) {
184 char src_str[100];
185 char grp_str[100];
186 char rpf_str[100];
187 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
188 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
189 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
190 zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
191 __FILE__, __PRETTY_FUNCTION__,
192 src_str, grp_str,
193 rpf_str,
194 join_timer_remain_msec, t_joinsuppress_msec);
195 }
196
197 if (join_timer_remain_msec < t_joinsuppress_msec) {
198 if (PIM_DEBUG_PIM_TRACE) {
199 char src_str[100];
200 char grp_str[100];
201 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
202 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
203 zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
204 __FILE__, __PRETTY_FUNCTION__,
205 src_str, grp_str, t_joinsuppress_msec);
206 }
207
208 pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
209 }
210}
211
212void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
213 struct pim_upstream *up,
214 struct in_addr rpf_addr)
215{
216 long join_timer_remain_msec;
217 int t_override_msec;
218
219 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
220 t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
221
222 if (PIM_DEBUG_PIM_TRACE) {
223 char src_str[100];
224 char grp_str[100];
225 char rpf_str[100];
226 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
227 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
228 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
229 zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
230 debug_label,
231 src_str, grp_str, rpf_str,
232 join_timer_remain_msec, t_override_msec);
233 }
234
235 if (join_timer_remain_msec > t_override_msec) {
236 if (PIM_DEBUG_PIM_TRACE) {
237 char src_str[100];
238 char grp_str[100];
239 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
240 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
241 zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
242 debug_label,
243 src_str, grp_str,
244 t_override_msec);
245 }
246
247 pim_upstream_join_timer_restart_msec(up, t_override_msec);
248 }
249}
250
251static void forward_on(struct pim_upstream *up)
252{
253 struct listnode *ifnode;
254 struct listnode *ifnextnode;
255 struct listnode *chnode;
256 struct listnode *chnextnode;
257 struct interface *ifp;
258 struct pim_interface *pim_ifp;
259 struct pim_ifchannel *ch;
260
261 /* scan all interfaces */
469351b3 262 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
263 pim_ifp = ifp->info;
264 if (!pim_ifp)
265 continue;
266
267 /* scan per-interface (S,G) state */
268 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
269
270 if (ch->upstream != up)
271 continue;
272
273 if (pim_macro_chisin_oiflist(ch))
274 pim_forward_start(ch);
275
276 } /* scan iface channel list */
277 } /* scan iflist */
278}
279
280static void forward_off(struct pim_upstream *up)
281{
282 struct listnode *ifnode;
283 struct listnode *ifnextnode;
284 struct listnode *chnode;
285 struct listnode *chnextnode;
286 struct interface *ifp;
287 struct pim_interface *pim_ifp;
288 struct pim_ifchannel *ch;
289
290 /* scan all interfaces */
469351b3 291 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
292 pim_ifp = ifp->info;
293 if (!pim_ifp)
294 continue;
295
296 /* scan per-interface (S,G) state */
297 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
298
299 if (ch->upstream != up)
300 continue;
301
302 pim_forward_stop(ch);
303
304 } /* scan iface channel list */
305 } /* scan iflist */
306}
307
308static void pim_upstream_switch(struct pim_upstream *up,
309 enum pim_upstream_state new_state)
310{
311 enum pim_upstream_state old_state = up->join_state;
312
313 zassert(old_state != new_state);
314
315 up->join_state = new_state;
316 up->state_transition = pim_time_monotonic_sec();
317
318 if (PIM_DEBUG_PIM_EVENTS) {
319 char src_str[100];
320 char grp_str[100];
321 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
322 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
323 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
324 __PRETTY_FUNCTION__,
325 ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
326 src_str, grp_str);
327 }
328
329 pim_upstream_update_assert_tracking_desired(up);
330
331 if (new_state == PIM_UPSTREAM_JOINED) {
332 forward_on(up);
333 send_join(up);
334 join_timer_start(up);
335 }
336 else {
337 forward_off(up);
338 pim_joinprune_send(up->rpf.source_nexthop.interface,
339 up->rpf.rpf_addr,
340 up->source_addr,
341 up->group_addr,
342 0 /* prune */);
343 zassert(up->t_join_timer);
344 THREAD_OFF(up->t_join_timer);
345 }
346
347}
348
8f5f5e91 349
12e41d03 350static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
651d0f71
DS
351 struct in_addr group_addr,
352 struct interface *incoming)
12e41d03
DL
353{
354 struct pim_upstream *up;
2f702571 355 enum pim_rpf_result rpf_result;
12e41d03
DL
356
357 up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
358 if (!up) {
359 zlog_err("%s: PIM XMALLOC(%zu) failure",
360 __PRETTY_FUNCTION__, sizeof(*up));
8f5f5e91 361 return NULL;
12e41d03
DL
362 }
363
364 up->source_addr = source_addr;
8f5f5e91
DS
365 if (!pim_rp_set_upstream_addr (&up->upstream_addr, source_addr))
366 {
367 if (PIM_DEBUG_PIM_TRACE)
368 zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__);
369
370 XFREE (MTYPE_PIM_UPSTREAM, up);
371 return NULL;
372 }
373
12e41d03
DL
374 up->group_addr = group_addr;
375 up->flags = 0;
376 up->ref_count = 1;
4a4c4a07
DS
377 up->t_join_timer = NULL;
378 up->t_ka_timer = NULL;
12e41d03
DL
379 up->join_state = 0;
380 up->state_transition = pim_time_monotonic_sec();
4a4c4a07 381 up->channel_oil = NULL;
f9e0ab5b 382 up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
12e41d03
DL
383
384 up->rpf.source_nexthop.interface = 0;
385 up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
386 up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
387 up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
388 up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY;
389
651d0f71 390 rpf_result = pim_rpf_update(up, 0, incoming);
2f702571
DS
391 if (rpf_result == PIM_RPF_FAILURE) {
392 XFREE(MTYPE_PIM_UPSTREAM, up);
393 return NULL;
394 }
12e41d03
DL
395
396 listnode_add(qpim_upstream_list, up);
397
398 return up;
399}
400
401struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
402 struct in_addr group_addr)
403{
404 struct listnode *up_node;
405 struct pim_upstream *up;
406
407 for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
d8cd119b
DS
408 if (group_addr.s_addr == up->group_addr.s_addr) {
409 if ((up->source_addr.s_addr == INADDR_ANY) ||
410 (source_addr.s_addr == up->source_addr.s_addr)) {
411 return up;
412 }
12e41d03
DL
413 }
414 }
415
416 return 0;
417}
418
419struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
651d0f71
DS
420 struct in_addr group_addr,
421 struct interface *incoming)
12e41d03
DL
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 {
651d0f71 430 up = pim_upstream_new(source_addr, group_addr, incoming);
12e41d03
DL
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
25a335e0
DS
716 if (I_am_RP (up->group_addr))
717 {
718 pim_br_clear_pmbr (up->source_addr, up->group_addr);
719 /*
720 * We need to do more here :)
721 * But this is the start.
722 */
723 }
724 else
725 {
726 pim_mroute_update_counters (up->channel_oil);
f14248dd 727
25a335e0
DS
728 if (up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt)
729 {
730 pim_mroute_del (up->channel_oil);
731 pim_upstream_delete (up);
732 }
733 else
734 {
735 up->t_ka_timer = NULL;
736 pim_upstream_keep_alive_timer_start (up, PIM_KEEPALIVE_PERIOD);
737 }
738 }
cb40b272 739 return 1;
f14248dd
DS
740}
741
f14248dd
DS
742void
743pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
744 uint32_t time)
745{
746 THREAD_TIMER_ON (master,
747 up->t_ka_timer,
748 pim_upstream_keep_alive_timer,
749 up, time);
750}
cb40b272
DS
751
752/*
753 * 4.2.1 Last-Hop Switchover to the SPT
754 *
755 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
756 * RP. Once traffic from sources to joined groups arrives at a last-hop
757 * router, it has the option of switching to receive the traffic on a
758 * shortest path tree (SPT).
759 *
760 * The decision for a router to switch to the SPT is controlled as
761 * follows:
762 *
763 * void
764 * CheckSwitchToSpt(S,G) {
765 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
766 * (+) pim_include(S,G) != NULL )
767 * AND SwitchToSptDesired(S,G) ) {
768 * # Note: Restarting the KAT will result in the SPT switch
769 * set KeepaliveTimer(S,G) to Keepalive_Period
770 * }
771 * }
772 *
773 * SwitchToSptDesired(S,G) is a policy function that is implementation
774 * defined. An "infinite threshold" policy can be implemented by making
775 * SwitchToSptDesired(S,G) return false all the time. A "switch on
776 * first packet" policy can be implemented by making
777 * SwitchToSptDesired(S,G) return true once a single packet has been
778 * received for the source and group.
779 */
780int
781pim_upstream_switch_to_spt_desired (struct in_addr source, struct in_addr group)
782{
783 return 0;
784}