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