]>
Commit | Line | Data |
---|---|---|
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 | */ |
28 | static 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 | */ | |
49 | static 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 | */ | |
67 | int 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 | */ | |
96 | int 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 | */ | |
145 | int 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 | ||
183 | int 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 | */ | |
218 | int 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 | */ | |
251 | struct 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 | 281 | struct pim_assert_metric |
282 | pim_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 | */ | |
306 | static 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 | */ | |
343 | int 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 | */ | |
374 | int 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 | } |