]>
git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_oil.c
3 * Copyright (C) 2008 Everton da Silva Marques
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.
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.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32 #include "pim_iface.h"
34 #include "pim_vxlan.h"
36 static void pim_channel_update_mute(struct channel_oil
*c_oil
);
38 char *pim_channel_oil_dump(struct channel_oil
*c_oil
, char *buf
, size_t size
)
41 struct interface
*ifp
;
45 sg
.src
= *oil_origin(c_oil
);
46 sg
.grp
= *oil_mcastgrp(c_oil
);
47 ifp
= pim_if_find_by_vif_index(c_oil
->pim
, *oil_parent(c_oil
));
48 snprintfrr(buf
, size
, "%pSG IIF: %s, OIFS: ", &sg
,
49 ifp
? ifp
->name
: "(?)");
51 out
= buf
+ strlen(buf
);
52 for (i
= 0; i
< MAXVIFS
; i
++) {
53 if (oil_if_has(c_oil
, i
) != 0) {
54 ifp
= pim_if_find_by_vif_index(c_oil
->pim
, i
);
55 snprintf(out
, buf
+ size
- out
, "%s ",
56 ifp
? ifp
->name
: "(?)");
64 int pim_channel_oil_compare(const struct channel_oil
*cc1
,
65 const struct channel_oil
*cc2
)
67 struct channel_oil
*c1
= (struct channel_oil
*)cc1
;
68 struct channel_oil
*c2
= (struct channel_oil
*)cc2
;
71 rv
= pim_addr_cmp(*oil_mcastgrp(c1
), *oil_mcastgrp(c2
));
74 rv
= pim_addr_cmp(*oil_origin(c1
), *oil_origin(c2
));
80 void pim_oil_init(struct pim_instance
*pim
)
82 rb_pim_oil_init(&pim
->channel_oil_head
);
85 void pim_oil_terminate(struct pim_instance
*pim
)
87 struct channel_oil
*c_oil
;
89 while ((c_oil
= rb_pim_oil_pop(&pim
->channel_oil_head
)))
90 pim_channel_oil_free(c_oil
);
92 rb_pim_oil_fini(&pim
->channel_oil_head
);
95 void pim_channel_oil_free(struct channel_oil
*c_oil
)
97 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
100 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
103 struct channel_oil
*c_oil
= NULL
;
104 struct channel_oil lookup
;
106 *oil_mcastgrp(&lookup
) = sg
->grp
;
107 *oil_origin(&lookup
) = sg
->src
;
109 c_oil
= rb_pim_oil_find(&pim
->channel_oil_head
, &lookup
);
114 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
115 pim_sgaddr
*sg
, const char *name
)
117 struct channel_oil
*c_oil
;
119 c_oil
= pim_find_channel_oil(pim
, sg
);
121 ++c_oil
->oil_ref_count
;
124 /* channel might be present prior to upstream */
125 c_oil
->up
= pim_upstream_find(
127 /* if the upstream entry is being anchored to an
128 * already existing channel OIL we need to re-evaluate
129 * the "Mute" state on AA OIFs
131 pim_channel_update_mute(c_oil
);
134 /* check if the IIF has changed
135 * XXX - is this really needed
137 pim_upstream_mroute_iif_update(c_oil
, __func__
);
139 if (PIM_DEBUG_MROUTE
)
141 "%s(%s): Existing oil for %pSG Ref Count: %d (Post Increment)",
142 __func__
, name
, sg
, c_oil
->oil_ref_count
);
146 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
148 *oil_mcastgrp(c_oil
) = sg
->grp
;
149 *oil_origin(c_oil
) = sg
->src
;
151 *oil_parent(c_oil
) = MAXVIFS
;
152 c_oil
->oil_ref_count
= 1;
153 c_oil
->installed
= 0;
154 c_oil
->up
= pim_upstream_find(pim
, sg
);
157 rb_pim_oil_add(&pim
->channel_oil_head
, c_oil
);
159 if (PIM_DEBUG_MROUTE
)
160 zlog_debug("%s(%s): c_oil %pSG add", __func__
, name
, sg
);
165 struct channel_oil
*pim_channel_oil_del(struct channel_oil
*c_oil
,
168 if (PIM_DEBUG_MROUTE
) {
169 pim_sgaddr sg
= {.src
= *oil_mcastgrp(c_oil
),
170 .grp
= *oil_origin(c_oil
)};
173 "%s(%s): Del oil for %pSG, Ref Count: %d (Predecrement)",
174 __func__
, name
, &sg
, c_oil
->oil_ref_count
);
176 --c_oil
->oil_ref_count
;
178 if (c_oil
->oil_ref_count
< 1) {
180 * notice that listnode_delete() can't be moved
181 * into pim_channel_oil_free() because the later is
182 * called by list_delete_all_node()
185 rb_pim_oil_del(&c_oil
->pim
->channel_oil_head
, c_oil
);
187 pim_channel_oil_free(c_oil
);
194 void pim_channel_oil_upstream_deref(struct channel_oil
*c_oil
)
196 /* The upstream entry associated with a channel_oil is abt to be
197 * deleted. If the channel_oil is kept around because of other
198 * references we need to remove upstream based states out of it.
200 c_oil
= pim_channel_oil_del(c_oil
, __func__
);
202 /* note: here we assume that c_oil->up has already been
205 pim_channel_update_mute(c_oil
);
209 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
210 uint32_t proto_mask
, const char *caller
)
212 struct pim_interface
*pim_ifp
;
219 assertf(pim_ifp
->mroute_vif_index
>= 0,
220 "trying to del OIF %s with VIF (%d)", oif
->name
,
221 pim_ifp
->mroute_vif_index
);
224 * Don't do anything if we've been asked to remove a source
225 * that is not actually on it.
227 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
228 if (PIM_DEBUG_MROUTE
) {
230 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
231 __FILE__
, __func__
, proto_mask
,
233 ->oif_flags
[pim_ifp
->mroute_vif_index
],
234 oif
->name
, pim_ifp
->mroute_vif_index
,
235 oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
),
236 oil_origin(channel_oil
),
237 oil_mcastgrp(channel_oil
));
242 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
244 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &
245 PIM_OIF_FLAG_PROTO_ANY
) {
246 if (PIM_DEBUG_MROUTE
) {
248 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
249 __FILE__
, __func__
, oif
->name
,
250 pim_ifp
->mroute_vif_index
,
251 oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
),
252 oil_origin(channel_oil
),
253 oil_mcastgrp(channel_oil
));
258 oil_if_set(channel_oil
, pim_ifp
->mroute_vif_index
, 0);
259 /* clear mute; will be re-evaluated when the OIF becomes valid again */
260 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~PIM_OIF_FLAG_MUTE
;
262 if (pim_upstream_mroute_add(channel_oil
, __func__
)) {
263 if (PIM_DEBUG_MROUTE
) {
265 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)",
266 __FILE__
, __func__
, oif
->name
,
267 pim_ifp
->mroute_vif_index
,
268 oil_origin(channel_oil
),
269 oil_mcastgrp(channel_oil
));
274 --channel_oil
->oil_size
;
276 if (PIM_DEBUG_MROUTE
) {
278 "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
279 __func__
, caller
, oil_origin(channel_oil
),
280 oil_mcastgrp(channel_oil
),
282 *oil_parent(channel_oil
), oif
->name
,
283 pim_ifp
->mroute_vif_index
);
289 void pim_channel_del_inherited_oif(struct channel_oil
*c_oil
,
290 struct interface
*oif
, const char *caller
)
292 struct pim_upstream
*up
= c_oil
->up
;
294 pim_channel_del_oif(c_oil
, oif
, PIM_OIF_FLAG_PROTO_STAR
,
297 /* if an inherited OIF is being removed join-desired can change
298 * if the inherited OIL is now empty and KAT is running
300 if (up
&& !pim_addr_is_any(up
->sg
.src
) &&
301 pim_upstream_empty_inherited_olist(up
))
302 pim_upstream_update_join_desired(up
->pim
, up
);
305 static bool pim_channel_eval_oif_mute(struct channel_oil
*c_oil
,
306 struct pim_interface
*pim_ifp
)
308 struct pim_interface
*pim_reg_ifp
;
309 struct pim_interface
*vxlan_ifp
;
310 bool do_mute
= false;
311 struct pim_instance
*pim
= c_oil
->pim
;
316 pim_reg_ifp
= pim
->regiface
->info
;
317 if (pim_ifp
== pim_reg_ifp
) {
318 /* suppress pimreg in the OIL if the mroute is not supposed to
319 * trigger register encapsulated data
321 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil
->up
->flags
))
327 vxlan_ifp
= pim_vxlan_get_term_ifp(pim
);
328 if (pim_ifp
== vxlan_ifp
) {
329 /* 1. vxlan termination device must never be added to the
330 * origination mroute (and that can actually happen because
331 * of XG inheritance from the termination mroute) otherwise
332 * traffic will end up looping.
333 * PS: This check has also been extended to non-orig mroutes
334 * that have a local SIP as such mroutes can move back and
335 * forth between orig<=>non-orig type.
336 * 2. vxlan termination device should be removed from the non-DF
337 * to prevent duplicates to the overlay rxer
339 if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil
->up
->flags
) ||
340 PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
) ||
341 pim_vxlan_is_local_sip(c_oil
->up
))
347 if (PIM_I_am_DualActive(pim_ifp
)) {
348 struct pim_upstream
*starup
= c_oil
->up
->parent
;
349 if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(c_oil
->up
->flags
)
350 && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
)))
353 /* In case entry is (S,G), Negotiation happens at (*.G) */
356 && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(starup
->flags
)
357 && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(starup
->flags
)))
364 void pim_channel_update_oif_mute(struct channel_oil
*c_oil
,
365 struct pim_interface
*pim_ifp
)
370 /* If pim_ifp is not a part of the OIL there is nothing to do */
371 if (!oil_if_has(c_oil
, pim_ifp
->mroute_vif_index
))
374 old_mute
= !!(c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &
376 new_mute
= pim_channel_eval_oif_mute(c_oil
, pim_ifp
);
377 if (old_mute
== new_mute
)
381 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
384 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
387 pim_upstream_mroute_add(c_oil
, __func__
);
390 /* pim_upstream has been set or cleared on the c_oil. re-eval mute state
391 * on all existing OIFs
393 static void pim_channel_update_mute(struct channel_oil
*c_oil
)
395 struct pim_interface
*pim_reg_ifp
;
396 struct pim_interface
*vxlan_ifp
;
398 if (c_oil
->pim
->regiface
) {
399 pim_reg_ifp
= c_oil
->pim
->regiface
->info
;
401 pim_channel_update_oif_mute(c_oil
, pim_reg_ifp
);
403 vxlan_ifp
= pim_vxlan_get_term_ifp(c_oil
->pim
);
405 pim_channel_update_oif_mute(c_oil
, vxlan_ifp
);
408 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
409 uint32_t proto_mask
, const char *caller
)
411 struct pim_interface
*pim_ifp
;
415 * If we've gotten here we've gone bad, but let's
419 zlog_warn("Attempt to Add OIF for non-existent channel oil");
425 assertf(pim_ifp
->mroute_vif_index
>= 0,
426 "trying to add OIF %s with VIF (%d)", oif
->name
,
427 pim_ifp
->mroute_vif_index
);
429 /* Prevent single protocol from subscribing same interface to
430 channel (S,G) multiple times */
431 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
432 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
434 if (PIM_DEBUG_MROUTE
) {
436 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
437 __FILE__
, __func__
, proto_mask
, oif
->name
,
438 pim_ifp
->mroute_vif_index
,
439 oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
),
440 oil_origin(channel_oil
),
441 oil_mcastgrp(channel_oil
));
446 /* Allow other protocol to request subscription of same interface to
447 * channel (S,G), we need to note this information
449 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
450 & PIM_OIF_FLAG_PROTO_ANY
) {
452 /* Updating time here is not required as this time has to
453 * indicate when the interface is added
456 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
457 /* Check the OIF really exists before returning, and only log
459 if (oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
) < 1) {
461 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%pPAs,%pPAs)",
462 __FILE__
, __func__
, proto_mask
, oif
->name
,
463 pim_ifp
->mroute_vif_index
,
464 oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
),
465 oil_origin(channel_oil
),
466 oil_mcastgrp(channel_oil
));
469 if (PIM_DEBUG_MROUTE
) {
471 "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d added to 0x%x",
472 __func__
, caller
, oil_origin(channel_oil
),
473 oil_mcastgrp(channel_oil
),
474 proto_mask
, oif
->name
,
475 pim_ifp
->mroute_vif_index
,
477 ->oif_flags
[pim_ifp
->mroute_vif_index
]);
482 old_ttl
= oil_if_has(channel_oil
, pim_ifp
->mroute_vif_index
);
485 if (PIM_DEBUG_MROUTE
) {
487 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%pPAs,%pPAs)",
488 __FILE__
, __func__
, oif
->name
,
489 pim_ifp
->mroute_vif_index
,
490 oil_origin(channel_oil
),
491 oil_mcastgrp(channel_oil
));
496 oil_if_set(channel_oil
, pim_ifp
->mroute_vif_index
, PIM_MROUTE_MIN_TTL
);
498 /* Some OIFs are held in a muted state i.e. the PIM state machine
499 * decided to include the OIF but additional status check such as
500 * MLAG DF role prevent it from being activated for traffic
503 if (pim_channel_eval_oif_mute(channel_oil
, pim_ifp
))
504 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
507 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
510 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
511 * valid to get installed in kernel.
513 if (*oil_parent(channel_oil
) != MAXVIFS
) {
514 if (pim_upstream_mroute_add(channel_oil
, __func__
)) {
515 if (PIM_DEBUG_MROUTE
) {
517 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%pPAs,%pPAs)",
518 __FILE__
, __func__
, oif
->name
,
519 pim_ifp
->mroute_vif_index
,
520 oil_origin(channel_oil
),
521 oil_mcastgrp(channel_oil
));
524 oil_if_set(channel_oil
, pim_ifp
->mroute_vif_index
,
530 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
531 pim_time_monotonic_sec();
532 ++channel_oil
->oil_size
;
533 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
535 if (PIM_DEBUG_MROUTE
) {
537 "%s(%s): (S,G)=(%pPAs,%pPAs): proto_mask=%u OIF=%s vif_index=%d: DONE",
538 __func__
, caller
, oil_origin(channel_oil
),
539 oil_mcastgrp(channel_oil
),
541 oif
->name
, pim_ifp
->mroute_vif_index
);
547 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
549 static struct channel_oil null_oil
;
554 /* exclude pimreg from the OIL when checking if the inherited_oil is
556 * pimreg device (in all vrfs) uses a vifi of
557 * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
558 if (oil_if_has(c_oil
, 0))
559 oil_if_set(&null_oil
, 0, 1);
561 oil_if_set(&null_oil
, 0, 0);
563 return !oil_if_cmp(&c_oil
->oil
, &null_oil
.oil
);