This commit is contained in:
Rajasekar Raja 2025-04-29 16:22:43 +00:00 committed by GitHub
commit 948e55bfa5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 193 additions and 7 deletions

View file

@ -1639,7 +1639,7 @@ zebra Terminal Mode Commands
total number of route nodes in the table. Which will be higher than
the actual number of routes that are held.
.. clicmd:: show nexthop-group rib [ID] [vrf NAME] [singleton [ip|ip6]] [type] [json]
.. clicmd:: show nexthop-group rib [ID [routes]] [vrf NAME] [singleton [ip|ip6]] [type] [json]
Display nexthop groups created by zebra. The [vrf NAME] option
is only meaningful if you have started zebra with the --vrfwnetns
@ -1650,7 +1650,9 @@ zebra Terminal Mode Commands
that has `Initial Delay`, means that this nexthop group entry
was not installed because no-one was using it at that point and
Zebra can delay installing this route until it is used by something
else.
else. [routes] option after the NHG ID will show all the routes using
a particular NHG.
.. clicmd:: show <ip|ipv6> zebra route dump [<vrf> VRFNAME]

View file

@ -80,6 +80,9 @@ struct route_entry {
/* Link list. */
struct re_list_item next;
/* Back pointer to route node */
struct route_node *rn;
/* Nexthop group, shared/refcounted, based on the nexthop(s)
* provided by the owner of the route
*/
@ -161,8 +164,42 @@ struct route_entry {
*/
struct nexthop_group fib_ng;
struct nexthop_group fib_backup_ng;
/* Selected re using an nhe are in its re RB tree */
struct nhe_re_tree_item re_item;
};
static int route_entry_cmp(const struct route_entry *re1, const struct route_entry *re2)
{
int ret;
/* Compare VRF first - most likely to be different */
if (re1->vrf_id != re2->vrf_id)
return (re1->vrf_id - re2->vrf_id);
/* Compare protocol type - next most likely differentiator */
if (re1->type != re2->type)
return (re1->type - re2->type);
/* Compare instance */
if (re1->instance != re2->instance)
return (re1->instance - re2->instance);
/* Compare the actual prefix */
if (re1->rn && re2->rn) {
ret = prefix_cmp(&re1->rn->p, &re2->rn->p);
if (ret != 0)
return ret;
} else if (re1->rn)
return 1;
else if (re2->rn)
return -1;
return 0;
}
DECLARE_RBTREE_UNIQ(nhe_re_tree, struct route_entry, re_item, route_entry_cmp);
#define RIB_SYSTEM_ROUTE(R) RSYSTEM_ROUTE((R)->type)
#define RIB_KERNEL_ROUTE(R) RKERNEL_ROUTE((R)->type)
@ -639,6 +676,9 @@ extern pid_t zebra_pid;
extern uint32_t rt_table_main_id;
extern void nhe_re_tree_replace(struct nhe_re_tree_head *head, struct route_entry *re_to_oper,
bool is_del);
void route_entry_dump_nh(const struct route_entry *re, const char *straddr,
const struct vrf *re_vrf,
const struct nexthop *nexthop);

View file

@ -398,6 +398,10 @@ struct nhg_hash_entry *zebra_nhg_alloc(void)
struct nhg_hash_entry *nhe;
nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry));
if (IS_ZEBRA_DEBUG_NHG_DETAIL)
zlog_debug("%s Creating the nhe %u (%p) re-tree", __func__, nhe->id, nhe);
nhe_re_tree_init(&nhe->re_head);
return nhe;
}
@ -1713,6 +1717,15 @@ void zebra_nhg_free(struct nhg_hash_entry *nhe)
EVENT_OFF(nhe->timer);
if (IS_ZEBRA_DEBUG_NHG_DETAIL)
zlog_debug("%s Deleting the nhe %u (%p) re-tree", __func__, nhe->id, nhe);
struct route_entry *re = NULL;
/* Just pop entries until tree is empty */
while ((re = nhe_re_tree_pop(&nhe->re_head)) != NULL)
;
nhe_re_tree_fini(&nhe->re_head);
zebra_nhg_free_members(nhe);
XFREE(MTYPE_NHG, nhe);
@ -3553,6 +3566,36 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
zebra_nhg_handle_install(nhe, true);
/*
* In cases such as quick Interface flaps/kernel nexthop related
* triggers (ip nexthop flush/ip nexthop del id <>), reinstall all
* the routes which the nhe has at this moment.
* The remainder new routes will anyway be successful since the NHG
* is now installed.
*/
while (nhe_re_tree_count(&nhe->re_head)) {
struct route_entry *re = nhe_re_tree_pop(&nhe->re_head);
if (IS_ZEBRA_DEBUG_NHG_DETAIL)
zlog_debug("%s Route re-install for re %p (%pRN, %s) on nhe %u (%p)",
__func__, re, re->rn,
zebra_route_string(re->type), id, nhe);
/*
* There is always a case where (say, initially BGP was best),
* which we try to install in the kernel, and it fails because
* NHG installation failed.
*
* Now when NHG installation is successful, if OSPF/Static
* is preferred to be installed or already installed, we don't
* want to install this BGP route entry anymore.
* In that case, just remove the entry (just in case).
*/
if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
rib_install_kernel(re->rn, re, NULL);
else
nhe_re_tree_replace(&re->nhe->re_head, re, true);
}
/* If daemon nhg, send it an update */
if (PROTO_OWNED(nhe))
zsend_nhg_notify(nhe->type, nhe->zapi_instance,

View file

@ -26,6 +26,7 @@ struct nh_grp {
};
PREDECL_RBTREE_UNIQ(nhg_connected_tree);
PREDECL_RBTREE_UNIQ(nhe_re_tree);
/*
* Hashtables containing nhg entries is in `zebra_router`.
@ -171,6 +172,9 @@ struct nhg_hash_entry {
* chooses this NHG then we can install it then.
*/
#define NEXTHOP_GROUP_INITIAL_DELAY_INSTALL (1 << 9)
/* Head of rb_tree of route_entries(re's)*/
struct nhe_re_tree_head re_head;
};
/* Upper 4 bits of the NHG are reserved for indicating the NHG type */

