Line data Source code
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 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
18 : */
19 :
20 : #include <zebra.h>
21 :
22 : #include "log.h"
23 : #include "prefix.h"
24 : #include "vty.h"
25 : #include "plist.h"
26 :
27 : #include "pimd.h"
28 : #include "pim_instance.h"
29 : #include "pim_macro.h"
30 : #include "pim_iface.h"
31 : #include "pim_ifchannel.h"
32 : #include "pim_rp.h"
33 :
34 : /*
35 : DownstreamJPState(S,G,I) is the per-interface state machine for
36 : receiving (S,G) Join/Prune messages.
37 :
38 : DownstreamJPState(S,G,I) is either Join or Prune-Pending
39 : DownstreamJPState(*,G,I) is either Join or Prune-Pending
40 : */
41 9 : static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch)
42 : {
43 9 : 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:
48 : return 0;
49 0 : case PIM_IFJOIN_JOIN:
50 : case PIM_IFJOIN_PRUNE_PENDING:
51 0 : return 1;
52 : }
53 : return 0;
54 : }
55 :
56 : /*
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
60 : by S to G.
61 : */
62 9 : static int local_receiver_include(const struct pim_ifchannel *ch)
63 : {
64 : /* local_receiver_include(S,G,I) ? */
65 9 : return ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE;
66 : }
67 :
68 : /*
69 : RFC 4601: 4.1.6. State Summarization Macros
70 :
71 : The set "joins(S,G)" is the set of all interfaces on which the
72 : router has received (S,G) Joins:
73 :
74 : joins(S,G) =
75 : { all interfaces I such that
76 : DownstreamJPState(S,G,I) is either Join or Prune-Pending }
77 :
78 : DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
79 : */
80 9 : int pim_macro_chisin_joins(const struct pim_ifchannel *ch)
81 : {
82 0 : return downstream_jpstate_isjoined(ch);
83 : }
84 :
85 : /*
86 : RFC 4601: 4.6.5. Assert State Macros
87 :
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.
90 :
91 : lost_assert(S,G) =
92 : { all interfaces I such that
93 : lost_assert(S,G,I) == true }
94 :
95 : bool lost_assert(S,G,I) {
96 : if ( RPF_interface(S) == I ) {
97 : return false
98 : } else {
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) )
103 : }
104 : }
105 :
106 : AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
107 : packet that won an Assert.
108 : */
109 7 : int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch)
110 : {
111 7 : struct interface *ifp;
112 7 : struct pim_interface *pim_ifp;
113 7 : struct pim_assert_metric spt_assert_metric;
114 :
115 7 : ifp = ch->interface;
116 7 : if (!ifp) {
117 0 : zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str);
118 0 : return 0; /* false */
119 : }
120 :
121 : /* RPF_interface(S) == I ? */
122 7 : if (ch->upstream->rpf.source_nexthop.interface == ifp)
123 : return 0; /* false */
124 :
125 7 : pim_ifp = ifp->info;
126 7 : if (!pim_ifp) {
127 0 : zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
128 : __func__, ch->sg_str, ifp->name);
129 0 : return 0; /* false */
130 : }
131 :
132 7 : if (pim_addr_is_any(ch->ifassert_winner))
133 : return 0; /* false */
134 :
135 : /* AssertWinner(S,G,I) == me ? */
136 0 : if (!pim_addr_cmp(ch->ifassert_winner, pim_ifp->primary_address))
137 : return 0; /* false */
138 :
139 0 : spt_assert_metric = pim_macro_spt_assert_metric(
140 0 : &ch->upstream->rpf, pim_ifp->primary_address);
141 :
142 0 : return pim_assert_metric_better(&ch->ifassert_winner_metric,
143 : &spt_assert_metric);
144 : }
145 :
146 : /*
147 : RFC 4601: 4.1.6. State Summarization Macros
148 :
149 : pim_include(S,G) =
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) }
154 :
155 : AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
156 : packet that won an Assert.
157 : */
158 4 : int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch)
159 : {
160 4 : struct pim_interface *pim_ifp = ch->interface->info;
161 4 : bool mlag_active = false;
162 :
163 4 : if (!pim_ifp) {
164 0 : zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
165 : __func__, ch->sg_str, ch->interface->name);
166 0 : return 0; /* false */
167 : }
168 :
169 : /* local_receiver_include(S,G,I) ? */
170 4 : if (!local_receiver_include(ch))
171 : return 0; /* false */
172 :
173 : /* OR AssertWinner(S,G,I) == me ? */
174 3 : if (!pim_addr_cmp(ch->ifassert_winner, pim_ifp->primary_address))
175 : return 1; /* true */
176 :
177 : /*
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
181 : */
182 3 : if (pim_ifp->activeactive) {
183 0 : struct pim_upstream *up = ch->upstream;
184 :
185 0 : if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags))
186 3 : mlag_active = true;
187 : }
188 :
189 3 : return (
190 : /* I_am_DR( I ) ? */
191 6 : (PIM_I_am_DR(pim_ifp) || mlag_active) &&
192 : /* lost_assert(S,G,I) == false ? */
193 3 : (!pim_macro_ch_lost_assert(ch)));
194 : }
195 :
196 4 : int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch)
197 : {
198 4 : if (pim_macro_chisin_joins(ch))
199 : return 1; /* true */
200 :
201 4 : return pim_macro_chisin_pim_include(ch);
202 : }
203 :
204 : /*
205 : RFC 4601: 4.6.1. (S,G) Assert Message State Machine
206 :
207 : CouldAssert(S,G,I) =
208 : SPTbit(S,G)==TRUE
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) )
212 : (-) lost_assert(*,G)
213 : (+) joins(S,G) (+) pim_include(S,G) ) )
214 :
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
217 : into account.
218 :
219 : CouldAssert(S,G,I) may be affected by changes in the following:
220 :
221 : pim_ifp->primary_address
222 : pim_ifp->pim_dr_addr
223 : ch->ifassert_winner_metric
224 : ch->ifassert_winner
225 : ch->local_ifmembership
226 : ch->ifjoin_state
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
230 : */
231 4 : int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch)
232 : {
233 4 : struct interface *ifp;
234 :
235 4 : ifp = ch->interface;
236 4 : if (!ifp) {
237 0 : zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str);
238 0 : return 0; /* false */
239 : }
240 :
241 : /* SPTbit(S,G) == true */
242 4 : if (ch->upstream->sptbit == PIM_UPSTREAM_SPTBIT_FALSE)
243 : return 0; /* false */
244 :
245 : /* RPF_interface(S) != I ? */
246 0 : if (ch->upstream->rpf.source_nexthop.interface == ifp)
247 : return 0; /* false */
248 :
249 : /* I in joins(S,G) (+) pim_include(S,G) ? */
250 0 : return pim_macro_chisin_joins_or_include(ch);
251 : }
252 :
253 : /*
254 : RFC 4601: 4.6.3. Assert Metrics
255 :
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:
258 :
259 : assert_metric
260 : spt_assert_metric(S,I) {
261 : return {0,MRIB.pref(S),MRIB.metric(S),my_ip_address(I)}
262 : }
263 : */
264 0 : struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf,
265 : pim_addr ifaddr)
266 : {
267 0 : struct pim_assert_metric metric;
268 :
269 0 : metric.rpt_bit_flag = 0;
270 0 : metric.metric_preference = rpf->source_nexthop.mrib_metric_preference;
271 0 : metric.route_metric = rpf->source_nexthop.mrib_route_metric;
272 0 : metric.ip_address = ifaddr;
273 :
274 0 : return metric;
275 : }
276 :
277 : /*
278 : RFC 4601: 4.6.3. Assert Metrics
279 :
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:
283 :
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)
289 : } else {
290 : return infinite_assert_metric()
291 : }
292 : }
293 : */
294 : struct pim_assert_metric
295 4 : pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch)
296 : {
297 4 : struct pim_interface *pim_ifp;
298 :
299 4 : pim_ifp = ch->interface->info;
300 :
301 4 : if (pim_ifp) {
302 4 : if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
303 0 : return pim_macro_spt_assert_metric(
304 0 : &ch->upstream->rpf, pim_ifp->primary_address);
305 : }
306 : }
307 :
308 4 : return router->infinite_assert_metric;
309 : }
310 :
311 : /*
312 : RFC 4601 4.2. Data Packet Forwarding Rules
313 :
314 : Macro:
315 : inherited_olist(S,G) =
316 : inherited_olist(S,G,rpt) (+)
317 : joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
318 : */
319 0 : static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch)
320 : {
321 0 : if (pim_macro_ch_lost_assert(ch))
322 : return 0; /* false */
323 :
324 0 : return pim_macro_chisin_joins_or_include(ch);
325 : }
326 :
327 : /*
328 : RFC 4601 4.2. Data Packet Forwarding Rules
329 : RFC 4601 4.8.2. PIM-SSM-Only Routers
330 :
331 : Additionally, the Packet forwarding rules of Section 4.2 can be
332 : simplified in a PIM-SSM-only router:
333 :
334 : iif is the incoming interface of the packet.
335 : oiflist = NULL
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
340 : }
341 : oiflist = oiflist (-) iif
342 : forward packet on all interfaces in oiflist
343 :
344 : Macro:
345 : inherited_olist(S,G) =
346 : joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
347 :
348 : Note:
349 : - The following test is performed as response to WRONGVIF kernel
350 : upcall:
351 : if (iif is in inherited_olist(S,G)) {
352 : send Assert(S,G) on iif
353 : }
354 : See pim_mroute.c mroute_msg().
355 : */
356 0 : int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch)
357 : {
358 0 : if (ch->upstream->join_state == PIM_UPSTREAM_NOTJOINED) {
359 : /* oiflist is NULL */
360 : return 0; /* false */
361 : }
362 :
363 : /* oiflist = oiflist (-) iif */
364 0 : if (ch->interface == ch->upstream->rpf.source_nexthop.interface)
365 : return 0; /* false */
366 :
367 0 : return pim_macro_chisin_inherited_olist(ch);
368 : }
369 :
370 : /*
371 : RFC 4601: 4.6.1. (S,G) Assert Message State Machine
372 :
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) )
376 : (-) lost_assert(*,G)
377 : (+) joins(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))
383 :
384 : AssertTrackingDesired(S,G,I) is true on any interface in which an
385 : (S,G) assert might affect our behavior.
386 : */
387 5 : int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch)
388 : {
389 5 : struct pim_interface *pim_ifp;
390 5 : struct interface *ifp;
391 :
392 5 : ifp = ch->interface;
393 5 : if (!ifp) {
394 0 : zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str);
395 0 : return 0; /* false */
396 : }
397 :
398 5 : pim_ifp = ifp->info;
399 5 : if (!pim_ifp) {
400 0 : zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
401 : __func__, ch->sg_str, ch->interface->name);
402 0 : return 0; /* false */
403 : }
404 :
405 : /* I in joins(S,G) ? */
406 8 : if (pim_macro_chisin_joins(ch))
407 : return 1; /* true */
408 :
409 : /* local_receiver_include(S,G,I) ? */
410 5 : if (local_receiver_include(ch)) {
411 : /* I_am_DR(I) ? */
412 3 : if (PIM_I_am_DR(pim_ifp))
413 : return 1; /* true */
414 :
415 : /* AssertWinner(S,G,I) == me ? */
416 0 : if (!pim_addr_cmp(ch->ifassert_winner,
417 : pim_ifp->primary_address))
418 : return 1; /* true */
419 : }
420 :
421 : /* RPF_interface(S) == I ? */
422 2 : if (ch->upstream->rpf.source_nexthop.interface == ifp) {
423 : /* JoinDesired(S,G) ? */
424 0 : if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags))
425 : return 1; /* true */
426 : }
427 :
428 : return 0; /* false */
429 : }
|