]>
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
= c_oil
->oil
.mfcc_origin
;
46 sg
.grp
= c_oil
->oil
.mfcc_mcastgrp
;
47 ifp
= pim_if_find_by_vif_index(c_oil
->pim
, c_oil
->oil
.mfcc_parent
);
48 snprintf(buf
, size
, "%s IIF: %s, OIFS: ", pim_str_sg_dump(&sg
),
49 ifp
? ifp
->name
: "(?)");
51 out
= buf
+ strlen(buf
);
52 for (i
= 0; i
< MAXVIFS
; i
++) {
53 if (c_oil
->oil
.mfcc_ttls
[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
*c1
,
65 const struct channel_oil
*c2
)
67 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
68 < ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
71 if (ntohl(c1
->oil
.mfcc_mcastgrp
.s_addr
)
72 > ntohl(c2
->oil
.mfcc_mcastgrp
.s_addr
))
75 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
76 < ntohl(c2
->oil
.mfcc_origin
.s_addr
))
79 if (ntohl(c1
->oil
.mfcc_origin
.s_addr
)
80 > ntohl(c2
->oil
.mfcc_origin
.s_addr
))
86 void pim_oil_init(struct pim_instance
*pim
)
88 rb_pim_oil_init(&pim
->channel_oil_head
);
91 void pim_oil_terminate(struct pim_instance
*pim
)
93 struct channel_oil
*c_oil
;
95 while ((c_oil
= rb_pim_oil_pop(&pim
->channel_oil_head
)))
96 pim_channel_oil_free(c_oil
);
98 rb_pim_oil_fini(&pim
->channel_oil_head
);
101 void pim_channel_oil_free(struct channel_oil
*c_oil
)
103 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
106 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
107 struct prefix_sg
*sg
)
109 struct channel_oil
*c_oil
= NULL
;
110 struct channel_oil lookup
;
112 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
113 lookup
.oil
.mfcc_origin
= sg
->src
;
115 c_oil
= rb_pim_oil_find(&pim
->channel_oil_head
, &lookup
);
120 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
121 struct prefix_sg
*sg
,
124 struct channel_oil
*c_oil
;
126 c_oil
= pim_find_channel_oil(pim
, sg
);
128 ++c_oil
->oil_ref_count
;
131 /* channel might be present prior to upstream */
132 c_oil
->up
= pim_upstream_find(
134 /* if the upstream entry is being anchored to an
135 * already existing channel OIL we need to re-evaluate
136 * the "Mute" state on AA OIFs
138 pim_channel_update_mute(c_oil
);
141 /* check if the IIF has changed
142 * XXX - is this really needed
144 pim_upstream_mroute_iif_update(c_oil
, __func__
);
146 if (PIM_DEBUG_MROUTE
)
148 "%s(%s): Existing oil for %pSG4 Ref Count: %d (Post Increment)",
149 __func__
, name
, sg
, c_oil
->oil_ref_count
);
153 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
155 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
156 c_oil
->oil
.mfcc_origin
= sg
->src
;
158 c_oil
->oil
.mfcc_parent
= MAXVIFS
;
159 c_oil
->oil_ref_count
= 1;
160 c_oil
->installed
= 0;
161 c_oil
->up
= pim_upstream_find(pim
, sg
);
164 rb_pim_oil_add(&pim
->channel_oil_head
, c_oil
);
166 if (PIM_DEBUG_MROUTE
)
167 zlog_debug("%s(%s): c_oil %s add",
168 __func__
, name
, pim_str_sg_dump(sg
));
173 struct channel_oil
*pim_channel_oil_del(struct channel_oil
*c_oil
,
176 if (PIM_DEBUG_MROUTE
) {
177 struct prefix_sg sg
= {.src
= c_oil
->oil
.mfcc_mcastgrp
,
178 .grp
= c_oil
->oil
.mfcc_origin
};
181 "%s(%s): Del oil for %pSG4, Ref Count: %d (Predecrement)",
182 __func__
, name
, &sg
, c_oil
->oil_ref_count
);
184 --c_oil
->oil_ref_count
;
186 if (c_oil
->oil_ref_count
< 1) {
188 * notice that listnode_delete() can't be moved
189 * into pim_channel_oil_free() because the later is
190 * called by list_delete_all_node()
193 rb_pim_oil_del(&c_oil
->pim
->channel_oil_head
, c_oil
);
195 pim_channel_oil_free(c_oil
);
202 void pim_channel_oil_upstream_deref(struct channel_oil
*c_oil
)
204 /* The upstream entry associated with a channel_oil is abt to be
205 * deleted. If the channel_oil is kept around because of other
206 * references we need to remove upstream based states out of it.
208 c_oil
= pim_channel_oil_del(c_oil
, __func__
);
210 /* note: here we assume that c_oil->up has already been
213 pim_channel_update_mute(c_oil
);
217 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
218 uint32_t proto_mask
, const char *caller
)
220 struct pim_interface
*pim_ifp
;
222 zassert(channel_oil
);
228 * Don't do anything if we've been asked to remove a source
229 * that is not actually on it.
231 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
232 if (PIM_DEBUG_MROUTE
) {
233 char group_str
[INET_ADDRSTRLEN
];
234 char source_str
[INET_ADDRSTRLEN
];
235 pim_inet4_dump("<group?>",
236 channel_oil
->oil
.mfcc_mcastgrp
,
237 group_str
, sizeof(group_str
));
238 pim_inet4_dump("<source?>",
239 channel_oil
->oil
.mfcc_origin
, source_str
,
242 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
243 __FILE__
, __func__
, proto_mask
,
245 ->oif_flags
[pim_ifp
->mroute_vif_index
],
246 oif
->name
, pim_ifp
->mroute_vif_index
,
248 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
249 source_str
, group_str
);
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
) {
259 char group_str
[INET_ADDRSTRLEN
];
260 char source_str
[INET_ADDRSTRLEN
];
261 pim_inet4_dump("<group?>",
262 channel_oil
->oil
.mfcc_mcastgrp
,
263 group_str
, sizeof(group_str
));
264 pim_inet4_dump("<source?>",
265 channel_oil
->oil
.mfcc_origin
, source_str
,
268 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
269 __FILE__
, __func__
, oif
->name
,
270 pim_ifp
->mroute_vif_index
,
272 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
273 source_str
, group_str
);
278 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
279 /* clear mute; will be re-evaluated when the OIF becomes valid again */
280 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~PIM_OIF_FLAG_MUTE
;
282 if (pim_upstream_mroute_add(channel_oil
, __func__
)) {
283 if (PIM_DEBUG_MROUTE
) {
284 char group_str
[INET_ADDRSTRLEN
];
285 char source_str
[INET_ADDRSTRLEN
];
286 pim_inet4_dump("<group?>",
287 channel_oil
->oil
.mfcc_mcastgrp
,
288 group_str
, sizeof(group_str
));
289 pim_inet4_dump("<source?>",
290 channel_oil
->oil
.mfcc_origin
, source_str
,
293 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
294 __FILE__
, __func__
, oif
->name
,
295 pim_ifp
->mroute_vif_index
, source_str
,
301 --channel_oil
->oil_size
;
303 if (PIM_DEBUG_MROUTE
) {
304 char group_str
[INET_ADDRSTRLEN
];
305 char source_str
[INET_ADDRSTRLEN
];
306 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
307 group_str
, sizeof(group_str
));
308 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
309 source_str
, sizeof(source_str
));
311 "%s(%s): (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
312 __func__
, caller
, source_str
, group_str
, proto_mask
,
313 channel_oil
->oil
.mfcc_parent
, oif
->name
,
314 pim_ifp
->mroute_vif_index
);
320 void pim_channel_del_inherited_oif(struct channel_oil
*c_oil
,
321 struct interface
*oif
, const char *caller
)
323 struct pim_upstream
*up
= c_oil
->up
;
325 pim_channel_del_oif(c_oil
, oif
, PIM_OIF_FLAG_PROTO_STAR
,
328 /* if an inherited OIF is being removed join-desired can change
329 * if the inherited OIL is now empty and KAT is running
331 if (up
&& up
->sg
.src
.s_addr
!= INADDR_ANY
&&
332 pim_upstream_empty_inherited_olist(up
))
333 pim_upstream_update_join_desired(up
->pim
, up
);
336 static bool pim_channel_eval_oif_mute(struct channel_oil
*c_oil
,
337 struct pim_interface
*pim_ifp
)
339 struct pim_interface
*pim_reg_ifp
;
340 struct pim_interface
*vxlan_ifp
;
341 bool do_mute
= false;
342 struct pim_instance
*pim
= c_oil
->pim
;
347 pim_reg_ifp
= pim
->regiface
->info
;
348 if (pim_ifp
== pim_reg_ifp
) {
349 /* suppress pimreg in the OIL if the mroute is not supposed to
350 * trigger register encapsulated data
352 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil
->up
->flags
))
358 vxlan_ifp
= pim_vxlan_get_term_ifp(pim
);
359 if (pim_ifp
== vxlan_ifp
) {
360 /* 1. vxlan termination device must never be added to the
361 * origination mroute (and that can actually happen because
362 * of XG inheritance from the termination mroute) otherwise
363 * traffic will end up looping.
364 * PS: This check has also been extended to non-orig mroutes
365 * that have a local SIP as such mroutes can move back and
366 * forth between orig<=>non-orig type.
367 * 2. vxlan termination device should be removed from the non-DF
368 * to prevent duplicates to the overlay rxer
370 if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil
->up
->flags
) ||
371 PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
) ||
372 pim_vxlan_is_local_sip(c_oil
->up
))
378 if (PIM_I_am_DualActive(pim_ifp
)) {
379 struct pim_upstream
*starup
= c_oil
->up
->parent
;
380 if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(c_oil
->up
->flags
)
381 && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
)))
384 /* In case entry is (S,G), Negotiation happens at (*.G) */
387 && PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(starup
->flags
)
388 && (PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(starup
->flags
)))
395 void pim_channel_update_oif_mute(struct channel_oil
*c_oil
,
396 struct pim_interface
*pim_ifp
)
401 /* If pim_ifp is not a part of the OIL there is nothing to do */
402 if (!c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
405 old_mute
= !!(c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &
407 new_mute
= pim_channel_eval_oif_mute(c_oil
, pim_ifp
);
408 if (old_mute
== new_mute
)
412 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
415 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
418 pim_upstream_mroute_add(c_oil
, __func__
);
421 /* pim_upstream has been set or cleared on the c_oil. re-eval mute state
422 * on all existing OIFs
424 static void pim_channel_update_mute(struct channel_oil
*c_oil
)
426 struct pim_interface
*pim_reg_ifp
;
427 struct pim_interface
*vxlan_ifp
;
429 pim_reg_ifp
= c_oil
->pim
->regiface
->info
;
431 pim_channel_update_oif_mute(c_oil
, pim_reg_ifp
);
432 vxlan_ifp
= pim_vxlan_get_term_ifp(c_oil
->pim
);
434 pim_channel_update_oif_mute(c_oil
, vxlan_ifp
);
437 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
438 uint32_t proto_mask
, const char *caller
)
440 struct pim_interface
*pim_ifp
;
444 * If we've gotten here we've gone bad, but let's
448 zlog_warn("Attempt to Add OIF for non-existent channel oil");
454 /* Prevent single protocol from subscribing same interface to
455 channel (S,G) multiple times */
456 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
457 if (PIM_DEBUG_MROUTE
) {
458 char group_str
[INET_ADDRSTRLEN
];
459 char source_str
[INET_ADDRSTRLEN
];
460 pim_inet4_dump("<group?>",
461 channel_oil
->oil
.mfcc_mcastgrp
,
462 group_str
, sizeof(group_str
));
463 pim_inet4_dump("<source?>",
464 channel_oil
->oil
.mfcc_origin
, source_str
,
467 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
468 __FILE__
, __func__
, proto_mask
, oif
->name
,
469 pim_ifp
->mroute_vif_index
,
471 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
472 source_str
, group_str
);
477 /* Allow other protocol to request subscription of same interface to
478 * channel (S,G), we need to note this information
480 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
481 & PIM_OIF_FLAG_PROTO_ANY
) {
483 /* Updating time here is not required as this time has to
484 * indicate when the interface is added
487 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
488 /* Check the OIF really exists before returning, and only log
490 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
492 char group_str
[INET_ADDRSTRLEN
];
493 char source_str
[INET_ADDRSTRLEN
];
494 pim_inet4_dump("<group?>",
495 channel_oil
->oil
.mfcc_mcastgrp
,
496 group_str
, sizeof(group_str
));
497 pim_inet4_dump("<source?>",
498 channel_oil
->oil
.mfcc_origin
,
499 source_str
, sizeof(source_str
));
501 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
502 __FILE__
, __func__
, proto_mask
,
503 oif
->name
, pim_ifp
->mroute_vif_index
,
504 channel_oil
->oil
.mfcc_ttls
505 [pim_ifp
->mroute_vif_index
],
506 source_str
, group_str
);
510 if (PIM_DEBUG_MROUTE
) {
511 char group_str
[INET_ADDRSTRLEN
];
512 char source_str
[INET_ADDRSTRLEN
];
513 pim_inet4_dump("<group?>",
514 channel_oil
->oil
.mfcc_mcastgrp
,
515 group_str
, sizeof(group_str
));
516 pim_inet4_dump("<source?>",
517 channel_oil
->oil
.mfcc_origin
, source_str
,
520 "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d added to 0x%x",
521 __func__
, caller
, source_str
, group_str
,
522 proto_mask
, oif
->name
,
523 pim_ifp
->mroute_vif_index
,
525 ->oif_flags
[pim_ifp
->mroute_vif_index
]);
530 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
533 if (PIM_DEBUG_MROUTE
) {
534 char group_str
[INET_ADDRSTRLEN
];
535 char source_str
[INET_ADDRSTRLEN
];
536 pim_inet4_dump("<group?>",
537 channel_oil
->oil
.mfcc_mcastgrp
,
538 group_str
, sizeof(group_str
));
539 pim_inet4_dump("<source?>",
540 channel_oil
->oil
.mfcc_origin
, source_str
,
543 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
544 __FILE__
, __func__
, oif
->name
,
545 pim_ifp
->mroute_vif_index
, source_str
,
551 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
554 /* Some OIFs are held in a muted state i.e. the PIM state machine
555 * decided to include the OIF but additional status check such as
556 * MLAG DF role prevent it from being activated for traffic
559 if (pim_channel_eval_oif_mute(channel_oil
, pim_ifp
))
560 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
563 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
566 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
567 * valid to get installed in kernel.
569 if (channel_oil
->oil
.mfcc_parent
!= MAXVIFS
) {
570 if (pim_upstream_mroute_add(channel_oil
, __func__
)) {
571 if (PIM_DEBUG_MROUTE
) {
572 char group_str
[INET_ADDRSTRLEN
];
573 char source_str
[INET_ADDRSTRLEN
];
574 pim_inet4_dump("<group?>",
575 channel_oil
->oil
.mfcc_mcastgrp
,
576 group_str
, sizeof(group_str
));
577 pim_inet4_dump("<source?>",
578 channel_oil
->oil
.mfcc_origin
, source_str
,
581 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
582 __FILE__
, __func__
, oif
->name
,
583 pim_ifp
->mroute_vif_index
, source_str
,
587 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
]
593 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
594 pim_time_monotonic_sec();
595 ++channel_oil
->oil_size
;
596 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
598 if (PIM_DEBUG_MROUTE
) {
599 char group_str
[INET_ADDRSTRLEN
];
600 char source_str
[INET_ADDRSTRLEN
];
601 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
602 group_str
, sizeof(group_str
));
603 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
604 source_str
, sizeof(source_str
));
606 "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
607 __func__
, caller
, source_str
, group_str
, proto_mask
,
608 oif
->name
, pim_ifp
->mroute_vif_index
);
614 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
616 static struct mfcctl null_oil
;
621 /* exclude pimreg from the OIL when checking if the inherited_oil is
623 * pimreg device (in all vrfs) uses a vifi of
624 * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
625 return !memcmp(&c_oil
->oil
.mfcc_ttls
[1], &null_oil
.mfcc_ttls
[1],
626 sizeof(null_oil
.mfcc_ttls
) - sizeof(null_oil
.mfcc_ttls
[0]));