blob: 3f56532526ad7736b79e68c6c74a9dc5b5d4bc7f [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
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 "log.h"
26
27#include "pim_macro.h"
28#include "pimd.h"
29#include "pim_str.h"
30#include "pim_iface.h"
31#include "pim_ifchannel.h"
32
33#define PIM_IFP_I_am_DR(pim_ifp) ((pim_ifp)->pim_dr_addr.s_addr == (pim_ifp)->primary_address.s_addr)
34
35/*
36 DownstreamJPState(S,G,I) is the per-interface state machine for
37 receiving (S,G) Join/Prune messages.
38
39 DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
40*/
41static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch)
42{
43 return ch->ifjoin_state != PIM_IFJOIN_NOINFO;
44}
45
46/*
47 The clause "local_receiver_include(S,G,I)" is true if the IGMP/MLD
48 module or other local membership mechanism has determined that local
49 members on interface I desire to receive traffic sent specifically
50 by S to G.
51*/
52static int local_receiver_include(const struct pim_ifchannel *ch)
53{
54 /* local_receiver_include(S,G,I) ? */
55 return ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE;
56}
57
58/*
59 RFC 4601: 4.1.6. State Summarization Macros
60
61 The set "joins(S,G)" is the set of all interfaces on which the
62 router has received (S,G) Joins:
63
64 joins(S,G) =
65 { all interfaces I such that
66 DownstreamJPState(S,G,I) is either Join or Prune-Pending }
67
68 DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
69*/
70int pim_macro_chisin_joins(const struct pim_ifchannel *ch)
71{
72 return downstream_jpstate_isjoined(ch);
73}
74
75/*
76 RFC 4601: 4.6.5. Assert State Macros
77
78 The set "lost_assert(S,G)" is the set of all interfaces on which the
79 router has received (S,G) joins but has lost an (S,G) assert.
80
81 lost_assert(S,G) =
82 { all interfaces I such that
83 lost_assert(S,G,I) == TRUE }
84
85 bool lost_assert(S,G,I) {
86 if ( RPF_interface(S) == I ) {
87 return FALSE
88 } else {
89 return ( AssertWinner(S,G,I) != NULL AND
90 AssertWinner(S,G,I) != me AND
91 (AssertWinnerMetric(S,G,I) is better
92 than spt_assert_metric(S,I) )
93 }
94 }
95
96 AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
97 packet that won an Assert.
98*/
99int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch)
100{
101 struct interface *ifp;
102 struct pim_interface *pim_ifp;
103 struct pim_assert_metric spt_assert_metric;
104
105 ifp = ch->interface;
106 if (!ifp) {
107 char src_str[100];
108 char grp_str[100];
109 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
110 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
111 zlog_warn("%s: (S,G)=(%s,%s): null interface",
112 __PRETTY_FUNCTION__,
113 src_str, grp_str);
114 return 0; /* false */
115 }
116
117 /* RPF_interface(S) == I ? */
118 if (ch->upstream->rpf.source_nexthop.interface == ifp)
119 return 0; /* false */
120
121 pim_ifp = ifp->info;
122 if (!pim_ifp) {
123 char src_str[100];
124 char grp_str[100];
125 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
126 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
127 zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
128 __PRETTY_FUNCTION__,
129 src_str, grp_str, ifp->name);
130 return 0; /* false */
131 }
132
133 if (PIM_INADDR_IS_ANY(ch->ifassert_winner))
134 return 0; /* false */
135
136 /* AssertWinner(S,G,I) == me ? */
137 if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
138 return 0; /* false */
139
140 spt_assert_metric = pim_macro_spt_assert_metric(&ch->upstream->rpf,
141 pim_ifp->primary_address);
142
143 return pim_assert_metric_better(&ch->ifassert_winner_metric,
144 &spt_assert_metric);
145}
146
147/*
148 RFC 4601: 4.1.6. State Summarization Macros
149
150 pim_include(S,G) =
151 { all interfaces I such that:
152 ( (I_am_DR( I ) AND lost_assert(S,G,I) == FALSE )
153 OR AssertWinner(S,G,I) == me )
154 AND local_receiver_include(S,G,I) }
155
156 AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
157 packet that won an Assert.
158*/
159int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch)
160{
161 struct pim_interface *pim_ifp = ch->interface->info;
162
163 if (!pim_ifp) {
164 char src_str[100];
165 char grp_str[100];
166 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
167 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
168 zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
169 __PRETTY_FUNCTION__,
170 src_str, grp_str, ch->interface->name);
171 return 0; /* false */
172 }
173
174 /* local_receiver_include(S,G,I) ? */
175 if (!local_receiver_include(ch))
176 return 0; /* false */
177
178 /* OR AssertWinner(S,G,I) == me ? */
179 if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
180 return 1; /* true */
181
182 return (
183 /* I_am_DR( I ) ? */
184 PIM_IFP_I_am_DR(pim_ifp)
185 &&
186 /* lost_assert(S,G,I) == FALSE ? */
187 (!pim_macro_ch_lost_assert(ch))
188 );
189}
190
191int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch)
192{
193 if (pim_macro_chisin_joins(ch))
194 return 1; /* true */
195
196 return pim_macro_chisin_pim_include(ch);
197}
198
199/*
200 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
201
202 CouldAssert(S,G,I) =
203 SPTbit(S,G)==TRUE
204 AND (RPF_interface(S) != I)
205 AND (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
206 (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
207 (-) lost_assert(*,G)
208 (+) joins(S,G) (+) pim_include(S,G) ) )
209
210 CouldAssert(S,G,I) is true for downstream interfaces that would be in
211 the inherited_olist(S,G) if (S,G) assert information was not taken
212 into account.
213
214 CouldAssert(S,G,I) may be affected by changes in the following:
215
216 pim_ifp->primary_address
217 pim_ifp->pim_dr_addr
218 ch->ifassert_winner_metric
219 ch->ifassert_winner
220 ch->local_ifmembership
221 ch->ifjoin_state
222 ch->upstream->rpf.source_nexthop.mrib_metric_preference
223 ch->upstream->rpf.source_nexthop.mrib_route_metric
224 ch->upstream->rpf.source_nexthop.interface
225*/
226int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch)
227{
228 struct interface *ifp;
229
230 /* SPTbit(S,G) is always true for PIM-SSM-Only Routers */
231
232 ifp = ch->interface;
233 if (!ifp) {
234 char src_str[100];
235 char grp_str[100];
236 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
237 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
238 zlog_warn("%s: (S,G)=(%s,%s): null interface",
239 __PRETTY_FUNCTION__,
240 src_str, grp_str);
241 return 0; /* false */
242 }
243
244 /* RPF_interface(S) != I ? */
245 if (ch->upstream->rpf.source_nexthop.interface == ifp)
246 return 0; /* false */
247
248 /* I in joins(S,G) (+) pim_include(S,G) ? */
249 return pim_macro_chisin_joins_or_include(ch);
250}
251
252/*
253 RFC 4601: 4.6.3. Assert Metrics
254
255 spt_assert_metric(S,I) gives the assert metric we use if we're
256 sending an assert based on active (S,G) forwarding state:
257
258 assert_metric
259 spt_assert_metric(S,I) {
260 return {0,MRIB.pref(S),MRIB.metric(S),my_ip_address(I)}
261 }
262*/
263struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf,
264 struct in_addr ifaddr)
265{
266 struct pim_assert_metric metric;
267
268 metric.rpt_bit_flag = 0;
269 metric.metric_preference = rpf->source_nexthop.mrib_metric_preference;
270 metric.route_metric = rpf->source_nexthop.mrib_route_metric;
271 metric.ip_address = ifaddr;
272
273 return metric;
274}
275
276/*
277 RFC 4601: 4.6.3. Assert Metrics
278
279 An assert metric for (S,G) to include in (or compare against) an
280 Assert message sent on interface I should be computed using the
281 following pseudocode:
282
283 assert_metric my_assert_metric(S,G,I) {
284 if( CouldAssert(S,G,I) == TRUE ) {
285 return spt_assert_metric(S,I)
286 } else if( CouldAssert(*,G,I) == TRUE ) {
287 return rpt_assert_metric(G,I)
288 } else {
289 return infinite_assert_metric()
290 }
291 }
292*/
293struct pim_assert_metric pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch)
294{
295 struct pim_interface *pim_ifp;
296
297 pim_ifp = ch->interface->info;
298
299 if (pim_ifp) {
300 if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
301 return pim_macro_spt_assert_metric(&ch->upstream->rpf, pim_ifp->primary_address);
302 }
303 }
304
305 return qpim_infinite_assert_metric;
306}
307
308/*
309 RFC 4601 4.2. Data Packet Forwarding Rules
310 RFC 4601 4.8.2. PIM-SSM-Only Routers
311
312 Macro:
313 inherited_olist(S,G) =
314 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
315*/
316static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch)
317{
318 if (pim_macro_ch_lost_assert(ch))
319 return 0; /* false */
320
321 return pim_macro_chisin_joins_or_include(ch);
322}
323
324/*
325 RFC 4601 4.2. Data Packet Forwarding Rules
326 RFC 4601 4.8.2. PIM-SSM-Only Routers
327
328 Additionally, the Packet forwarding rules of Section 4.2 can be
329 simplified in a PIM-SSM-only router:
330
331 iif is the incoming interface of the packet.
332 oiflist = NULL
333 if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
334 oiflist = inherited_olist(S,G)
335 } else if (iif is in inherited_olist(S,G)) {
336 send Assert(S,G) on iif
337 }
338 oiflist = oiflist (-) iif
339 forward packet on all interfaces in oiflist
340
341 Macro:
342 inherited_olist(S,G) =
343 joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
344
345 Note:
346 - The following test is performed as response to WRONGVIF kernel
347 upcall:
348 if (iif is in inherited_olist(S,G)) {
349 send Assert(S,G) on iif
350 }
351 See pim_mroute.c mroute_msg().
352*/
353int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch)
354{
355 if (ch->upstream->join_state != PIM_UPSTREAM_JOINED) {
356 /* oiflist is NULL */
357 return 0; /* false */
358 }
359
360 /* oiflist = oiflist (-) iif */
361 if (ch->interface == ch->upstream->rpf.source_nexthop.interface)
362 return 0; /* false */
363
364 return pim_macro_chisin_inherited_olist(ch);
365}
366
367/*
368 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
369
370 AssertTrackingDesired(S,G,I) =
371 (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
372 (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
373 (-) lost_assert(*,G)
374 (+) joins(S,G) ) )
375 OR (local_receiver_include(S,G,I) == TRUE
376 AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me)))
377 OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == TRUE))
378 OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == TRUE)
379 AND (SPTbit(S,G) == FALSE))
380
381 AssertTrackingDesired(S,G,I) is true on any interface in which an
382 (S,G) assert might affect our behavior.
383*/
384int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch)
385{
386 struct pim_interface *pim_ifp;
387 struct interface *ifp;
388
389 ifp = ch->interface;
390 if (!ifp) {
391 char src_str[100];
392 char grp_str[100];
393 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
394 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
395 zlog_warn("%s: (S,G)=(%s,%s): null interface",
396 __PRETTY_FUNCTION__,
397 src_str, grp_str);
398 return 0; /* false */
399 }
400
401 pim_ifp = ifp->info;
402 if (!pim_ifp) {
403 char src_str[100];
404 char grp_str[100];
405 pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
406 pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
407 zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
408 __PRETTY_FUNCTION__,
409 src_str, grp_str, ch->interface->name);
410 return 0; /* false */
411 }
412
413 /* I in joins(S,G) ? */
414 if (pim_macro_chisin_joins(ch))
415 return 1; /* true */
416
417 /* local_receiver_include(S,G,I) ? */
418 if (local_receiver_include(ch)) {
419 /* I_am_DR(I) ? */
420 if (PIM_IFP_I_am_DR(pim_ifp))
421 return 1; /* true */
422
423 /* AssertWinner(S,G,I) == me ? */
424 if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
425 return 1; /* true */
426 }
427
428 /* RPF_interface(S) == I ? */
429 if (ch->upstream->rpf.source_nexthop.interface == ifp) {
430 /* JoinDesired(S,G) ? */
431 if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags))
432 return 1; /* true */
433 }
434
435 return 0; /* false */
436}
437