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