]>
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 static bool pim_oil_equal(const void *arg1
, const void *arg2
)
91 const struct channel_oil
*c1
= (const struct channel_oil
*)arg1
;
92 const struct channel_oil
*c2
= (const struct channel_oil
*)arg2
;
94 if ((c1
->oil
.mfcc_mcastgrp
.s_addr
== c2
->oil
.mfcc_mcastgrp
.s_addr
)
95 && (c1
->oil
.mfcc_origin
.s_addr
== c2
->oil
.mfcc_origin
.s_addr
))
101 static unsigned int pim_oil_hash_key(const void *arg
)
103 const struct channel_oil
*oil
= arg
;
105 return jhash_2words(oil
->oil
.mfcc_mcastgrp
.s_addr
,
106 oil
->oil
.mfcc_origin
.s_addr
, 0);
109 void pim_oil_init(struct pim_instance
*pim
)
111 rb_pim_oil_init(&pim
->channel_oil_head
);
114 void pim_oil_terminate(struct pim_instance
*pim
)
116 struct channel_oil
*c_oil
;
118 while ((c_oil
= rb_pim_oil_pop(&pim
->channel_oil_head
)))
119 pim_channel_oil_free(c_oil
);
121 rb_pim_oil_fini(&pim
->channel_oil_head
);
124 void pim_channel_oil_free(struct channel_oil
*c_oil
)
126 XFREE(MTYPE_PIM_CHANNEL_OIL
, c_oil
);
129 struct channel_oil
*pim_find_channel_oil(struct pim_instance
*pim
,
130 struct prefix_sg
*sg
)
132 struct channel_oil
*c_oil
= NULL
;
133 struct channel_oil lookup
;
135 lookup
.oil
.mfcc_mcastgrp
= sg
->grp
;
136 lookup
.oil
.mfcc_origin
= sg
->src
;
138 c_oil
= rb_pim_oil_find(&pim
->channel_oil_head
, &lookup
);
143 struct channel_oil
*pim_channel_oil_add(struct pim_instance
*pim
,
144 struct prefix_sg
*sg
,
147 struct channel_oil
*c_oil
;
149 c_oil
= pim_find_channel_oil(pim
, sg
);
151 ++c_oil
->oil_ref_count
;
154 /* channel might be present prior to upstream */
155 c_oil
->up
= pim_upstream_find(
157 /* if the upstream entry is being anchored to an
158 * already existing channel OIL we need to re-evaluate
159 * the "Mute" state on AA OIFs
161 pim_channel_update_mute(c_oil
);
164 /* check if the IIF has changed
165 * XXX - is this really needed
167 pim_upstream_mroute_iif_update(c_oil
, __func__
);
169 if (PIM_DEBUG_MROUTE
)
171 "%s(%s): Existing oil for %pSG4 Ref Count: %d (Post Increment)",
172 __PRETTY_FUNCTION__
, name
, sg
,
173 c_oil
->oil_ref_count
);
177 c_oil
= XCALLOC(MTYPE_PIM_CHANNEL_OIL
, sizeof(*c_oil
));
179 c_oil
->oil
.mfcc_mcastgrp
= sg
->grp
;
180 c_oil
->oil
.mfcc_origin
= sg
->src
;
182 c_oil
->oil
.mfcc_parent
= MAXVIFS
;
183 c_oil
->oil_ref_count
= 1;
184 c_oil
->installed
= 0;
185 c_oil
->up
= pim_upstream_find(pim
, sg
);
188 rb_pim_oil_add(&pim
->channel_oil_head
, c_oil
);
190 if (PIM_DEBUG_MROUTE
)
191 zlog_debug("%s(%s): c_oil %s add",
192 __func__
, name
, pim_str_sg_dump(sg
));
197 struct channel_oil
*pim_channel_oil_del(struct channel_oil
*c_oil
,
200 if (PIM_DEBUG_MROUTE
) {
201 struct prefix_sg sg
= {.src
= c_oil
->oil
.mfcc_mcastgrp
,
202 .grp
= c_oil
->oil
.mfcc_origin
};
205 "%s(%s): Del oil for %pSG4, Ref Count: %d (Predecrement)",
206 __PRETTY_FUNCTION__
, name
, &sg
, c_oil
->oil_ref_count
);
208 --c_oil
->oil_ref_count
;
210 if (c_oil
->oil_ref_count
< 1) {
212 * notice that listnode_delete() can't be moved
213 * into pim_channel_oil_free() because the later is
214 * called by list_delete_all_node()
217 rb_pim_oil_del(&c_oil
->pim
->channel_oil_head
, c_oil
);
219 pim_channel_oil_free(c_oil
);
226 void pim_channel_oil_upstream_deref(struct channel_oil
*c_oil
)
228 /* The upstream entry associated with a channel_oil is abt to be
229 * deleted. If the channel_oil is kept around because of other
230 * references we need to remove upstream based states out of it.
232 c_oil
= pim_channel_oil_del(c_oil
, __func__
);
234 /* note: here we assume that c_oil->up has already been
237 pim_channel_update_mute(c_oil
);
241 int pim_channel_del_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
242 uint32_t proto_mask
, const char *caller
)
244 struct pim_interface
*pim_ifp
;
246 zassert(channel_oil
);
252 * Don't do anything if we've been asked to remove a source
253 * that is not actually on it.
255 if (!(channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
)) {
256 if (PIM_DEBUG_MROUTE
) {
257 char group_str
[INET_ADDRSTRLEN
];
258 char source_str
[INET_ADDRSTRLEN
];
259 pim_inet4_dump("<group?>",
260 channel_oil
->oil
.mfcc_mcastgrp
,
261 group_str
, sizeof(group_str
));
262 pim_inet4_dump("<source?>",
263 channel_oil
->oil
.mfcc_origin
, source_str
,
266 "%s %s: no existing protocol mask %u(%u) for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
267 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
269 ->oif_flags
[pim_ifp
->mroute_vif_index
],
270 oif
->name
, pim_ifp
->mroute_vif_index
,
272 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
273 source_str
, group_str
);
278 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~proto_mask
;
280 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &
281 PIM_OIF_FLAG_PROTO_ANY
) {
282 if (PIM_DEBUG_MROUTE
) {
283 char group_str
[INET_ADDRSTRLEN
];
284 char source_str
[INET_ADDRSTRLEN
];
285 pim_inet4_dump("<group?>",
286 channel_oil
->oil
.mfcc_mcastgrp
,
287 group_str
, sizeof(group_str
));
288 pim_inet4_dump("<source?>",
289 channel_oil
->oil
.mfcc_origin
, source_str
,
292 "%s %s: other protocol masks remain for requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
293 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
294 pim_ifp
->mroute_vif_index
,
296 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
297 source_str
, group_str
);
302 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] = 0;
303 /* clear mute; will be re-evaluated when the OIF becomes valid again */
304 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &= ~PIM_OIF_FLAG_MUTE
;
306 if (pim_upstream_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
307 if (PIM_DEBUG_MROUTE
) {
308 char group_str
[INET_ADDRSTRLEN
];
309 char source_str
[INET_ADDRSTRLEN
];
310 pim_inet4_dump("<group?>",
311 channel_oil
->oil
.mfcc_mcastgrp
,
312 group_str
, sizeof(group_str
));
313 pim_inet4_dump("<source?>",
314 channel_oil
->oil
.mfcc_origin
, source_str
,
317 "%s %s: could not remove output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
318 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
319 pim_ifp
->mroute_vif_index
, source_str
,
325 --channel_oil
->oil_size
;
327 if (PIM_DEBUG_MROUTE
) {
328 char group_str
[INET_ADDRSTRLEN
];
329 char source_str
[INET_ADDRSTRLEN
];
330 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
331 group_str
, sizeof(group_str
));
332 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
333 source_str
, sizeof(source_str
));
335 "%s(%s): (S,G)=(%s,%s): proto_mask=%u IIF:%d OIF=%s vif_index=%d",
336 __PRETTY_FUNCTION__
, caller
, source_str
, group_str
,
337 proto_mask
, channel_oil
->oil
.mfcc_parent
, oif
->name
,
338 pim_ifp
->mroute_vif_index
);
344 void pim_channel_del_inherited_oif(struct channel_oil
*c_oil
,
345 struct interface
*oif
, const char *caller
)
347 struct pim_upstream
*up
= c_oil
->up
;
349 pim_channel_del_oif(c_oil
, oif
, PIM_OIF_FLAG_PROTO_STAR
,
352 /* if an inherited OIF is being removed join-desired can change
353 * if the inherited OIL is now empty and KAT is running
355 if (up
&& up
->sg
.src
.s_addr
!= INADDR_ANY
&&
356 pim_upstream_empty_inherited_olist(up
))
357 pim_upstream_update_join_desired(up
->pim
, up
);
360 static bool pim_channel_eval_oif_mute(struct channel_oil
*c_oil
,
361 struct pim_interface
*pim_ifp
)
363 struct pim_interface
*pim_reg_ifp
;
364 struct pim_interface
*vxlan_ifp
;
365 bool do_mute
= false;
366 struct pim_instance
*pim
= c_oil
->pim
;
371 pim_reg_ifp
= pim
->regiface
->info
;
372 if (pim_ifp
== pim_reg_ifp
) {
373 /* suppress pimreg in the OIL if the mroute is not supposed to
374 * trigger register encapsulated data
376 if (PIM_UPSTREAM_FLAG_TEST_NO_PIMREG_DATA(c_oil
->up
->flags
))
382 vxlan_ifp
= pim_vxlan_get_term_ifp(pim
);
383 if (pim_ifp
== vxlan_ifp
) {
384 /* 1. vxlan termination device must never be added to the
385 * origination mroute (and that can actually happen because
386 * of XG inheritance from the termination mroute) otherwise
387 * traffic will end up looping.
388 * PS: This check has also been extended to non-orig mroutes
389 * that have a local SIP as such mroutes can move back and
390 * forth between orig<=>non-orig type.
391 * 2. vxlan termination device should be removed from the non-DF
392 * to prevent duplicates to the overlay rxer
394 if (PIM_UPSTREAM_FLAG_TEST_SRC_VXLAN_ORIG(c_oil
->up
->flags
) ||
395 PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(c_oil
->up
->flags
) ||
396 pim_vxlan_is_local_sip(c_oil
->up
))
405 void pim_channel_update_oif_mute(struct channel_oil
*c_oil
,
406 struct pim_interface
*pim_ifp
)
411 /* If pim_ifp is not a part of the OIL there is nothing to do */
412 if (!c_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
])
415 old_mute
= !!(c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &
417 new_mute
= pim_channel_eval_oif_mute(c_oil
, pim_ifp
);
418 if (old_mute
== new_mute
)
422 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
425 c_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
428 pim_upstream_mroute_add(c_oil
, __PRETTY_FUNCTION__
);
431 /* pim_upstream has been set or cleared on the c_oil. re-eval mute state
432 * on all existing OIFs
434 static void pim_channel_update_mute(struct channel_oil
*c_oil
)
436 struct pim_interface
*pim_reg_ifp
;
437 struct pim_interface
*vxlan_ifp
;
439 pim_reg_ifp
= c_oil
->pim
->regiface
->info
;
441 pim_channel_update_oif_mute(c_oil
, pim_reg_ifp
);
442 vxlan_ifp
= pim_vxlan_get_term_ifp(c_oil
->pim
);
444 pim_channel_update_oif_mute(c_oil
, vxlan_ifp
);
447 int pim_channel_add_oif(struct channel_oil
*channel_oil
, struct interface
*oif
,
448 uint32_t proto_mask
, const char *caller
)
450 struct pim_interface
*pim_ifp
;
454 * If we've gotten here we've gone bad, but let's
458 zlog_warn("Attempt to Add OIF for non-existent channel oil");
464 /* Prevent single protocol from subscribing same interface to
465 channel (S,G) multiple times */
466 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] & proto_mask
) {
467 if (PIM_DEBUG_MROUTE
) {
468 char group_str
[INET_ADDRSTRLEN
];
469 char source_str
[INET_ADDRSTRLEN
];
470 pim_inet4_dump("<group?>",
471 channel_oil
->oil
.mfcc_mcastgrp
,
472 group_str
, sizeof(group_str
));
473 pim_inet4_dump("<source?>",
474 channel_oil
->oil
.mfcc_origin
, source_str
,
477 "%s %s: existing protocol mask %u requested OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
478 __FILE__
, __PRETTY_FUNCTION__
, proto_mask
,
479 oif
->name
, pim_ifp
->mroute_vif_index
,
481 .mfcc_ttls
[pim_ifp
->mroute_vif_index
],
482 source_str
, group_str
);
487 /* Allow other protocol to request subscription of same interface to
488 * channel (S,G), we need to note this information
490 if (channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
]
491 & PIM_OIF_FLAG_PROTO_ANY
) {
493 /* Updating time here is not required as this time has to
494 * indicate when the interface is added
497 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
498 /* Check the OIF really exists before returning, and only log
500 if (channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] < 1) {
502 char group_str
[INET_ADDRSTRLEN
];
503 char source_str
[INET_ADDRSTRLEN
];
504 pim_inet4_dump("<group?>",
505 channel_oil
->oil
.mfcc_mcastgrp
,
506 group_str
, sizeof(group_str
));
507 pim_inet4_dump("<source?>",
508 channel_oil
->oil
.mfcc_origin
,
509 source_str
, sizeof(source_str
));
511 "%s %s: new protocol mask %u requested nonexistent OIF %s (vif_index=%d, min_ttl=%d) for channel (S,G)=(%s,%s)",
512 __FILE__
, __PRETTY_FUNCTION__
,
513 proto_mask
, oif
->name
,
514 pim_ifp
->mroute_vif_index
,
515 channel_oil
->oil
.mfcc_ttls
516 [pim_ifp
->mroute_vif_index
],
517 source_str
, group_str
);
524 old_ttl
= channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
];
527 if (PIM_DEBUG_MROUTE
) {
528 char group_str
[INET_ADDRSTRLEN
];
529 char source_str
[INET_ADDRSTRLEN
];
530 pim_inet4_dump("<group?>",
531 channel_oil
->oil
.mfcc_mcastgrp
,
532 group_str
, sizeof(group_str
));
533 pim_inet4_dump("<source?>",
534 channel_oil
->oil
.mfcc_origin
, source_str
,
537 "%s %s: interface %s (vif_index=%d) is existing output for channel (S,G)=(%s,%s)",
538 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
539 pim_ifp
->mroute_vif_index
, source_str
,
545 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
] =
548 /* Some OIFs are held in a muted state i.e. the PIM state machine
549 * decided to include the OIF but additional status check such as
550 * MLAG DF role prevent it from being activated for traffic
553 if (pim_channel_eval_oif_mute(channel_oil
, pim_ifp
))
554 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |=
557 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] &=
560 /* channel_oil->oil.mfcc_parent != MAXVIFS indicate this entry is not
561 * valid to get installed in kernel.
563 if (channel_oil
->oil
.mfcc_parent
!= MAXVIFS
) {
564 if (pim_upstream_mroute_add(channel_oil
, __PRETTY_FUNCTION__
)) {
565 if (PIM_DEBUG_MROUTE
) {
566 char group_str
[INET_ADDRSTRLEN
];
567 char source_str
[INET_ADDRSTRLEN
];
568 pim_inet4_dump("<group?>",
569 channel_oil
->oil
.mfcc_mcastgrp
,
570 group_str
, sizeof(group_str
));
571 pim_inet4_dump("<source?>",
572 channel_oil
->oil
.mfcc_origin
, source_str
,
575 "%s %s: could not add output interface %s (vif_index=%d) for channel (S,G)=(%s,%s)",
576 __FILE__
, __PRETTY_FUNCTION__
, oif
->name
,
577 pim_ifp
->mroute_vif_index
, source_str
,
581 channel_oil
->oil
.mfcc_ttls
[pim_ifp
->mroute_vif_index
]
587 channel_oil
->oif_creation
[pim_ifp
->mroute_vif_index
] =
588 pim_time_monotonic_sec();
589 ++channel_oil
->oil_size
;
590 channel_oil
->oif_flags
[pim_ifp
->mroute_vif_index
] |= proto_mask
;
592 if (PIM_DEBUG_MROUTE
) {
593 char group_str
[INET_ADDRSTRLEN
];
594 char source_str
[INET_ADDRSTRLEN
];
595 pim_inet4_dump("<group?>", channel_oil
->oil
.mfcc_mcastgrp
,
596 group_str
, sizeof(group_str
));
597 pim_inet4_dump("<source?>", channel_oil
->oil
.mfcc_origin
,
598 source_str
, sizeof(source_str
));
600 "%s(%s): (S,G)=(%s,%s): proto_mask=%u OIF=%s vif_index=%d: DONE",
601 __PRETTY_FUNCTION__
, caller
, source_str
, group_str
,
602 proto_mask
, oif
->name
, pim_ifp
->mroute_vif_index
);
608 int pim_channel_oil_empty(struct channel_oil
*c_oil
)
610 static struct mfcctl null_oil
;
615 /* exclude pimreg from the OIL when checking if the inherited_oil is
617 * pimreg device (in all vrfs) uses a vifi of
618 * 0 (PIM_OIF_PIM_REGISTER_VIF) so we simply mfcc_ttls[0] */
619 return !memcmp(&c_oil
->oil
.mfcc_ttls
[1], &null_oil
.mfcc_ttls
[1],
620 sizeof(null_oil
.mfcc_ttls
) - sizeof(null_oil
.mfcc_ttls
[0]));