]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_upstream.c
pimd: Add null_register bit to pim_register_send
[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);
25a335e0 62 up->channel_oil = NULL;
12e41d03
DL
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 352static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
651d0f71
DS
353 struct in_addr group_addr,
354 struct interface *incoming)
12e41d03
DL
355{
356 struct pim_upstream *up;
2f702571 357 enum pim_rpf_result rpf_result;
12e41d03
DL
358
359 up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
360 if (!up) {
361 zlog_err("%s: PIM XMALLOC(%zu) failure",
362 __PRETTY_FUNCTION__, sizeof(*up));
8f5f5e91 363 return NULL;
12e41d03
DL
364 }
365
366 up->source_addr = source_addr;
8f5f5e91
DS
367 if (!pim_rp_set_upstream_addr (&up->upstream_addr, source_addr))
368 {
369 if (PIM_DEBUG_PIM_TRACE)
370 zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__);
371
372 XFREE (MTYPE_PIM_UPSTREAM, up);
373 return NULL;
374 }
375
12e41d03
DL
376 up->group_addr = group_addr;
377 up->flags = 0;
378 up->ref_count = 1;
4a4c4a07
DS
379 up->t_join_timer = NULL;
380 up->t_ka_timer = NULL;
12e41d03
DL
381 up->join_state = 0;
382 up->state_transition = pim_time_monotonic_sec();
4a4c4a07 383 up->channel_oil = NULL;
f9e0ab5b 384 up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
12e41d03
DL
385
386 up->rpf.source_nexthop.interface = 0;
387 up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
388 up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
389 up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
390 up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY;
391
651d0f71 392 rpf_result = pim_rpf_update(up, 0, incoming);
2f702571
DS
393 if (rpf_result == PIM_RPF_FAILURE) {
394 XFREE(MTYPE_PIM_UPSTREAM, up);
395 return NULL;
396 }
12e41d03
DL
397
398 listnode_add(qpim_upstream_list, up);
399
400 return up;
401}
402
403struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
404 struct in_addr group_addr)
405{
406 struct listnode *up_node;
407 struct pim_upstream *up;
408
409 for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
d8cd119b
DS
410 if (group_addr.s_addr == up->group_addr.s_addr) {
411 if ((up->source_addr.s_addr == INADDR_ANY) ||
412 (source_addr.s_addr == up->source_addr.s_addr)) {
413 return up;
414 }
12e41d03
DL
415 }
416 }
417
d7259eac 418 return NULL;
12e41d03
DL
419}
420
421struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
651d0f71
DS
422 struct in_addr group_addr,
423 struct interface *incoming)
12e41d03
DL
424{
425 struct pim_upstream *up;
426
427 up = pim_upstream_find(source_addr, group_addr);
428 if (up) {
429 ++up->ref_count;
430 }
431 else {
651d0f71 432 up = pim_upstream_new(source_addr, group_addr, incoming);
12e41d03
DL
433 }
434
435 return up;
436}
437
438void pim_upstream_del(struct pim_upstream *up)
439{
440 --up->ref_count;
441
442 if (up->ref_count < 1) {
443 pim_upstream_delete(up);
444 }
445}
446
447/*
448 Evaluate JoinDesired(S,G):
449
450 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
451 in the set:
452
453 inherited_olist(S,G) =
454 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
455
456 JoinDesired(S,G) may be affected by changes in the following:
457
458 pim_ifp->primary_address
459 pim_ifp->pim_dr_addr
460 ch->ifassert_winner_metric
461 ch->ifassert_winner
462 ch->local_ifmembership
463 ch->ifjoin_state
464 ch->upstream->rpf.source_nexthop.mrib_metric_preference
465 ch->upstream->rpf.source_nexthop.mrib_route_metric
466 ch->upstream->rpf.source_nexthop.interface
467
468 See also pim_upstream_update_join_desired() below.
469 */
470int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
471{
472 struct listnode *ifnode;
473 struct listnode *ifnextnode;
474 struct listnode *chnode;
475 struct listnode *chnextnode;
476 struct interface *ifp;
477 struct pim_interface *pim_ifp;
478 struct pim_ifchannel *ch;
479
480 /* scan all interfaces */
469351b3 481 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
482 pim_ifp = ifp->info;
483 if (!pim_ifp)
484 continue;
485
486 /* scan per-interface (S,G) state */
487 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
488 if (ch->upstream != up)
489 continue;
490
491 if (pim_macro_ch_lost_assert(ch))
492 continue; /* keep searching */
493
494 if (pim_macro_chisin_joins_or_include(ch))
495 return 1; /* true */
496 } /* scan iface channel list */
497 } /* scan iflist */
498
499 return 0; /* false */
500}
501
502/*
503 See also pim_upstream_evaluate_join_desired() above.
504*/
505void pim_upstream_update_join_desired(struct pim_upstream *up)
506{
507 int was_join_desired; /* boolean */
508 int is_join_desired; /* boolean */
509
510 was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
511
512 is_join_desired = pim_upstream_evaluate_join_desired(up);
513 if (is_join_desired)
514 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
515 else
516 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
517
518 /* switched from false to true */
519 if (is_join_desired && !was_join_desired) {
520 zassert(up->join_state == PIM_UPSTREAM_NOTJOINED);
521 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
522 return;
523 }
524
525 /* switched from true to false */
526 if (!is_join_desired && was_join_desired) {
527 zassert(up->join_state == PIM_UPSTREAM_JOINED);
528 pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
529 return;
530 }
531}
532
533/*
534 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
535 Transitions from Joined State
536 RPF'(S,G) GenID changes
537
538 The upstream (S,G) state machine remains in Joined state. If the
539 Join Timer is set to expire in more than t_override seconds, reset
540 it so that it expires after t_override seconds.
541*/
542void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
543{
544 struct listnode *up_node;
545 struct listnode *up_nextnode;
546 struct pim_upstream *up;
547
548 /*
549 Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
550 */
551 for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
552
553 if (PIM_DEBUG_PIM_TRACE) {
554 char neigh_str[100];
555 char src_str[100];
556 char grp_str[100];
557 char rpf_addr_str[100];
558 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
559 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
560 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
561 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
562 zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
563 __PRETTY_FUNCTION__,
564 neigh_str, src_str, grp_str,
565 up->join_state == PIM_UPSTREAM_JOINED,
566 rpf_addr_str);
567 }
568
569 /* consider only (S,G) upstream in Joined state */
570 if (up->join_state != PIM_UPSTREAM_JOINED)
571 continue;
572
573 /* match RPF'(S,G)=neigh_addr */
574 if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr)
575 continue;
576
577 pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
578 up, neigh_addr);
579 }
580}
581
582
583void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
584 struct interface *old_rpf_ifp)
585{
586 struct listnode *ifnode;
587 struct listnode *ifnextnode;
588 struct interface *ifp;
589
590 /* scan all interfaces */
469351b3 591 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
592 struct listnode *chnode;
593 struct listnode *chnextnode;
594 struct pim_ifchannel *ch;
595 struct pim_interface *pim_ifp;
596
597 pim_ifp = ifp->info;
598 if (!pim_ifp)
599 continue;
600
601 /* search all ifchannels */
602 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
603 if (ch->upstream != up)
604 continue;
605
606 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
607 if (
608 /* RPF_interface(S) was NOT I */
609 (old_rpf_ifp == ch->interface)
610 &&
611 /* RPF_interface(S) stopped being I */
612 (ch->upstream->rpf.source_nexthop.interface != ch->interface)
613 ) {
614 assert_action_a5(ch);
615 }
616 } /* PIM_IFASSERT_I_AM_LOSER */
617
618 pim_ifchannel_update_assert_tracking_desired(ch);
619 }
620 }
621}
622
623void pim_upstream_update_could_assert(struct pim_upstream *up)
624{
625 struct listnode *ifnode;
626 struct listnode *ifnextnode;
627 struct listnode *chnode;
628 struct listnode *chnextnode;
629 struct interface *ifp;
630 struct pim_interface *pim_ifp;
631 struct pim_ifchannel *ch;
632
633 /* scan all interfaces */
469351b3 634 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
635 pim_ifp = ifp->info;
636 if (!pim_ifp)
637 continue;
638
639 /* scan per-interface (S,G) state */
640 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
641
642 if (ch->upstream != up)
643 continue;
644
645 pim_ifchannel_update_could_assert(ch);
646
647 } /* scan iface channel list */
648 } /* scan iflist */
649}
650
651void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
652{
653 struct listnode *ifnode;
654 struct listnode *ifnextnode;
655 struct listnode *chnode;
656 struct listnode *chnextnode;
657 struct interface *ifp;
658 struct pim_interface *pim_ifp;
659 struct pim_ifchannel *ch;
660
661 /* scan all interfaces */
469351b3 662 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
663 pim_ifp = ifp->info;
664 if (!pim_ifp)
665 continue;
666
667 /* scan per-interface (S,G) state */
668 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
669
670 if (ch->upstream != up)
671 continue;
672
673 pim_ifchannel_update_my_assert_metric(ch);
674
675 } /* scan iface channel list */
676 } /* scan iflist */
677}
678
679static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
680{
681 struct listnode *ifnode;
682 struct listnode *ifnextnode;
683 struct listnode *chnode;
684 struct listnode *chnextnode;
685 struct interface *ifp;
686 struct pim_interface *pim_ifp;
687 struct pim_ifchannel *ch;
688
689 /* scan all interfaces */
469351b3 690 for (ALL_LIST_ELEMENTS (vrf_iflist (VRF_DEFAULT), ifnode, ifnextnode, ifp)) {
12e41d03
DL
691 pim_ifp = ifp->info;
692 if (!pim_ifp)
693 continue;
694
695 /* scan per-interface (S,G) state */
696 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
697
698 if (ch->upstream != up)
699 continue;
700
701 pim_ifchannel_update_assert_tracking_desired(ch);
702
703 } /* scan iface channel list */
704 } /* scan iflist */
705}
f14248dd
DS
706
707/*
708 * On an RP, the PMBR value must be cleared when the
709 * Keepalive Timer expires
710 */
711static int
712pim_upstream_keep_alive_timer (struct thread *t)
713{
714 struct pim_upstream *up;
715
716 up = THREAD_ARG(t);
717
25a335e0
DS
718 if (I_am_RP (up->group_addr))
719 {
720 pim_br_clear_pmbr (up->source_addr, up->group_addr);
721 /*
722 * We need to do more here :)
723 * But this is the start.
724 */
725 }
726 else
727 {
728 pim_mroute_update_counters (up->channel_oil);
f14248dd 729
25a335e0
DS
730 if (up->channel_oil->cc.oldpktcnt >= up->channel_oil->cc.pktcnt)
731 {
732 pim_mroute_del (up->channel_oil);
733 pim_upstream_delete (up);
734 }
735 else
736 {
737 up->t_ka_timer = NULL;
738 pim_upstream_keep_alive_timer_start (up, PIM_KEEPALIVE_PERIOD);
739 }
740 }
cb40b272 741 return 1;
f14248dd
DS
742}
743
f14248dd
DS
744void
745pim_upstream_keep_alive_timer_start (struct pim_upstream *up,
746 uint32_t time)
747{
748 THREAD_TIMER_ON (master,
749 up->t_ka_timer,
750 pim_upstream_keep_alive_timer,
751 up, time);
752}
cb40b272
DS
753
754/*
755 * 4.2.1 Last-Hop Switchover to the SPT
756 *
757 * In Sparse-Mode PIM, last-hop routers join the shared tree towards the
758 * RP. Once traffic from sources to joined groups arrives at a last-hop
759 * router, it has the option of switching to receive the traffic on a
760 * shortest path tree (SPT).
761 *
762 * The decision for a router to switch to the SPT is controlled as
763 * follows:
764 *
765 * void
766 * CheckSwitchToSpt(S,G) {
767 * if ( ( pim_include(*,G) (-) pim_exclude(S,G)
768 * (+) pim_include(S,G) != NULL )
769 * AND SwitchToSptDesired(S,G) ) {
770 * # Note: Restarting the KAT will result in the SPT switch
771 * set KeepaliveTimer(S,G) to Keepalive_Period
772 * }
773 * }
774 *
775 * SwitchToSptDesired(S,G) is a policy function that is implementation
776 * defined. An "infinite threshold" policy can be implemented by making
777 * SwitchToSptDesired(S,G) return false all the time. A "switch on
778 * first packet" policy can be implemented by making
779 * SwitchToSptDesired(S,G) return true once a single packet has been
780 * received for the source and group.
781 */
782int
783pim_upstream_switch_to_spt_desired (struct in_addr source, struct in_addr group)
784{
785 return 0;
786}
d7259eac
DS
787
788const char *
789pim_upstream_state2str (struct pim_upstream *up)
790{
791 switch (up->join_state)
792 {
793 case PIM_UPSTREAM_NOTJOINED:
794 return "NtJnd";
795 break;
796 case PIM_UPSTREAM_JOINED:
797 return "Jnd";
798 break;
799 case PIM_UPSTREAM_JOIN_PENDING:
800 return "JPend";
801 break;
802 case PIM_UPSTREAM_PRUNE:
803 return "Prune";
804 break;
805 }
806 return "Unkwn";
807}