diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index 64979ed7ed..8933245de1 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -36,6 +36,8 @@ // struct list *pim_channel_oil_list = NULL; // struct hash *pim_channel_oil_hash = NULL; +static void pim_channel_update_mute(struct channel_oil *c_oil); + char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size) { char *out; @@ -205,8 +207,17 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, pim_channel_oil_change_iif(pim, c_oil, input_vif_index, name); ++c_oil->oil_ref_count; - /* channel might be present prior to upstream */ - c_oil->up = pim_upstream_find(pim, sg); + + if (!c_oil->up) { + /* channel might be present prior to upstream */ + c_oil->up = pim_upstream_find( + pim, sg); + /* if the upstream entry is being anchored to an + * already existing channel OIL we need to re-evaluate + * the "Mute" state on AA OIFs + */ + pim_channel_update_mute(c_oil); + } if (PIM_DEBUG_MROUTE) zlog_debug( @@ -247,7 +258,8 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, return c_oil; } -void pim_channel_oil_del(struct channel_oil *c_oil, const char *name) +struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, + const char *name) { if (PIM_DEBUG_MROUTE) { struct prefix_sg sg = {.src = c_oil->oil.mfcc_mcastgrp, @@ -270,6 +282,24 @@ void pim_channel_oil_del(struct channel_oil *c_oil, const char *name) hash_release(c_oil->pim->channel_oil_hash, c_oil); pim_channel_oil_free(c_oil); + return NULL; + } + + return c_oil; +} + +void pim_channel_oil_upstream_deref(struct channel_oil *c_oil) +{ + /* The upstream entry associated with a channel_oil is abt to be + * deleted. If the channel_oil is kept around because of other + * references we need to remove upstream based states out of it. + */ + c_oil = pim_channel_oil_del(c_oil, __func__); + if (c_oil) { + /* note: here we assume that c_oil->up has already been + * cleared + */ + pim_channel_update_mute(c_oil); } } @@ -448,6 +478,22 @@ void pim_channel_update_oif_mute(struct channel_oil *c_oil, pim_mroute_add(c_oil, __PRETTY_FUNCTION__); } +/* pim_upstream has been set or cleared on the c_oil. re-eval mute state + * on all existing OIFs + */ +static void pim_channel_update_mute(struct channel_oil *c_oil) +{ + struct pim_interface *pim_reg_ifp; + struct pim_interface *vxlan_ifp; + + pim_reg_ifp = c_oil->pim->regiface->info; + if (pim_reg_ifp) + pim_channel_update_oif_mute(c_oil, pim_reg_ifp); + vxlan_ifp = pim_vxlan_get_term_ifp(c_oil->pim); + if (vxlan_ifp) + pim_channel_update_oif_mute(c_oil, vxlan_ifp); +} + int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, uint32_t proto_mask, const char *caller) { diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index a371e2a7c6..087f47121d 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -120,7 +120,8 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, void pim_channel_oil_change_iif(struct pim_instance *pim, struct channel_oil *c_oil, int input_vif_index, const char *name); -void pim_channel_oil_del(struct channel_oil *c_oil, const char *name); +struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, + const char *name); int pim_channel_add_oif(struct channel_oil *c_oil, struct interface *oif, uint32_t proto_mask, const char *caller); @@ -133,4 +134,6 @@ char *pim_channel_oil_dump(struct channel_oil *c_oil, char *buf, size_t size); void pim_channel_update_oif_mute(struct channel_oil *c_oil, struct pim_interface *pim_ifp); + +void pim_channel_oil_upstream_deref(struct channel_oil *c_oil); #endif /* PIM_OIL_H */ diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index 02dfb136c5..fdb7363042 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -141,14 +141,22 @@ static struct pim_upstream *pim_upstream_find_parent(struct pim_instance *pim, static void upstream_channel_oil_detach(struct pim_upstream *up) { - if (up->channel_oil) { + struct channel_oil *channel_oil = up->channel_oil; + + if (channel_oil) { /* Detaching from channel_oil, channel_oil may exist post del, but upstream would not keep reference of it */ - up->channel_oil->up = NULL; - pim_channel_oil_del(up->channel_oil, __PRETTY_FUNCTION__); + channel_oil->up = NULL; up->channel_oil = NULL; + + /* attempt to delete channel_oil; if channel_oil is being held + * because of other references cleanup info such as "Mute" + * inferred from the parent upstream + */ + pim_channel_oil_upstream_deref(channel_oil); } + } struct pim_upstream *pim_upstream_del(struct pim_instance *pim, diff --git a/pimd/pim_vxlan.h b/pimd/pim_vxlan.h index f0a66e6b77..c6507a474c 100644 --- a/pimd/pim_vxlan.h +++ b/pimd/pim_vxlan.h @@ -115,6 +115,13 @@ static inline bool pim_vxlan_is_orig_mroute(struct pim_vxlan_sg *vxlan_sg) return (vxlan_sg->sg.src.s_addr != 0); } +static inline bool pim_vxlan_is_local_sip(struct pim_upstream *up) +{ + return (up->sg.src.s_addr != INADDR_ANY) && + up->rpf.source_nexthop.interface && + if_is_loopback_or_vrf(up->rpf.source_nexthop.interface); +} + extern struct pim_vxlan *pim_vxlan_p; extern struct pim_vxlan_sg *pim_vxlan_sg_find(struct pim_instance *pim, struct prefix_sg *sg);