3 Copyright (C) 2008 Everton da Silva Marques
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.
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.
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,
20 $QuaggaId: $Format:%an, %ai, %h$ $
25 #include "zebra/rib.h"
37 #include "pim_iface.h"
39 #include "pim_zlookup.h"
40 #include "pim_upstream.h"
41 #include "pim_ifchannel.h"
42 #include "pim_neighbor.h"
44 #include "pim_zebra.h"
46 #include "pim_macro.h"
48 static void join_timer_start(struct pim_upstream *up);
49 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
51 void pim_upstream_free(struct pim_upstream *up)
53 XFREE(MTYPE_PIM_UPSTREAM, up);
56 static void upstream_channel_oil_detach(struct pim_upstream *up)
58 if (up->channel_oil) {
59 pim_channel_oil_del(up->channel_oil);
64 void pim_upstream_delete(struct pim_upstream *up)
66 THREAD_OFF(up->t_join_timer);
68 upstream_channel_oil_detach(up);
71 notice that listnode_delete() can't be moved
72 into pim_upstream_free() because the later is
73 called by list_delete_all_node()
75 listnode_delete(qpim_upstream_list, up);
77 pim_upstream_free(up);
80 static void send_join(struct pim_upstream *up)
82 zassert(up->join_state == PIM_UPSTREAM_JOINED);
85 if (PIM_DEBUG_PIM_TRACE) {
86 if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
90 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
91 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
92 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
93 zlog_warn("%s: can't send join upstream: RPF'(%s,%s)=%s",
95 src_str, grp_str, rpf_str);
100 /* send Join(S,G) to the current upstream neighbor */
101 pim_joinprune_send(up->rpf.source_nexthop.interface,
108 static int on_join_timer(struct thread *t)
110 struct pim_upstream *up;
118 up->t_join_timer = 0;
119 join_timer_start(up);
124 static void join_timer_start(struct pim_upstream *up)
126 if (PIM_DEBUG_PIM_EVENTS) {
129 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
130 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
131 zlog_debug("%s: starting %d sec timer for upstream (S,G)=(%s,%s)",
137 zassert(!up->t_join_timer);
139 THREAD_TIMER_ON(master, up->t_join_timer,
141 up, qpim_t_periodic);
144 void pim_upstream_join_timer_restart(struct pim_upstream *up)
146 THREAD_OFF(up->t_join_timer);
147 join_timer_start(up);
150 static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up,
153 if (PIM_DEBUG_PIM_EVENTS) {
156 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
157 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
158 zlog_debug("%s: restarting %d msec timer for upstream (S,G)=(%s,%s)",
164 THREAD_OFF(up->t_join_timer);
165 THREAD_TIMER_MSEC_ON(master, up->t_join_timer,
170 void pim_upstream_join_suppress(struct pim_upstream *up,
171 struct in_addr rpf_addr,
174 long t_joinsuppress_msec;
175 long join_timer_remain_msec;
177 t_joinsuppress_msec = MIN(pim_if_t_suppressed_msec(up->rpf.source_nexthop.interface),
180 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
182 if (PIM_DEBUG_PIM_TRACE) {
186 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
187 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
188 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
189 zlog_debug("%s %s: detected Join(%s,%s) to RPF'(S,G)=%s: join_timer=%ld msec t_joinsuppress=%ld msec",
190 __FILE__, __PRETTY_FUNCTION__,
193 join_timer_remain_msec, t_joinsuppress_msec);
196 if (join_timer_remain_msec < t_joinsuppress_msec) {
197 if (PIM_DEBUG_PIM_TRACE) {
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 zlog_debug("%s %s: suppressing Join(S,G)=(%s,%s) for %ld msec",
203 __FILE__, __PRETTY_FUNCTION__,
204 src_str, grp_str, t_joinsuppress_msec);
207 pim_upstream_join_timer_restart_msec(up, t_joinsuppress_msec);
211 void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
212 struct pim_upstream *up,
213 struct in_addr rpf_addr)
215 long join_timer_remain_msec;
218 join_timer_remain_msec = pim_time_timer_remain_msec(up->t_join_timer);
219 t_override_msec = pim_if_t_override_msec(up->rpf.source_nexthop.interface);
221 if (PIM_DEBUG_PIM_TRACE) {
225 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
226 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
227 pim_inet4_dump("<rpf?>", rpf_addr, rpf_str, sizeof(rpf_str));
228 zlog_debug("%s: to RPF'(%s,%s)=%s: join_timer=%ld msec t_override=%d msec",
230 src_str, grp_str, rpf_str,
231 join_timer_remain_msec, t_override_msec);
234 if (join_timer_remain_msec > t_override_msec) {
235 if (PIM_DEBUG_PIM_TRACE) {
238 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
239 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
240 zlog_debug("%s: decreasing (S,G)=(%s,%s) join timer to t_override=%d msec",
246 pim_upstream_join_timer_restart_msec(up, t_override_msec);
250 static void forward_on(struct pim_upstream *up)
252 struct listnode *ifnode;
253 struct listnode *ifnextnode;
254 struct listnode *chnode;
255 struct listnode *chnextnode;
256 struct interface *ifp;
257 struct pim_interface *pim_ifp;
258 struct pim_ifchannel *ch;
260 /* scan all interfaces */
261 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
266 /* scan per-interface (S,G) state */
267 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
269 if (ch->upstream != up)
272 if (pim_macro_chisin_oiflist(ch))
273 pim_forward_start(ch);
275 } /* scan iface channel list */
279 static void forward_off(struct pim_upstream *up)
281 struct listnode *ifnode;
282 struct listnode *ifnextnode;
283 struct listnode *chnode;
284 struct listnode *chnextnode;
285 struct interface *ifp;
286 struct pim_interface *pim_ifp;
287 struct pim_ifchannel *ch;
289 /* scan all interfaces */
290 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
295 /* scan per-interface (S,G) state */
296 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
298 if (ch->upstream != up)
301 pim_forward_stop(ch);
303 } /* scan iface channel list */
307 static void pim_upstream_switch(struct pim_upstream *up,
308 enum pim_upstream_state new_state)
310 enum pim_upstream_state old_state = up->join_state;
312 zassert(old_state != new_state);
314 up->join_state = new_state;
315 up->state_transition = pim_time_monotonic_sec();
317 if (PIM_DEBUG_PIM_EVENTS) {
320 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
321 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
322 zlog_debug("%s: PIM_UPSTREAM_%s: (S,G)=(%s,%s)",
324 ((new_state == PIM_UPSTREAM_JOINED) ? "JOINED" : "NOTJOINED"),
328 pim_upstream_update_assert_tracking_desired(up);
330 if (new_state == PIM_UPSTREAM_JOINED) {
333 join_timer_start(up);
337 pim_joinprune_send(up->rpf.source_nexthop.interface,
342 zassert(up->t_join_timer);
343 THREAD_OFF(up->t_join_timer);
348 static struct pim_upstream *pim_upstream_new(struct in_addr source_addr,
349 struct in_addr group_addr)
351 struct pim_upstream *up;
352 enum pim_rpf_result rpf_result;
354 up = XMALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
356 zlog_err("%s: PIM XMALLOC(%zu) failure",
357 __PRETTY_FUNCTION__, sizeof(*up));
361 up->source_addr = source_addr;
362 up->group_addr = group_addr;
365 up->t_join_timer = 0;
367 up->state_transition = pim_time_monotonic_sec();
370 up->rpf.source_nexthop.interface = 0;
371 up->rpf.source_nexthop.mrib_nexthop_addr.s_addr = PIM_NET_INADDR_ANY;
372 up->rpf.source_nexthop.mrib_metric_preference = qpim_infinite_assert_metric.metric_preference;
373 up->rpf.source_nexthop.mrib_route_metric = qpim_infinite_assert_metric.route_metric;
374 up->rpf.rpf_addr.s_addr = PIM_NET_INADDR_ANY;
376 rpf_result = pim_rpf_update(up, 0);
377 if (rpf_result == PIM_RPF_FAILURE) {
378 XFREE(MTYPE_PIM_UPSTREAM, up);
382 listnode_add(qpim_upstream_list, up);
387 struct pim_upstream *pim_upstream_find(struct in_addr source_addr,
388 struct in_addr group_addr)
390 struct listnode *up_node;
391 struct pim_upstream *up;
393 for (ALL_LIST_ELEMENTS_RO(qpim_upstream_list, up_node, up)) {
395 (source_addr.s_addr == up->source_addr.s_addr) &&
396 (group_addr.s_addr == up->group_addr.s_addr)
405 struct pim_upstream *pim_upstream_add(struct in_addr source_addr,
406 struct in_addr group_addr)
408 struct pim_upstream *up;
410 up = pim_upstream_find(source_addr, group_addr);
415 up = pim_upstream_new(source_addr, group_addr);
421 void pim_upstream_del(struct pim_upstream *up)
425 if (up->ref_count < 1) {
426 pim_upstream_delete(up);
431 Evaluate JoinDesired(S,G):
433 JoinDesired(S,G) is true if there is a downstream (S,G) interface I
436 inherited_olist(S,G) =
437 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
439 JoinDesired(S,G) may be affected by changes in the following:
441 pim_ifp->primary_address
443 ch->ifassert_winner_metric
445 ch->local_ifmembership
447 ch->upstream->rpf.source_nexthop.mrib_metric_preference
448 ch->upstream->rpf.source_nexthop.mrib_route_metric
449 ch->upstream->rpf.source_nexthop.interface
451 See also pim_upstream_update_join_desired() below.
453 int pim_upstream_evaluate_join_desired(struct pim_upstream *up)
455 struct listnode *ifnode;
456 struct listnode *ifnextnode;
457 struct listnode *chnode;
458 struct listnode *chnextnode;
459 struct interface *ifp;
460 struct pim_interface *pim_ifp;
461 struct pim_ifchannel *ch;
463 /* scan all interfaces */
464 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
469 /* scan per-interface (S,G) state */
470 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
471 if (ch->upstream != up)
474 if (pim_macro_ch_lost_assert(ch))
475 continue; /* keep searching */
477 if (pim_macro_chisin_joins_or_include(ch))
479 } /* scan iface channel list */
482 return 0; /* false */
486 See also pim_upstream_evaluate_join_desired() above.
488 void pim_upstream_update_join_desired(struct pim_upstream *up)
490 int was_join_desired; /* boolean */
491 int is_join_desired; /* boolean */
493 was_join_desired = PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags);
495 is_join_desired = pim_upstream_evaluate_join_desired(up);
497 PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(up->flags);
499 PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(up->flags);
501 /* switched from false to true */
502 if (is_join_desired && !was_join_desired) {
503 zassert(up->join_state == PIM_UPSTREAM_NOTJOINED);
504 pim_upstream_switch(up, PIM_UPSTREAM_JOINED);
508 /* switched from true to false */
509 if (!is_join_desired && was_join_desired) {
510 zassert(up->join_state == PIM_UPSTREAM_JOINED);
511 pim_upstream_switch(up, PIM_UPSTREAM_NOTJOINED);
517 RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
518 Transitions from Joined State
519 RPF'(S,G) GenID changes
521 The upstream (S,G) state machine remains in Joined state. If the
522 Join Timer is set to expire in more than t_override seconds, reset
523 it so that it expires after t_override seconds.
525 void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr)
527 struct listnode *up_node;
528 struct listnode *up_nextnode;
529 struct pim_upstream *up;
532 Scan all (S,G) upstreams searching for RPF'(S,G)=neigh_addr
534 for (ALL_LIST_ELEMENTS(qpim_upstream_list, up_node, up_nextnode, up)) {
536 if (PIM_DEBUG_PIM_TRACE) {
540 char rpf_addr_str[100];
541 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
542 pim_inet4_dump("<src?>", up->source_addr, src_str, sizeof(src_str));
543 pim_inet4_dump("<grp?>", up->group_addr, grp_str, sizeof(grp_str));
544 pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_addr_str, sizeof(rpf_addr_str));
545 zlog_debug("%s: matching neigh=%s against upstream (S,G)=(%s,%s) joined=%d rpf_addr=%s",
547 neigh_str, src_str, grp_str,
548 up->join_state == PIM_UPSTREAM_JOINED,
552 /* consider only (S,G) upstream in Joined state */
553 if (up->join_state != PIM_UPSTREAM_JOINED)
556 /* match RPF'(S,G)=neigh_addr */
557 if (up->rpf.rpf_addr.s_addr != neigh_addr.s_addr)
560 pim_upstream_join_timer_decrease_to_t_override("RPF'(S,G) GenID change",
566 void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
567 struct interface *old_rpf_ifp)
569 struct listnode *ifnode;
570 struct listnode *ifnextnode;
571 struct interface *ifp;
573 /* scan all interfaces */
574 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
575 struct listnode *chnode;
576 struct listnode *chnextnode;
577 struct pim_ifchannel *ch;
578 struct pim_interface *pim_ifp;
584 /* search all ifchannels */
585 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
586 if (ch->upstream != up)
589 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
591 /* RPF_interface(S) was NOT I */
592 (old_rpf_ifp == ch->interface)
594 /* RPF_interface(S) stopped being I */
595 (ch->upstream->rpf.source_nexthop.interface != ch->interface)
597 assert_action_a5(ch);
599 } /* PIM_IFASSERT_I_AM_LOSER */
601 pim_ifchannel_update_assert_tracking_desired(ch);
606 void pim_upstream_update_could_assert(struct pim_upstream *up)
608 struct listnode *ifnode;
609 struct listnode *ifnextnode;
610 struct listnode *chnode;
611 struct listnode *chnextnode;
612 struct interface *ifp;
613 struct pim_interface *pim_ifp;
614 struct pim_ifchannel *ch;
616 /* scan all interfaces */
617 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
622 /* scan per-interface (S,G) state */
623 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
625 if (ch->upstream != up)
628 pim_ifchannel_update_could_assert(ch);
630 } /* scan iface channel list */
634 void pim_upstream_update_my_assert_metric(struct pim_upstream *up)
636 struct listnode *ifnode;
637 struct listnode *ifnextnode;
638 struct listnode *chnode;
639 struct listnode *chnextnode;
640 struct interface *ifp;
641 struct pim_interface *pim_ifp;
642 struct pim_ifchannel *ch;
644 /* scan all interfaces */
645 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
650 /* scan per-interface (S,G) state */
651 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
653 if (ch->upstream != up)
656 pim_ifchannel_update_my_assert_metric(ch);
658 } /* scan iface channel list */
662 static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up)
664 struct listnode *ifnode;
665 struct listnode *ifnextnode;
666 struct listnode *chnode;
667 struct listnode *chnextnode;
668 struct interface *ifp;
669 struct pim_interface *pim_ifp;
670 struct pim_ifchannel *ch;
672 /* scan all interfaces */
673 for (ALL_LIST_ELEMENTS(iflist, ifnode, ifnextnode, ifp)) {
678 /* scan per-interface (S,G) state */
679 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, chnode, chnextnode, ch)) {
681 if (ch->upstream != up)
684 pim_ifchannel_update_assert_tracking_desired(ch);
686 } /* scan iface channel list */