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