]>
git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2008 Everton da Silva Marques
19 #include "pim_iface.h"
21 #include "pim_vxlan.h"
23 static void pim_channel_update_mute(struct channel_oil
*c_oil
);
25 char *pim_channel_oil_dump(struct channel_oil
*c_oil
, char *buf
, size_t size
)
28 struct interface
*ifp
;
32 sg
.src
= *oil_origin(c_oil
);
33 sg
.grp
= *oil_mcastgrp(c_oil
);
34 ifp
= pim_if_find_by_vif_index(c_oil
->pim
, *oil_parent(c_oil
));
35 snprintfrr(buf
, size
, "%pSG IIF: %s, OIFS: ", &sg
,
36 ifp
? ifp
->name
: "(?)");
38 out
= buf
+ strlen(buf
);
39 for (i
= 0; i
< MAXVIFS
; i
++) {
40 if (oil_if_has(c_oil
, i
) != 0) {
41 ifp
= pim_if_find_by_vif_index(c_oil
->pim
, i
);
42 snprintf(out
, buf
+ size
- out
, "%s ",
43 ifp
? ifp
->name
: "(?)");
51 int pim_channel_oil_compare(const struct channel_oil
*cc1
,
52 const struct channel_oil
*cc2
)
54 struct channel_oil
*c1
= (struct channel_oil
*)cc1
;
55 struct channel_oil
*c2
= (struct channel_oil
*)cc2
;
58 rv
= pim_addr_cmp(*oil_mcastgrp(c1
), *oil_mcastgrp(c2
));
61 rv
= pim_addr_cmp(*oil_origin(c1
), *oil_origin(c2
));
67 void pim_oil_init(struct pim_instance
*pim
)
69 rb_pim_oil_init(&pim
->channel_oil_head
);
72 void pim_oil_terminate(struct pim_instance
*pim
)
74 struct channel_oil
*c_oil
;
76 while ((c_oil
= rb_pim_oil_pop(&pim
->channel_oil_head
)))
77 pim_channel_oil_free(c_oil
);
79 rb_pim_oil_fini(&pim
->channel_oil_head
);
82 void pim_channel_oil_free(struct channel_oil
*c_oil
)
84 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
87 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
90 struct channel_oil
*c_oil
= NULL
;
91 struct channel_oil lookup
;
93 *oil_mcastgrp(&lookup
) = sg
->grp
;
94 *oil_origin(&lookup
) = sg
->src
;
96 c_oil
= rb_pim_oil_find(&pim
->channel_oil_head
, &lookup
);
101 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
102 pim_sgaddr
*sg
, const char *name
)
104 struct channel_oil
*c_oil
;
106 c_oil
= pim_find_channel_oil(pim
, sg
);
108 ++c_oil
->oil_ref_count
;
111 /* channel might be present prior to upstream */
112 c_oil
->up
= pim_upstream_find(
114 /* if the upstream entry is being anchored to an
115 * already existing channel OIL we need to re-evaluate
116 * the "Mute" state on AA OIFs
118 pim_channel_update_mute(c_oil
);
121 /* check if the IIF has changed
122 * XXX - is this really needed
124 pim_upstream_mroute_iif_update(c_oil
, __func__
);
126 if (PIM_DEBUG_MROUTE
)
128 "%s(%s): Existing oil for %pSG Ref Count: %d (Post Increment)",
129 __func__
, name
, sg
, c_oil
->oil_ref_count
);
133 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
135 *oil_mcastgrp(c_oil
) = sg
->grp
;
136 *oil_origin(c_oil
) = sg
->src
;
138 *oil_parent(c_oil
) = MAXVIFS
;
139 c_oil
->oil_ref_count
= 1;
140 c_oil
->installed
= 0;
141 c_oil
->up
= pim_upstream_find(pim
, sg
);
144 rb_pim_oil_add(&pim
->channel_oil_head
, c_oil
);
146 if (PIM_DEBUG_MROUTE
)
147 zlog_debug("%s(%s): c_oil %pSG add", __func__
, name
, sg
);
154 * Clean up mroute and channel oil created for dropping pkts from directly
155 * connected source when the interface was non DR.
157 void pim_clear_nocache_state(struct pim_interface
*pim_ifp
)
159 struct channel_oil
*c_oil
;
161 frr_each_safe (rb_pim_oil
, &pim_ifp
->pim
->channel_oil_head
, c_oil
) {
164 !(PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(c_oil
->up
->flags
)))
167 if (*oil_parent(c_oil
) != pim_ifp
->mroute_vif_index
)
170 EVENT_OFF(c_oil
->up
->t_ka_timer
);
171 PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(c_oil
->up
->flags
);
172 PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(c_oil
->up
->flags
);
173 pim_upstream_del(pim_ifp
->pim
, c_oil
->up
, __func__
);
177 struct channel_oil
*pim_channel_oil_del(struct channel_oil
*c_oil
,
180 if (PIM_DEBUG_MROUTE
) {
181 pim_sgaddr sg
= {.src
= *oil_origin(c_oil
),
182 .grp
= *oil_mcastgrp(c_oil
)};
185 "%s(%s): Del oil for %pSG, Ref Count: %d (Predecrement)",
186 __func__
, name
, &sg
, c_oil
->oil_ref_count
);
188 --c_oil
->oil_ref_count
;
190 if (c_oil
->oil_ref_count
< 1) {
192 * notice that listnode_delete() can't be moved
193 * into pim_channel_oil_free() because the later is
194 * called by list_delete_all_node()
197 rb_pim_oil_del(&c_oil
->pim
->channel_oil_head
, c_oil
);
199 pim_channel_oil_free(c_oil
);
206 void pim_channel_oil_upstream_deref(struct channel_oil
*c_oil
)
208 /* The upstream entry associated with a channel_oil is abt to be
209 * deleted. If the channel_oil is kept around because of other
210 * references we need to remove upstream based states out of it.
212 c_oil
= pim_channel_oil_del(c_oil
, __func__
);
214 /* note: here we assume that c_oil->up has already been
217 pim_channel_update_mute(c_oil
);
221 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
222 uint32_t proto_mask
, const char *caller
)
224 struct pim_interface
*pim_ifp
;
231 assertf(pim_ifp
->mroute_vif_index
>= 0,
232 "trying to del OIF %s with VIF (%d)", oif
->name
,
233 pim_ifp
->mroute_vif_index
);
236 * Don't do anything if we've been asked to remove a source
237 * that is not actually on it.
239 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
240 if (PIM_DEBUG_MROUTE
) {
242 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
243 __FILE__
, __func__
, proto_mask
,
245 ->oif_flags
[pim_ifp
->mroute_vif_index
],
246 oif
->name
, pim_ifp
->mroute_vif_index
,
247 oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
),
248 oil_origin(channel_oil
),
249 oil_mcastgrp(channel_oil
));
254 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
256 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &
257 PIM_OIF_FLAG_PROTO_ANY
) {
258 if (PIM_DEBUG_MROUTE
) {
260 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
261 __FILE__
, __func__
, oif
->name
,
262 pim_ifp
->mroute_vif_index
,
263 oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
),
264 oil_origin(channel_oil
),
265 oil_mcastgrp(channel_oil
));
270 oil_if_set(channel_oil
, pim_ifp
->mroute_vif_index
, 0);
271 /* clear mute; will be re-evaluated when the OIF becomes valid again */
272 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~PIM_OIF_FLAG_MUTE
;
274 if (pim_upstream_mroute_add(channel_oil
, __func__
)) {
275 if (PIM_DEBUG_MROUTE
) {
277 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)",
278 __FILE__
, __func__
, oif
->name
,
279 pim_ifp
->mroute_vif_index
,
280 oil_origin(channel_oil
),
281 oil_mcastgrp(channel_oil
));
286 --channel_oil
->oil_size
;
288 if (PIM_DEBUG_MROUTE
) {
290 "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
291 __func__
, caller
, oil_origin(channel_oil
),
292 oil_mcastgrp(channel_oil
),
294 *oil_parent(channel_oil
), oif
->name
,
295 pim_ifp
->mroute_vif_index
);
301 void pim_channel_del_inherited_oif(struct channel_oil
*c_oil
,
302 struct interface
*oif
, const char *caller
)
304 struct pim_upstream
*up
= c_oil
->up
;
306 pim_channel_del_oif(c_oil
, oif
, PIM_OIF_FLAG_PROTO_STAR
,
309 /* if an inherited OIF is being removed join-desired can change
310 * if the inherited OIL is now empty and KAT is running
312 if (up
&& !pim_addr_is_any(up
->sg
.src
) &&
313 pim_upstream_empty_inherited_olist(up
))
314 pim_upstream_update_join_desired(up
->pim
, up
);
317 static bool pim_channel_eval_oif_mute(struct channel_oil
*c_oil
,
318 struct pim_interface
*pim_ifp
)
320 struct pim_interface
*pim_reg_ifp
;
321 struct pim_interface
*vxlan_ifp
;
322 bool do_mute
= false;
323 struct pim_instance
*pim
= c_oil
->pim
;
328 pim_reg_ifp
= pim
->regiface
->info
;
329 if (pim_ifp
== pim_reg_ifp
) {
330 /* suppress pimreg in the OIL if the mroute is not supposed to
331 * trigger register encapsulated data
333 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil
->up
->flags
))
339 vxlan_ifp
= pim_vxlan_get_term_ifp(pim
);
340 if (pim_ifp
== vxlan_ifp
) {
341 /* 1. vxlan termination device must never be added to the
342 * origination mroute (and that can actually happen because
343 * of XG inheritance from the termination mroute) otherwise
344 * traffic will end up looping.
345 * PS: This check has also been extended to non-orig mroutes
346 * that have a local SIP as such mroutes can move back and
347 * forth between orig<=>non-orig type.
348 * 2. vxlan termination device should be removed from the non-DF
349 * to prevent duplicates to the overlay rxer
351 if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil
->up
->flags
) ||
352 PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
) ||
353 pim_vxlan_is_local_sip(c_oil
->up
))
359 if (PIM_I_am_DualActive(pim_ifp
)) {
360 struct pim_upstream
*starup
= c_oil
->up
->parent
;
361 if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(c_oil
->up
->flags
)
362 && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
)))
365 /* In case entry is (S,G), Negotiation happens at (*.G) */
368 && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(starup
->flags
)
369 && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(starup
->flags
)))
376 void pim_channel_update_oif_mute(struct channel_oil
*c_oil
,
377 struct pim_interface
*pim_ifp
)
382 /* If pim_ifp is not a part of the OIL there is nothing to do */
383 if (!oil_if_has(c_oil
, pim_ifp
->mroute_vif_index
))
386 old_mute
= !!(c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &
388 new_mute
= pim_channel_eval_oif_mute(c_oil
, pim_ifp
);
389 if (old_mute
== new_mute
)
393 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
396 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
399 pim_upstream_mroute_add(c_oil
, __func__
);
402 /* pim_upstream has been set or cleared on the c_oil. re-eval mute state
403 * on all existing OIFs
405 static void pim_channel_update_mute(struct channel_oil
*c_oil
)
407 struct pim_interface
*pim_reg_ifp
;
408 struct pim_interface
*vxlan_ifp
;
410 if (c_oil
->pim
->regiface
) {
411 pim_reg_ifp
= c_oil
->pim
->regiface
->info
;
413 pim_channel_update_oif_mute(c_oil
, pim_reg_ifp
);
415 vxlan_ifp
= pim_vxlan_get_term_ifp(c_oil
->pim
);
417 pim_channel_update_oif_mute(c_oil
, vxlan_ifp
);
420 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
421 uint32_t proto_mask
, const char *caller
)
423 struct pim_interface
*pim_ifp
;
427 * If we've gotten here we've gone bad, but let's
431 zlog_warn("Attempt to Add OIF for non-existent channel oil");
437 assertf(pim_ifp
->mroute_vif_index
>= 0,
438 "trying to add OIF %s with VIF (%d)", oif
->name
,
439 pim_ifp
->mroute_vif_index
);
441 /* Prevent single protocol from subscribing same interface to
442 channel (S,G) multiple times */
443 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
444 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
446 if (PIM_DEBUG_MROUTE
) {
448 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
449 __FILE__
, __func__
, proto_mask
, oif
->name
,
450 pim_ifp
->mroute_vif_index
,
451 oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
),
452 oil_origin(channel_oil
),
453 oil_mcastgrp(channel_oil
));
458 /* Allow other protocol to request subscription of same interface to
459 * channel (S,G), we need to note this information
461 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
462 & PIM_OIF_FLAG_PROTO_ANY
) {
464 /* Updating time here is not required as this time has to
465 * indicate when the interface is added
468 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
469 /* Check the OIF really exists before returning, and only log
471 if (oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
) < 1) {
473 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
474 __FILE__
, __func__
, proto_mask
, oif
->name
,
475 pim_ifp
->mroute_vif_index
,
476 oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
),
477 oil_origin(channel_oil
),
478 oil_mcastgrp(channel_oil
));
481 if (PIM_DEBUG_MROUTE
) {
483 "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d added to 0x%x",
484 __func__
, caller
, oil_origin(channel_oil
),
485 oil_mcastgrp(channel_oil
),
486 proto_mask
, oif
->name
,
487 pim_ifp
->mroute_vif_index
,
489 ->oif_flags
[pim_ifp
->mroute_vif_index
]);
494 old_ttl
= oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
);
497 if (PIM_DEBUG_MROUTE
) {
499 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%pPAs,%pPAs)",
500 __FILE__
, __func__
, oif
->name
,
501 pim_ifp
->mroute_vif_index
,
502 oil_origin(channel_oil
),
503 oil_mcastgrp(channel_oil
));
508 oil_if_set(channel_oil
, pim_ifp
->mroute_vif_index
, PIM_MROUTE_MIN_TTL
);
510 /* Some OIFs are held in a muted state i.e. the PIM state machine
511 * decided to include the OIF but additional status check such as
512 * MLAG DF role prevent it from being activated for traffic
515 if (pim_channel_eval_oif_mute(channel_oil
, pim_ifp
))
516 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
519 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
522 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
523 * valid to get installed in kernel.
525 if (*oil_parent(channel_oil
) != MAXVIFS
) {
526 if (pim_upstream_mroute_add(channel_oil
, __func__
)) {
527 if (PIM_DEBUG_MROUTE
) {
529 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)",
530 __FILE__
, __func__
, oif
->name
,
531 pim_ifp
->mroute_vif_index
,
532 oil_origin(channel_oil
),
533 oil_mcastgrp(channel_oil
));
536 oil_if_set(channel_oil
, pim_ifp
->mroute_vif_index
,
542 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
543 pim_time_monotonic_sec();
544 ++channel_oil
->oil_size
;
545 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
547 if (PIM_DEBUG_MROUTE
) {
549 "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d: DONE",
550 __func__
, caller
, oil_origin(channel_oil
),
551 oil_mcastgrp(channel_oil
),
553 oif
->name
, pim_ifp
->mroute_vif_index
);
559 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
561 static struct channel_oil null_oil
;
566 /* exclude pimreg from the OIL when checking if the inherited_oil is
568 * pimreg device (in all vrfs) uses a vifi of
569 * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
570 if (oil_if_has(c_oil
, 0))
571 oil_if_set(&null_oil
, 0, 1);
573 oil_if_set(&null_oil
, 0, 0);
575 return !oil_if_cmp(&c_oil
->oil
, &null_oil
.oil
);