]>
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 // struct list *pim_channel_oil_list = NULL;
37 // struct hash *pim_channel_oil_hash = NULL;
39 static void pim_channel_update_mute(struct channel_oil
*c_oil
);
41 char *pim_channel_oil_dump(struct channel_oil
*c_oil
, char *buf
, size_t size
)
44 struct interface
*ifp
;
48 sg
.src
= c_oil
->oil
.mfcc_origin
;
49 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
50 ifp
= pim_if_find_by_vif_index(c_oil
->pim
, c_oil
->oil
.mfcc_parent
);
51 snprintf(buf
, size
, "%s IIF: %s, OIFS: ", pim_str_sg_dump(&sg
),
52 ifp
? ifp
->name
: "(?)");
54 out
= buf
+ strlen(buf
);
55 for (i
= 0; i
< MAXVIFS
; i
++) {
56 if (c_oil
->oil
.mfcc_ttls
[i
] != 0) {
57 ifp
= pim_if_find_by_vif_index(c_oil
->pim
, i
);
58 snprintf(out
, buf
+ size
- out
, "%s ",
59 ifp
? ifp
->name
: "(?)");
67 int pim_channel_oil_compare(const struct channel_oil
*c1
,
68 const struct channel_oil
*c2
)
70 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
71 < ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
74 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
75 > ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
78 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
79 < ntohl(c2
->oil
.mfcc_origin
.s_addr
))
82 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
83 > ntohl(c2
->oil
.mfcc_origin
.s_addr
))
89 void pim_oil_init(struct pim_instance
*pim
)
91 rb_pim_oil_init(&pim
->channel_oil_head
);
94 void pim_oil_terminate(struct pim_instance
*pim
)
96 struct channel_oil
*c_oil
;
98 while ((c_oil
= rb_pim_oil_pop(&pim
->channel_oil_head
)))
99 pim_channel_oil_free(c_oil
);
101 rb_pim_oil_fini(&pim
->channel_oil_head
);
104 void pim_channel_oil_free(struct channel_oil
*c_oil
)
106 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
109 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
110 struct prefix_sg
*sg
)
112 struct channel_oil
*c_oil
= NULL
;
113 struct channel_oil lookup
;
115 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
116 lookup
.oil
.mfcc_origin
= sg
->src
;
118 c_oil
= rb_pim_oil_find(&pim
->channel_oil_head
, &lookup
);
123 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
124 struct prefix_sg
*sg
,
127 struct channel_oil
*c_oil
;
129 c_oil
= pim_find_channel_oil(pim
, sg
);
131 ++c_oil
->oil_ref_count
;
134 /* channel might be present prior to upstream */
135 c_oil
->up
= pim_upstream_find(
137 /* if the upstream entry is being anchored to an
138 * already existing channel OIL we need to re-evaluate
139 * the "Mute" state on AA OIFs
141 pim_channel_update_mute(c_oil
);
144 /* check if the IIF has changed
145 * XXX - is this really needed
147 pim_upstream_mroute_iif_update(c_oil
, __func__
);
149 if (PIM_DEBUG_MROUTE
)
151 "%s(%s): Existing oil for %pSG4 Ref Count: %d (Post Increment)",
152 __PRETTY_FUNCTION__
, name
, sg
,
153 c_oil
->oil_ref_count
);
157 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
159 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
160 c_oil
->oil
.mfcc_origin
= sg
->src
;
162 c_oil
->oil
.mfcc_parent
= MAXVIFS
;
163 c_oil
->oil_ref_count
= 1;
164 c_oil
->installed
= 0;
165 c_oil
->up
= pim_upstream_find(pim
, sg
);
168 rb_pim_oil_add(&pim
->channel_oil_head
, c_oil
);
170 if (PIM_DEBUG_MROUTE
)
171 zlog_debug("%s(%s): c_oil %s add",
172 __func__
, name
, pim_str_sg_dump(sg
));
177 struct channel_oil
*pim_channel_oil_del(struct channel_oil
*c_oil
,
180 if (PIM_DEBUG_MROUTE
) {
181 struct prefix_sg sg
= {.src
= c_oil
->oil
.mfcc_mcastgrp
,
182 .grp
= c_oil
->oil
.mfcc_origin
};
185 "%s(%s): Del oil for %pSG4, Ref Count: %d (Predecrement)",
186 __PRETTY_FUNCTION__
, 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
;
226 zassert(channel_oil
);
232 * Don't do anything if we've been asked to remove a source
233 * that is not actually on it.
235 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
236 if (PIM_DEBUG_MROUTE
) {
237 char group_str
[INET_ADDRSTRLEN
];
238 char source_str
[INET_ADDRSTRLEN
];
239 pim_inet4_dump("<group?>",
240 channel_oil
->oil
.mfcc_mcastgrp
,
241 group_str
, sizeof(group_str
));
242 pim_inet4_dump("<source?>",
243 channel_oil
->oil
.mfcc_origin
, source_str
,
246 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
247 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
249 ->oif_flags
[pim_ifp
->mroute_vif_index
],
250 oif
->name
, pim_ifp
->mroute_vif_index
,
252 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
253 source_str
, group_str
);
258 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
260 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &
261 PIM_OIF_FLAG_PROTO_ANY
) {
262 if (PIM_DEBUG_MROUTE
) {
263 char group_str
[INET_ADDRSTRLEN
];
264 char source_str
[INET_ADDRSTRLEN
];
265 pim_inet4_dump("<group?>",
266 channel_oil
->oil
.mfcc_mcastgrp
,
267 group_str
, sizeof(group_str
));
268 pim_inet4_dump("<source?>",
269 channel_oil
->oil
.mfcc_origin
, source_str
,
272 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
273 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
274 pim_ifp
->mroute_vif_index
,
276 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
277 source_str
, group_str
);
282 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
283 /* clear mute; will be re-evaluated when the OIF becomes valid again */
284 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~PIM_OIF_FLAG_MUTE
;
286 if (pim_upstream_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
287 if (PIM_DEBUG_MROUTE
) {
288 char group_str
[INET_ADDRSTRLEN
];
289 char source_str
[INET_ADDRSTRLEN
];
290 pim_inet4_dump("<group?>",
291 channel_oil
->oil
.mfcc_mcastgrp
,
292 group_str
, sizeof(group_str
));
293 pim_inet4_dump("<source?>",
294 channel_oil
->oil
.mfcc_origin
, source_str
,
297 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
298 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
299 pim_ifp
->mroute_vif_index
, source_str
,
305 --channel_oil
->oil_size
;
307 if (PIM_DEBUG_MROUTE
) {
308 char group_str
[INET_ADDRSTRLEN
];
309 char source_str
[INET_ADDRSTRLEN
];
310 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
311 group_str
, sizeof(group_str
));
312 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
313 source_str
, sizeof(source_str
));
315 "%s(%s): (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
316 __PRETTY_FUNCTION__
, caller
, source_str
, group_str
,
317 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
318 pim_ifp
->mroute_vif_index
);
324 void pim_channel_del_inherited_oif(struct channel_oil
*c_oil
,
325 struct interface
*oif
, const char *caller
)
327 struct pim_upstream
*up
= c_oil
->up
;
329 pim_channel_del_oif(c_oil
, oif
, PIM_OIF_FLAG_PROTO_STAR
,
332 /* if an inherited OIF is being removed join-desired can change
333 * if the inherited OIL is now empty and KAT is running
335 if (up
&& up
->sg
.src
.s_addr
!= INADDR_ANY
&&
336 pim_upstream_empty_inherited_olist(up
))
337 pim_upstream_update_join_desired(up
->pim
, up
);
340 static bool pim_channel_eval_oif_mute(struct channel_oil
*c_oil
,
341 struct pim_interface
*pim_ifp
)
343 struct pim_interface
*pim_reg_ifp
;
344 struct pim_interface
*vxlan_ifp
;
345 bool do_mute
= false;
346 struct pim_instance
*pim
= c_oil
->pim
;
351 pim_reg_ifp
= pim
->regiface
->info
;
352 if (pim_ifp
== pim_reg_ifp
) {
353 /* suppress pimreg in the OIL if the mroute is not supposed to
354 * trigger register encapsulated data
356 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil
->up
->flags
))
362 vxlan_ifp
= pim_vxlan_get_term_ifp(pim
);
363 if (pim_ifp
== vxlan_ifp
) {
364 /* 1. vxlan termination device must never be added to the
365 * origination mroute (and that can actually happen because
366 * of XG inheritance from the termination mroute) otherwise
367 * traffic will end up looping.
368 * PS: This check has also been extended to non-orig mroutes
369 * that have a local SIP as such mroutes can move back and
370 * forth between orig<=>non-orig type.
371 * 2. vxlan termination device should be removed from the non-DF
372 * to prevent duplicates to the overlay rxer
374 if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil
->up
->flags
) ||
375 PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
) ||
376 pim_vxlan_is_local_sip(c_oil
->up
))
385 void pim_channel_update_oif_mute(struct channel_oil
*c_oil
,
386 struct pim_interface
*pim_ifp
)
391 /* If pim_ifp is not a part of the OIL there is nothing to do */
392 if (!c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
395 old_mute
= !!(c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &
397 new_mute
= pim_channel_eval_oif_mute(c_oil
, pim_ifp
);
398 if (old_mute
== new_mute
)
402 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
405 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
408 pim_upstream_mroute_add(c_oil
, __PRETTY_FUNCTION__
);
411 /* pim_upstream has been set or cleared on the c_oil. re-eval mute state
412 * on all existing OIFs
414 static void pim_channel_update_mute(struct channel_oil
*c_oil
)
416 struct pim_interface
*pim_reg_ifp
;
417 struct pim_interface
*vxlan_ifp
;
419 pim_reg_ifp
= c_oil
->pim
->regiface
->info
;
421 pim_channel_update_oif_mute(c_oil
, pim_reg_ifp
);
422 vxlan_ifp
= pim_vxlan_get_term_ifp(c_oil
->pim
);
424 pim_channel_update_oif_mute(c_oil
, vxlan_ifp
);
427 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
428 uint32_t proto_mask
, const char *caller
)
430 struct pim_interface
*pim_ifp
;
434 * If we've gotten here we've gone bad, but let's
438 zlog_warn("Attempt to Add OIF for non-existent channel oil");
444 /* Prevent single protocol from subscribing same interface to
445 channel (S,G) multiple times */
446 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
447 if (PIM_DEBUG_MROUTE
) {
448 char group_str
[INET_ADDRSTRLEN
];
449 char source_str
[INET_ADDRSTRLEN
];
450 pim_inet4_dump("<group?>",
451 channel_oil
->oil
.mfcc_mcastgrp
,
452 group_str
, sizeof(group_str
));
453 pim_inet4_dump("<source?>",
454 channel_oil
->oil
.mfcc_origin
, source_str
,
457 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
458 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
459 oif
->name
, pim_ifp
->mroute_vif_index
,
461 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
462 source_str
, group_str
);
467 /* Allow other protocol to request subscription of same interface to
468 * channel (S,G), we need to note this information
470 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
471 & PIM_OIF_FLAG_PROTO_ANY
) {
473 /* Updating time here is not required as this time has to
474 * indicate when the interface is added
477 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
478 /* Check the OIF really exists before returning, and only log
480 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
482 char group_str
[INET_ADDRSTRLEN
];
483 char source_str
[INET_ADDRSTRLEN
];
484 pim_inet4_dump("<group?>",
485 channel_oil
->oil
.mfcc_mcastgrp
,
486 group_str
, sizeof(group_str
));
487 pim_inet4_dump("<source?>",
488 channel_oil
->oil
.mfcc_origin
,
489 source_str
, sizeof(source_str
));
491 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
492 __FILE__
, __PRETTY_FUNCTION__
,
493 proto_mask
, oif
->name
,
494 pim_ifp
->mroute_vif_index
,
495 channel_oil
->oil
.mfcc_ttls
496 [pim_ifp
->mroute_vif_index
],
497 source_str
, group_str
);
504 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
507 if (PIM_DEBUG_MROUTE
) {
508 char group_str
[INET_ADDRSTRLEN
];
509 char source_str
[INET_ADDRSTRLEN
];
510 pim_inet4_dump("<group?>",
511 channel_oil
->oil
.mfcc_mcastgrp
,
512 group_str
, sizeof(group_str
));
513 pim_inet4_dump("<source?>",
514 channel_oil
->oil
.mfcc_origin
, source_str
,
517 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
518 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
519 pim_ifp
->mroute_vif_index
, source_str
,
525 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
528 /* Some OIFs are held in a muted state i.e. the PIM state machine
529 * decided to include the OIF but additional status check such as
530 * MLAG DF role prevent it from being activated for traffic
533 if (pim_channel_eval_oif_mute(channel_oil
, pim_ifp
))
534 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
537 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
540 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
541 * valid to get installed in kernel.
543 if (channel_oil
->oil
.mfcc_parent
!= MAXVIFS
) {
544 if (pim_upstream_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
545 if (PIM_DEBUG_MROUTE
) {
546 char group_str
[INET_ADDRSTRLEN
];
547 char source_str
[INET_ADDRSTRLEN
];
548 pim_inet4_dump("<group?>",
549 channel_oil
->oil
.mfcc_mcastgrp
,
550 group_str
, sizeof(group_str
));
551 pim_inet4_dump("<source?>",
552 channel_oil
->oil
.mfcc_origin
, source_str
,
555 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
556 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
557 pim_ifp
->mroute_vif_index
, source_str
,
561 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
]
567 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
568 pim_time_monotonic_sec();
569 ++channel_oil
->oil_size
;
570 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
572 if (PIM_DEBUG_MROUTE
) {
573 char group_str
[INET_ADDRSTRLEN
];
574 char source_str
[INET_ADDRSTRLEN
];
575 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
576 group_str
, sizeof(group_str
));
577 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
578 source_str
, sizeof(source_str
));
580 "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
581 __PRETTY_FUNCTION__
, caller
, source_str
, group_str
,
582 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
588 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
590 static struct mfcctl null_oil
;
595 /* exclude pimreg from the OIL when checking if the inherited_oil is
597 * pimreg device (in all vrfs) uses a vifi of
598 * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
599 return !memcmp(&c_oil
->oil
.mfcc_ttls
[1], &null_oil
.mfcc_ttls
[1],
600 sizeof(null_oil
.mfcc_ttls
) - sizeof(null_oil
.mfcc_ttls
[0]));