diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 16359cd792a3..96de28261545 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -389,6 +389,103 @@ struct nhg_hash_entry *zebra_nhg_alloc(void) return nhe; } +static uint32_t zebra_nhg_prefix_proto_nhgs_hash_key(const void *arg) +{ + const struct nhg_prefix_proto_nhgs *nhg_p = arg; + uint32_t key, key2; + + key = prefix_hash_key(&(nhg_p->prefix)); + key2 = prefix_hash_key(&(nhg_p->src_prefix)); + key = jhash_1word(key, key2); + key = jhash_2words(nhg_p->afi, nhg_p->safi, key); + key = jhash_2words(nhg_p->table_id, nhg_p->vrf_id, key); + + return key; +} + +static bool zebra_nhg_prefix_proto_nhgs_hash_equal(const void *arg1, + const void *arg2) +{ + const struct nhg_prefix_proto_nhgs *r1, *r2; + + r1 = (const struct nhg_prefix_proto_nhgs *)arg1; + r2 = (const struct nhg_prefix_proto_nhgs *)arg2; + + if (r1->afi != r2->afi) + return false; + + if (r1->safi != r2->safi) + return false; + + if (r1->vrf_id != r2->vrf_id) + return false; + + if (r1->table_id != r2->table_id) + return false; + + if (prefix_cmp(&r1->prefix, &r2->prefix)) + return false; + + if (r1->src_prefix.family != r2->src_prefix.family) + return false; + + if (r1->src_prefix.family && + prefix_cmp(&r1->src_prefix, &r2->src_prefix)) + return false; + + return true; +} + +static void zebra_nhg_prefix_copy_entry(struct hash_bucket *b, void *data) +{ + struct nhg_prefix_proto_nhgs *nhg_p = b->data, *new_nhg_p; + struct nhg_hash_entry *nhe = data; + + assert(nhe); + assert(nhe->prefix_proto_nhgs); + + new_nhg_p = hash_get(nhe->prefix_proto_nhgs, nhg_p, + zebra_nhg_prefix_proto_nhgs_alloc); + new_nhg_p->back_ptr = nhe; + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => copy prefix %pFX to NHG (%pNG)", __func__, + &nhg_p->prefix, nhe); +} + +void zebra_nhg_prefix_proto_nhgs_hash_free(void *p) +{ + struct nhg_prefix_proto_nhgs *nhg_p = p; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => detach prefix %pFX from NHG (%pNG)", + __func__, &nhg_p->prefix, nhg_p->back_ptr); + + XFREE(MTYPE_NHG, nhg_p); +} + +void *zebra_nhg_prefix_proto_nhgs_alloc(void *arg) +{ + struct nhg_prefix_proto_nhgs *nhg_p, *orig; + + orig = (struct nhg_prefix_proto_nhgs *)arg; + + nhg_p = XCALLOC(MTYPE_NHG, sizeof(struct nhg_prefix_proto_nhgs)); + + memcpy(nhg_p, orig, sizeof(struct nhg_prefix_proto_nhgs)); + + return nhg_p; +} + +void zebra_nhg_prefix_copy(struct nhg_hash_entry *old_entry, + struct nhg_hash_entry *new_entry) +{ + if (new_entry && old_entry && old_entry->prefix_proto_nhgs && + new_entry->prefix_proto_nhgs) { + hash_iterate(old_entry->prefix_proto_nhgs, + zebra_nhg_prefix_copy_entry, new_entry); + } +} + /* * Allocate new nhe and make shallow copy of 'orig'; no * recursive info is copied. @@ -402,6 +499,12 @@ struct nhg_hash_entry *zebra_nhe_copy(const struct nhg_hash_entry *orig, nhe->id = id; + if (id >= ZEBRA_NHG_PROTO_LOWER) + nhe->prefix_proto_nhgs = + hash_create_size(8, zebra_nhg_prefix_proto_nhgs_hash_key, + zebra_nhg_prefix_proto_nhgs_hash_equal, + "Zebra Nexthop groups Prefixes hash list index"); + nexthop_group_copy(&(nhe->nhg), &(orig->nhg)); nhe->vrf_id = orig->vrf_id; @@ -1618,6 +1721,13 @@ void zebra_nhg_free(struct nhg_hash_entry *nhe) zebra_nhg_free_members(nhe); + if (nhe->prefix_proto_nhgs) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => suppressing prefixes from NHG (%pNG)", + __func__, nhe); + hash_clean_and_free(&nhe->prefix_proto_nhgs, + zebra_nhg_prefix_proto_nhgs_hash_free); + } XFREE(MTYPE_NHG, nhe); } diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 23eb677ee63a..7076cf0b5474 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -91,6 +91,10 @@ struct nhg_hash_entry { struct event *timer; + /* list of prefixes using that nexthop group + * only populated for protocol nexthop groups + */ + struct hash *prefix_proto_nhgs; /* * Is this nexthop group valid, ie all nexthops are fully resolved. * What is fully resolved? It's a nexthop that is either self contained @@ -225,6 +229,18 @@ struct nhg_ctx { enum nhg_ctx_status status; }; +struct nhg_prefix_proto_nhgs { + /* key */ + struct prefix prefix; + struct prefix src_prefix; + afi_t afi; + safi_t safi; + uint32_t table_id; + vrf_id_t vrf_id; + + struct nhg_hash_entry *back_ptr; +}; + /* Global control to disable use of kernel nexthops, if available. We can't * force the kernel to support nexthop ids, of course, but we can disable * zebra's use of them, for testing e.g. By default, if the kernel supports @@ -246,6 +262,7 @@ bool zebra_nhg_recursive_use_backups(void); * Use these where possible instead of direct access. */ struct nhg_hash_entry *zebra_nhg_alloc(void); + void zebra_nhg_free(struct nhg_hash_entry *nhe); /* In order to clear a generic hash, we need a generic api, sigh. */ void zebra_nhg_hash_free(void *p); @@ -264,6 +281,13 @@ void zebra_nhe_init(struct nhg_hash_entry *nhe, afi_t afi, struct nhg_hash_entry *zebra_nhe_copy(const struct nhg_hash_entry *orig, uint32_t id); +/* Allocate/Free prefix_proto_nhgs object */ +void *zebra_nhg_prefix_proto_nhgs_alloc(void *arg); +void zebra_nhg_prefix_proto_nhgs_hash_free(void *p); +/* Utility to copy hash list of prefixes in a new nhg */ +void zebra_nhg_prefix_copy(struct nhg_hash_entry *old, + struct nhg_hash_entry *new); + /* Allocate, free backup nexthop info objects */ struct nhg_backup_info *zebra_nhg_backup_alloc(void); void zebra_nhg_backup_free(struct nhg_backup_info **p); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index c327079a228b..dee30743bbb3 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -478,6 +478,8 @@ int rib_handle_nhg_replace(struct nhg_hash_entry *old_entry, } } + zebra_nhg_prefix_copy(old_entry, new_entry); + /* * if ret > 0, some previous re->nhe has freed the address to which * old_entry is pointing. @@ -1977,6 +1979,10 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) bool fib_changed = false; struct rib_table_info *info; bool rt_delete = false; + struct nhg_hash_entry *nhe; + struct nhg_prefix_proto_nhgs nhg_p = {}, *p_nhg_p; + const struct prefix *p; + const struct prefix *src_p = NULL; zvrf = zebra_vrf_lookup_by_id(dplane_ctx_get_vrf(ctx)); vrf = vrf_lookup_by_id(dplane_ctx_get_vrf(ctx)); @@ -2070,6 +2076,25 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) } if (op == DPLANE_OP_ROUTE_INSTALL || op == DPLANE_OP_ROUTE_UPDATE) { + if (re && re->nhe_id >= ZEBRA_NHG_PROTO_LOWER) { + nhe = zebra_nhg_lookup_id(re->nhe_id); + if (nhe) { + srcdest_rnode_prefixes(rn, &p, &src_p); + prefix_copy(&nhg_p.prefix, p); + if (src_p) + prefix_copy(&nhg_p.src_prefix, src_p); + nhg_p.table_id = dplane_ctx_get_table(ctx); + nhg_p.afi = dplane_ctx_get_afi(ctx); + nhg_p.safi = dplane_ctx_get_safi(ctx); + nhg_p.vrf_id = dplane_ctx_get_vrf(ctx); + nhg_p.back_ptr = nhe; + hash_get(nhe->prefix_proto_nhgs, &nhg_p, + zebra_nhg_prefix_proto_nhgs_alloc); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => attach prefix %pFX to NHG (%pNG)", + __func__, p, nhe); + } + } if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { if (re) { UNSET_FLAG(re->status, ROUTE_ENTRY_FAILED); @@ -2173,6 +2198,29 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if (re) { UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); UNSET_FLAG(re->status, ROUTE_ENTRY_FAILED); + + if (re->nhe && re->nhe->prefix_proto_nhgs) { + /* remove prefix from nhe_id list */ + srcdest_rnode_prefixes(rn, &p, &src_p); + prefix_copy(&nhg_p.prefix, p); + if (src_p) + prefix_copy(&nhg_p.src_prefix, + src_p); + nhg_p.table_id = + dplane_ctx_get_table(ctx); + nhg_p.afi = dplane_ctx_get_afi(ctx); + nhg_p.safi = dplane_ctx_get_safi(ctx); + nhg_p.vrf_id = dplane_ctx_get_vrf(ctx); + p_nhg_p = + hash_lookup(re->nhe->prefix_proto_nhgs, + &nhg_p); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: => detach prefix %pFX from NHG (%pNG)", + __func__, p, re->nhe); + if (p_nhg_p) + zebra_nhg_prefix_proto_nhgs_hash_free( + p_nhg_p); + } } zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_REMOVED);