View file

@ -1980,6 +1980,30 @@ done:
}
/* Finds existing entry and deletes it. Add new entry if ADD/UPDATE */
void nhe_re_tree_replace(struct nhe_re_tree_head *head, struct route_entry *re_to_oper, bool is_del)
{
struct route_entry *old_re = NULL;
if (head->rr.rbt_root != NULL) {
old_re = nhe_re_tree_find(head, re_to_oper);
if (old_re) {
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug("%s Found and deleting old_re %p (%pRN %s)", __func__,
old_re, old_re->rn, zebra_route_string(old_re->type));
nhe_re_tree_del(head, old_re);
}
}
if (!is_del) {
nhe_re_tree_add(head, re_to_oper);
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug("%s Added new_re %p (%pRN %s)", __func__, re_to_oper,
re_to_oper->rn, zebra_route_string(re_to_oper->type));
}
}
/*
* Route-update results processing after async dataplane update.
@ -2091,6 +2115,12 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx)
}
if (op == DPLANE_OP_ROUTE_INSTALL || op == DPLANE_OP_ROUTE_UPDATE) {
if (old_re && old_re != re)
nhe_re_tree_replace(&old_re->nhe->re_head, old_re, true);
if (re)
nhe_re_tree_replace(&re->nhe->re_head, re, false);
if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
if (re) {
UNSET_FLAG(re->status, ROUTE_ENTRY_FAILED);
@ -2183,8 +2213,10 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx)
}
} else if (op == DPLANE_OP_ROUTE_DELETE) {
rt_delete = true;
if (re)
if (re) {
nhe_re_tree_replace(&re->nhe->re_head, re, true);
SET_FLAG(re->status, ROUTE_ENTRY_FAILED);
}
/*
* In the delete case, the zebra core datastructs were
* updated (or removed) at the time the delete was issued,
@ -4117,6 +4149,7 @@ static void rib_link(struct route_node *rn, struct route_entry *re, int process)
rnode_debug(rn, re->vrf_id, "rn %p adding dest", rn);
}
re->rn = rn;
re_list_add_head(&dest->routes, re);
afi = (rn->p.family == AF_INET)
@ -4173,6 +4206,13 @@ void rib_unlink(struct route_node *rn, struct route_entry *re)
dest = rib_dest_from_rnode(rn);
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug("%s Deleting re %p (%pRN, %s) from NHE %u (%p) re-tree", __func__, re,
re->rn, zebra_route_string(re->type), re->nhe->id, re->nhe);
nhe_re_tree_replace(&re->nhe->re_head, re, true);
re->rn = NULL;
re_list_del(&dest->routes, re);
if (dest->selected_fib == re)
@ -4445,6 +4485,7 @@ struct route_entry *zebra_rib_route_entry_new(vrf_id_t vrf_id, int type,
re->uptime = monotime(NULL);
re->tag = tag;
re->nhe_id = nhe_id;
re->rn = NULL;
return re;
}

View file

@ -1435,11 +1435,12 @@ DEFPY (show_interface_nexthop_group,
DEFPY(show_nexthop_group,
show_nexthop_group_cmd,
"show nexthop-group rib <(0-4294967295)$id|[singleton <ip$v4|ipv6$v6>] [<kernel|zebra|bgp|sharp>$type_str] [vrf <NAME$vrf_name|all$vrf_all>]> [json]",
"show nexthop-group rib <(0-4294967295)$id [routes$routes]|[singleton <ip$v4|ipv6$v6>] [<kernel|zebra|bgp|sharp>$type_str] [vrf <NAME$vrf_name|all$vrf_all>]> [json]",
SHOW_STR
"Show Nexthop Groups\n"
"RIB information\n"
"Nexthop Group ID\n"
"Show routes using this nexthop group\n"
"Show Singleton Nexthop-Groups\n"
IP_STR
IP6_STR
@ -1450,19 +1451,74 @@ DEFPY(show_nexthop_group,
VRF_FULL_CMD_HELP_STR
JSON_STR)
{
struct zebra_vrf *zvrf = NULL;
afi_t afi = AFI_UNSPEC;
int type = 0;
bool uj = use_json(argc, argv);
json_object *json = NULL;
json_object *json_vrf = NULL;
struct route_entry *re = NULL;
if (uj)
json = json_object_new_object();
if (id)
return show_nexthop_group_id_cmd_helper(vty, id, json);
if (id) {
struct nhg_hash_entry *nhe = zebra_nhg_lookup_id(id);
if (!nhe) {
vty_out(vty, "%% Can't find nexthop group %lu\n", id);
return CMD_WARNING;
}
if (routes) {
if (uj) {
json_object *json_routes = json_object_new_array();
frr_each (nhe_re_tree, &nhe->re_head, re) {
json_object *json_route = json_object_new_object();
char buf[PREFIX_STRLEN];
const char *proto_name;
prefix2str(&re->rn->p, buf, sizeof(buf));
proto_name = zebra_route_string(re->type);
json_object_string_add(json_route, "prefix", buf);
json_object_boolean_add(json_route, "installed",
CHECK_FLAG(re->flags,
ZEBRA_FLAG_SELECTED));
json_object_string_add(json_route, "protocol", proto_name);
json_object_array_add(json_routes, json_route);
}
json_object_object_add(json, "routes", json_routes);
vty_json(vty, json);
} else {
vty_out(vty, "Routes using nexthop group %lu:\n", id);
vty_out(vty,
"Route Entry Status Protocol\n");
vty_out(vty,
"------------------------------ ----------- --------\n");
frr_each (nhe_re_tree, &nhe->re_head, re) {
char buf[PREFIX_STRLEN];
const char *proto_name;
prefix2str(&re->rn->p, buf, sizeof(buf));
proto_name = zebra_route_string(re->type);
vty_out(vty, "%-30s %-11s %s\n", buf,
CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)
? "installed"
: "not installed",
proto_name);
}
}
return CMD_SUCCESS;
}
show_nexthop_group_id_cmd_helper(vty, id, uj ? json : NULL);
return CMD_SUCCESS;
}
if (v4)
afi = AFI_IP;