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 along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include "pim_instance.h"
29 #include "pim_macro.h"
30 #include "pim_iface.h"
31 #include "pim_ifchannel.h"
35 DownstreamJPState(S,G,I) is the per-interface state machine for
36 receiving (S,G) Join/Prune messages.
38 DownstreamJPState(S,G,I) is either Join or Prune-Pending
39 DownstreamJPState(*,G,I) is either Join or Prune-Pending
41 static int downstream_jpstate_isjoined(const struct pim_ifchannel
*ch
)
43 switch (ch
->ifjoin_state
) {
44 case PIM_IFJOIN_NOINFO
:
45 case PIM_IFJOIN_PRUNE
:
46 case PIM_IFJOIN_PRUNE_TMP
:
47 case PIM_IFJOIN_PRUNE_PENDING_TMP
:
50 case PIM_IFJOIN_PRUNE_PENDING
:
57 The clause "local_receiver_include(S,G,I)" is true if the IGMP/MLD
58 module or other local membership mechanism has determined that local
59 members on interface I desire to receive traffic sent specifically
62 static int local_receiver_include(const struct pim_ifchannel
*ch
)
64 /* local_receiver_include(S,G,I) ? */
65 return ch
->local_ifmembership
== PIM_IFMEMBERSHIP_INCLUDE
;
69 RFC 4601: 4.1.6. State Summarization Macros
71 The set "joins(S,G)" is the set of all interfaces on which the
72 router has received (S,G) Joins:
75 { all interfaces I such that
76 DownstreamJPState(S,G,I) is either Join or Prune-Pending }
78 DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
80 int pim_macro_chisin_joins(const struct pim_ifchannel
*ch
)
82 return downstream_jpstate_isjoined(ch
);
86 RFC 4601: 4.6.5. Assert State Macros
88 The set "lost_assert(S,G)" is the set of all interfaces on which the
89 router has received (S,G) joins but has lost an (S,G) assert.
92 { all interfaces I such that
93 lost_assert(S,G,I) == true }
95 bool lost_assert(S,G,I) {
96 if ( RPF_interface(S) == I ) {
99 return ( AssertWinner(S,G,I) != NULL AND
100 AssertWinner(S,G,I) != me AND
101 (AssertWinnerMetric(S,G,I) is better
102 than spt_assert_metric(S,I) )
106 AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
107 packet that won an Assert.
109 int pim_macro_ch_lost_assert(const struct pim_ifchannel
*ch
)
111 struct interface
*ifp
;
112 struct pim_interface
*pim_ifp
;
113 struct pim_assert_metric spt_assert_metric
;
117 zlog_warn("%s: (S,G)=%s: null interface", __func__
, ch
->sg_str
);
118 return 0; /* false */
121 /* RPF_interface(S) == I ? */
122 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ifp
)
123 return 0; /* false */
127 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
128 __func__
, ch
->sg_str
, ifp
->name
);
129 return 0; /* false */
132 if (pim_addr_is_any(ch
->ifassert_winner
))
133 return 0; /* false */
135 /* AssertWinner(S,G,I) == me ? */
136 if (!pim_addr_cmp(ch
->ifassert_winner
, pim_ifp
->primary_address
))
137 return 0; /* false */
139 spt_assert_metric
= pim_macro_spt_assert_metric(
140 &ch
->upstream
->rpf
, pim_ifp
->primary_address
);
142 return pim_assert_metric_better(&ch
->ifassert_winner_metric
,
147 RFC 4601: 4.1.6. State Summarization Macros
150 { all interfaces I such that:
151 ( (I_am_DR( I ) AND lost_assert(S,G,I) == false )
152 OR AssertWinner(S,G,I) == me )
153 AND local_receiver_include(S,G,I) }
155 AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
156 packet that won an Assert.
158 int pim_macro_chisin_pim_include(const struct pim_ifchannel
*ch
)
160 struct pim_interface
*pim_ifp
= ch
->interface
->info
;
161 bool mlag_active
= false;
164 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
165 __func__
, ch
->sg_str
, ch
->interface
->name
);
166 return 0; /* false */
169 /* local_receiver_include(S,G,I) ? */
170 if (!local_receiver_include(ch
))
171 return 0; /* false */
173 /* OR AssertWinner(S,G,I) == me ? */
174 if (!pim_addr_cmp(ch
->ifassert_winner
, pim_ifp
->primary_address
))
178 * When we have a activeactive interface we need to signal
179 * that this interface is interesting to the upstream
180 * decision to JOIN *if* we are syncing over the interface
182 if (pim_ifp
->activeactive
) {
183 struct pim_upstream
*up
= ch
->upstream
;
185 if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up
->flags
))
191 (PIM_I_am_DR(pim_ifp
) || mlag_active
) &&
192 /* lost_assert(S,G,I) == false ? */
193 (!pim_macro_ch_lost_assert(ch
)));
196 int pim_macro_chisin_joins_or_include(const struct pim_ifchannel
*ch
)
198 if (pim_macro_chisin_joins(ch
))
201 return pim_macro_chisin_pim_include(ch
);
205 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
209 AND (RPF_interface(S) != I)
210 AND (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
211 (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
213 (+) joins(S,G) (+) pim_include(S,G) ) )
215 CouldAssert(S,G,I) is true for downstream interfaces that would be in
216 the inherited_olist(S,G) if (S,G) assert information was not taken
219 CouldAssert(S,G,I) may be affected by changes in the following:
221 pim_ifp->primary_address
223 ch->ifassert_winner_metric
225 ch->local_ifmembership
227 ch->upstream->rpf.source_nexthop.mrib_metric_preference
228 ch->upstream->rpf.source_nexthop.mrib_route_metric
229 ch->upstream->rpf.source_nexthop.interface
231 int pim_macro_ch_could_assert_eval(const struct pim_ifchannel
*ch
)
233 struct interface
*ifp
;
237 zlog_warn("%s: (S,G)=%s: null interface", __func__
, ch
->sg_str
);
238 return 0; /* false */
241 /* SPTbit(S,G) == true */
242 if (ch
->upstream
->sptbit
== PIM_UPSTREAM_SPTBIT_FALSE
)
243 return 0; /* false */
245 /* RPF_interface(S) != I ? */
246 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ifp
)
247 return 0; /* false */
249 /* I in joins(S,G) (+) pim_include(S,G) ? */
250 return pim_macro_chisin_joins_or_include(ch
);
254 RFC 4601: 4.6.3. Assert Metrics
256 spt_assert_metric(S,I) gives the assert metric we use if we're
257 sending an assert based on active (S,G) forwarding state:
260 spt_assert_metric(S,I) {
261 return {0,MRIB.pref(S),MRIB.metric(S),my_ip_address(I)}
264 struct pim_assert_metric
pim_macro_spt_assert_metric(const struct pim_rpf
*rpf
,
267 struct pim_assert_metric metric
;
269 metric
.rpt_bit_flag
= 0;
270 metric
.metric_preference
= rpf
->source_nexthop
.mrib_metric_preference
;
271 metric
.route_metric
= rpf
->source_nexthop
.mrib_route_metric
;
272 metric
.ip_address
= ifaddr
;
278 RFC 4601: 4.6.3. Assert Metrics
280 An assert metric for (S,G) to include in (or compare against) an
281 Assert message sent on interface I should be computed using the
282 following pseudocode:
284 assert_metric my_assert_metric(S,G,I) {
285 if( CouldAssert(S,G,I) == true ) {
286 return spt_assert_metric(S,I)
287 } else if( CouldAssert(*,G,I) == true ) {
288 return rpt_assert_metric(G,I)
290 return infinite_assert_metric()
294 struct pim_assert_metric
295 pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel
*ch
)
297 struct pim_interface
*pim_ifp
;
299 pim_ifp
= ch
->interface
->info
;
302 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch
->flags
)) {
303 return pim_macro_spt_assert_metric(
304 &ch
->upstream
->rpf
, pim_ifp
->primary_address
);
308 return router
->infinite_assert_metric
;
312 RFC 4601 4.2. Data Packet Forwarding Rules
315 inherited_olist(S,G) =
316 inherited_olist(S,G,rpt) (+)
317 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
319 static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel
*ch
)
321 if (pim_macro_ch_lost_assert(ch
))
322 return 0; /* false */
324 return pim_macro_chisin_joins_or_include(ch
);
328 RFC 4601 4.2. Data Packet Forwarding Rules
329 RFC 4601 4.8.2. PIM-SSM-Only Routers
331 Additionally, the Packet forwarding rules of Section 4.2 can be
332 simplified in a PIM-SSM-only router:
334 iif is the incoming interface of the packet.
336 if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
337 oiflist = inherited_olist(S,G)
338 } else if (iif is in inherited_olist(S,G)) {
339 send Assert(S,G) on iif
341 oiflist = oiflist (-) iif
342 forward packet on all interfaces in oiflist
345 inherited_olist(S,G) =
346 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
349 - The following test is performed as response to WRONGVIF kernel
351 if (iif is in inherited_olist(S,G)) {
352 send Assert(S,G) on iif
354 See pim_mroute.c mroute_msg().
356 int pim_macro_chisin_oiflist(const struct pim_ifchannel
*ch
)
358 if (ch
->upstream
->join_state
== PIM_UPSTREAM_NOTJOINED
) {
359 /* oiflist is NULL */
360 return 0; /* false */
363 /* oiflist = oiflist (-) iif */
364 if (ch
->interface
== ch
->upstream
->rpf
.source_nexthop
.interface
)
365 return 0; /* false */
367 return pim_macro_chisin_inherited_olist(ch
);
371 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
373 AssertTrackingDesired(S,G,I) =
374 (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
375 (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
378 OR (local_receiver_include(S,G,I) == true
379 AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me)))
380 OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == true))
381 OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == true)
382 AND (SPTbit(S,G) == false))
384 AssertTrackingDesired(S,G,I) is true on any interface in which an
385 (S,G) assert might affect our behavior.
387 int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel
*ch
)
389 struct pim_interface
*pim_ifp
;
390 struct interface
*ifp
;
394 zlog_warn("%s: (S,G)=%s: null interface", __func__
, ch
->sg_str
);
395 return 0; /* false */
400 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
401 __func__
, ch
->sg_str
, ch
->interface
->name
);
402 return 0; /* false */
405 /* I in joins(S,G) ? */
406 if (pim_macro_chisin_joins(ch
))
409 /* local_receiver_include(S,G,I) ? */
410 if (local_receiver_include(ch
)) {
412 if (PIM_I_am_DR(pim_ifp
))
415 /* AssertWinner(S,G,I) == me ? */
416 if (!pim_addr_cmp(ch
->ifassert_winner
,
417 pim_ifp
->primary_address
))
421 /* RPF_interface(S) == I ? */
422 if (ch
->upstream
->rpf
.source_nexthop
.interface
== ifp
) {
423 /* JoinDesired(S,G) ? */
424 if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch
->upstream
->flags
))
428 return 0; /* false */