/src/frr/pimd/pim_macro.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * PIM for Quagga |
4 | | * Copyright (C) 2008 Everton da Silva Marques |
5 | | */ |
6 | | |
7 | | #include <zebra.h> |
8 | | |
9 | | #include "log.h" |
10 | | #include "prefix.h" |
11 | | #include "vty.h" |
12 | | #include "plist.h" |
13 | | |
14 | | #include "pimd.h" |
15 | | #include "pim_instance.h" |
16 | | #include "pim_macro.h" |
17 | | #include "pim_iface.h" |
18 | | #include "pim_ifchannel.h" |
19 | | #include "pim_rp.h" |
20 | | |
21 | | /* |
22 | | DownstreamJPState(S,G,I) is the per-interface state machine for |
23 | | receiving (S,G) Join/Prune messages. |
24 | | |
25 | | DownstreamJPState(S,G,I) is either Join or Prune-Pending |
26 | | DownstreamJPState(*,G,I) is either Join or Prune-Pending |
27 | | */ |
28 | | static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch) |
29 | 118k | { |
30 | 118k | switch (ch->ifjoin_state) { |
31 | 70.7k | case PIM_IFJOIN_NOINFO: |
32 | 70.7k | case PIM_IFJOIN_PRUNE: |
33 | 70.7k | case PIM_IFJOIN_PRUNE_TMP: |
34 | 70.7k | case PIM_IFJOIN_PRUNE_PENDING_TMP: |
35 | 70.7k | return 0; |
36 | 16.0k | case PIM_IFJOIN_JOIN: |
37 | 48.1k | case PIM_IFJOIN_PRUNE_PENDING: |
38 | 48.1k | return 1; |
39 | 118k | } |
40 | 0 | return 0; |
41 | 118k | } |
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 | 70.7k | { |
51 | | /* local_receiver_include(S,G,I) ? */ |
52 | 70.7k | return ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE; |
53 | 70.7k | } |
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 |
63 | | DownstreamJPState(S,G,I) is either Join or Prune-Pending } |
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 | 118k | { |
69 | 118k | return downstream_jpstate_isjoined(ch); |
70 | 118k | } |
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 |
80 | | lost_assert(S,G,I) == true } |
81 | | |
82 | | bool lost_assert(S,G,I) { |
83 | | if ( RPF_interface(S) == I ) { |
84 | | return false |
85 | | } else { |
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) ) |
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 | 81.9k | { |
98 | 81.9k | struct interface *ifp; |
99 | 81.9k | struct pim_interface *pim_ifp; |
100 | 81.9k | struct pim_assert_metric spt_assert_metric; |
101 | | |
102 | 81.9k | ifp = ch->interface; |
103 | 81.9k | if (!ifp) { |
104 | 0 | zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str); |
105 | 0 | return 0; /* false */ |
106 | 0 | } |
107 | | |
108 | | /* RPF_interface(S) == I ? */ |
109 | 81.9k | if (ch->upstream->rpf.source_nexthop.interface == ifp) |
110 | 0 | return 0; /* false */ |
111 | | |
112 | 81.9k | pim_ifp = ifp->info; |
113 | 81.9k | if (!pim_ifp) { |
114 | 0 | zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", |
115 | 0 | __func__, ch->sg_str, ifp->name); |
116 | 0 | return 0; /* false */ |
117 | 0 | } |
118 | | |
119 | 81.9k | if (pim_addr_is_any(ch->ifassert_winner)) |
120 | 81.9k | return 0; /* false */ |
121 | | |
122 | | /* AssertWinner(S,G,I) == me ? */ |
123 | 8 | if (!pim_addr_cmp(ch->ifassert_winner, pim_ifp->primary_address)) |
124 | 8 | return 0; /* false */ |
125 | | |
126 | 0 | spt_assert_metric = pim_macro_spt_assert_metric( |
127 | 0 | &ch->upstream->rpf, pim_ifp->primary_address); |
128 | |
|
129 | 0 | return pim_assert_metric_better(&ch->ifassert_winner_metric, |
130 | 0 | &spt_assert_metric); |
131 | 8 | } |
132 | | |
133 | | /* |
134 | | RFC 4601: 4.1.6. State Summarization Macros |
135 | | |
136 | | pim_include(S,G) = |
137 | | { all interfaces I such that: |
138 | | ( (I_am_DR( I ) AND lost_assert(S,G,I) == false ) |
139 | | OR AssertWinner(S,G,I) == me ) |
140 | | AND local_receiver_include(S,G,I) } |
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 | 34.9k | { |
147 | 34.9k | struct pim_interface *pim_ifp = ch->interface->info; |
148 | 34.9k | bool mlag_active = false; |
149 | | |
150 | 34.9k | if (!pim_ifp) { |
151 | 0 | zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", |
152 | 0 | __func__, ch->sg_str, ch->interface->name); |
153 | 0 | return 0; /* false */ |
154 | 0 | } |
155 | | |
156 | | /* local_receiver_include(S,G,I) ? */ |
157 | 34.9k | if (!local_receiver_include(ch)) |
158 | 34.9k | return 0; /* false */ |
159 | | |
160 | | /* OR AssertWinner(S,G,I) == me ? */ |
161 | 0 | if (!pim_addr_cmp(ch->ifassert_winner, pim_ifp->primary_address)) |
162 | 0 | return 1; /* true */ |
163 | | |
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 | 0 | if (pim_ifp->activeactive) { |
170 | 0 | struct pim_upstream *up = ch->upstream; |
171 | |
|
172 | 0 | if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags)) |
173 | 0 | mlag_active = true; |
174 | 0 | } |
175 | |
|
176 | 0 | return ( |
177 | | /* I_am_DR( I ) ? */ |
178 | 0 | (PIM_I_am_DR(pim_ifp) || mlag_active) && |
179 | | /* lost_assert(S,G,I) == false ? */ |
180 | 0 | (!pim_macro_ch_lost_assert(ch))); |
181 | 0 | } |
182 | | |
183 | | int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch) |
184 | 81.9k | { |
185 | 81.9k | if (pim_macro_chisin_joins(ch)) |
186 | 47.0k | return 1; /* true */ |
187 | | |
188 | 34.9k | return pim_macro_chisin_pim_include(ch); |
189 | 81.9k | } |
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) ) |
198 | | (+) ( pim_include(*,G) (-) pim_exclude(S,G) ) |
199 | | (-) lost_assert(*,G) |
200 | | (+) joins(S,G) (+) pim_include(S,G) ) ) |
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 | 37.0k | { |
220 | 37.0k | struct interface *ifp; |
221 | | |
222 | 37.0k | ifp = ch->interface; |
223 | 37.0k | if (!ifp) { |
224 | 0 | zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str); |
225 | 0 | return 0; /* false */ |
226 | 0 | } |
227 | | |
228 | | /* SPTbit(S,G) == true */ |
229 | 37.0k | if (ch->upstream->sptbit == PIM_UPSTREAM_SPTBIT_FALSE) |
230 | 37.0k | return 0; /* false */ |
231 | | |
232 | | /* RPF_interface(S) != I ? */ |
233 | 0 | if (ch->upstream->rpf.source_nexthop.interface == ifp) |
234 | 0 | return 0; /* false */ |
235 | | |
236 | | /* I in joins(S,G) (+) pim_include(S,G) ? */ |
237 | 0 | return pim_macro_chisin_joins_or_include(ch); |
238 | 0 | } |
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, |
252 | | pim_addr ifaddr) |
253 | 0 | { |
254 | 0 | struct pim_assert_metric metric; |
255 | |
|
256 | 0 | metric.rpt_bit_flag = 0; |
257 | 0 | metric.metric_preference = rpf->source_nexthop.mrib_metric_preference; |
258 | 0 | metric.route_metric = rpf->source_nexthop.mrib_route_metric; |
259 | 0 | metric.ip_address = ifaddr; |
260 | |
|
261 | 0 | return metric; |
262 | 0 | } |
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) { |
272 | | if( CouldAssert(S,G,I) == true ) { |
273 | | return spt_assert_metric(S,I) |
274 | | } else if( CouldAssert(*,G,I) == true ) { |
275 | | return rpt_assert_metric(G,I) |
276 | | } else { |
277 | | return infinite_assert_metric() |
278 | | } |
279 | | } |
280 | | */ |
281 | | struct pim_assert_metric |
282 | | pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch) |
283 | 70.8k | { |
284 | 70.8k | struct pim_interface *pim_ifp; |
285 | | |
286 | 70.8k | pim_ifp = ch->interface->info; |
287 | | |
288 | 70.8k | if (pim_ifp) { |
289 | 70.8k | if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { |
290 | 0 | return pim_macro_spt_assert_metric( |
291 | 0 | &ch->upstream->rpf, pim_ifp->primary_address); |
292 | 0 | } |
293 | 70.8k | } |
294 | | |
295 | 70.8k | return router->infinite_assert_metric; |
296 | 70.8k | } |
297 | | |
298 | | /* |
299 | | RFC 4601 4.2. Data Packet Forwarding Rules |
300 | | |
301 | | Macro: |
302 | | inherited_olist(S,G) = |
303 | | inherited_olist(S,G,rpt) (+) |
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 | 0 | { |
308 | 0 | if (pim_macro_ch_lost_assert(ch)) |
309 | 0 | return 0; /* false */ |
310 | | |
311 | 0 | return pim_macro_chisin_joins_or_include(ch); |
312 | 0 | } |
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: |
320 | | |
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 |
330 | | |
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 | 1.18k | { |
345 | 1.18k | if (ch->upstream->join_state == PIM_UPSTREAM_NOTJOINED) { |
346 | | /* oiflist is NULL */ |
347 | 1.18k | return 0; /* false */ |
348 | 1.18k | } |
349 | | |
350 | | /* oiflist = oiflist (-) iif */ |
351 | 0 | if (ch->interface == ch->upstream->rpf.source_nexthop.interface) |
352 | 0 | return 0; /* false */ |
353 | | |
354 | 0 | return pim_macro_chisin_inherited_olist(ch); |
355 | 0 | } |
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) ) ) |
365 | | OR (local_receiver_include(S,G,I) == true |
366 | | AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me))) |
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)) |
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 | 37.0k | { |
376 | 37.0k | struct pim_interface *pim_ifp; |
377 | 37.0k | struct interface *ifp; |
378 | | |
379 | 37.0k | ifp = ch->interface; |
380 | 37.0k | if (!ifp) { |
381 | 0 | zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str); |
382 | 0 | return 0; /* false */ |
383 | 0 | } |
384 | | |
385 | 37.0k | pim_ifp = ifp->info; |
386 | 37.0k | if (!pim_ifp) { |
387 | 0 | zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", |
388 | 0 | __func__, ch->sg_str, ch->interface->name); |
389 | 0 | return 0; /* false */ |
390 | 0 | } |
391 | | |
392 | | /* I in joins(S,G) ? */ |
393 | 37.0k | if (pim_macro_chisin_joins(ch)) |
394 | 1.19k | return 1; /* true */ |
395 | | |
396 | | /* local_receiver_include(S,G,I) ? */ |
397 | 35.8k | if (local_receiver_include(ch)) { |
398 | | /* I_am_DR(I) ? */ |
399 | 0 | if (PIM_I_am_DR(pim_ifp)) |
400 | 0 | return 1; /* true */ |
401 | | |
402 | | /* AssertWinner(S,G,I) == me ? */ |
403 | 0 | if (!pim_addr_cmp(ch->ifassert_winner, |
404 | 0 | pim_ifp->primary_address)) |
405 | 0 | return 1; /* true */ |
406 | 0 | } |
407 | | |
408 | | /* RPF_interface(S) == I ? */ |
409 | 35.8k | if (ch->upstream->rpf.source_nexthop.interface == ifp) { |
410 | | /* JoinDesired(S,G) ? */ |
411 | 0 | if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags)) |
412 | 0 | return 1; /* true */ |
413 | 0 | } |
414 | | |
415 | 35.8k | return 0; /* false */ |
416 | 35.8k | } |