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