ripd: Implement `allow-ecmp X` command

Allow setting an arbitrary number of paths to be installed instead of ALL.

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
This commit is contained in:
Donatas Abraitis 2023-05-04 09:13:07 +03:00
parent 9c011d7eaf
commit 75fce4645a
7 changed files with 107 additions and 17 deletions

View File

@ -85,14 +85,33 @@ void cli_show_router_rip(struct vty *vty, const struct lyd_node *dnode,
/*
* XPath: /frr-ripd:ripd/instance/allow-ecmp
*/
DEFPY_YANG (rip_allow_ecmp,
DEFUN_YANG (rip_allow_ecmp,
rip_allow_ecmp_cmd,
"[no] allow-ecmp",
NO_STR
"Allow Equal Cost MultiPath\n")
"allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]",
"Allow Equal Cost MultiPath\n"
"Number of paths\n")
{
nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY,
no ? "false" : "true");
int idx_number = 1;
char mpaths[3] = {};
uint32_t paths = MULTIPATH_NUM;
if (argv[idx_number])
paths = strtol(argv[idx_number]->arg, NULL, 10);
snprintf(mpaths, sizeof(mpaths), "%u", paths);
nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, mpaths);
return nb_cli_apply_changes(vty, NULL);
}
DEFUN_YANG (no_rip_allow_ecmp,
no_rip_allow_ecmp_cmd,
"no allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]",
NO_STR
"Allow Equal Cost MultiPath\n"
"Number of paths\n")
{
nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, 0);
return nb_cli_apply_changes(vty, NULL);
}
@ -100,10 +119,14 @@ DEFPY_YANG (rip_allow_ecmp,
void cli_show_rip_allow_ecmp(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults)
{
if (!yang_dnode_get_bool(dnode, NULL))
vty_out(vty, " no");
uint8_t paths;
vty_out(vty, " allow-ecmp\n");
paths = yang_dnode_get_uint8(dnode, NULL);
if (!paths)
vty_out(vty, " no allow-ecmp\n");
else
vty_out(vty, " allow-ecmp %d\n", paths);
}
/*
@ -1156,6 +1179,7 @@ void rip_cli_init(void)
install_element(RIP_NODE, &rip_no_distribute_list_cmd);
install_element(RIP_NODE, &rip_allow_ecmp_cmd);
install_element(RIP_NODE, &no_rip_allow_ecmp_cmd);
install_element(RIP_NODE, &rip_default_information_originate_cmd);
install_element(RIP_NODE, &rip_default_metric_cmd);
install_element(RIP_NODE, &no_rip_default_metric_cmd);

View File

@ -101,9 +101,13 @@ int ripd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args)
return NB_OK;
rip = nb_running_get_entry(args->dnode, NULL, true);
rip->ecmp = yang_dnode_get_bool(args->dnode, NULL);
if (!rip->ecmp)
rip->ecmp = yang_dnode_get_uint8(args->dnode, NULL);
if (!rip->ecmp) {
rip_ecmp_disable(rip);
return NB_OK;
}
rip_ecmp_change(rip);
return NB_OK;
}

View File

@ -155,7 +155,10 @@ struct rip_info *rip_ecmp_add(struct rip *rip, struct rip_info *rinfo_new)
{
struct route_node *rp = rinfo_new->rp;
struct rip_info *rinfo = NULL;
struct rip_info *rinfo_exist = NULL;
struct list *list = NULL;
struct listnode *node = NULL;
struct listnode *nnode = NULL;
if (rp->info == NULL)
rp->info = list_new();
@ -166,6 +169,33 @@ struct rip_info *rip_ecmp_add(struct rip *rip, struct rip_info *rinfo_new)
if (listcount(list) && !rip->ecmp)
return NULL;
/* Add or replace an existing ECMP path with lower neighbor IP */
if (listcount(list) && listcount(list) >= rip->ecmp) {
struct rip_info *from_highest = NULL;
/* Find the rip_info struct that has the highest nexthop IP */
for (ALL_LIST_ELEMENTS(list, node, nnode, rinfo_exist))
if (!from_highest ||
(from_highest &&
IPV4_ADDR_CMP(&rinfo_exist->from,
&from_highest->from) > 0)) {
from_highest = rinfo_exist;
}
/* If we have a route in ECMP group, delete the old
* one that has a higher next-hop address. Lower IP is
* preferred.
*/
if (rip->ecmp > 1 && from_highest &&
IPV4_ADDR_CMP(&from_highest->from, &rinfo_new->from) > 0) {
rip_ecmp_delete(rip, from_highest);
goto add_or_replace;
}
return NULL;
}
add_or_replace:
rinfo = rip_info_new();
memcpy(rinfo, rinfo_new, sizeof(struct rip_info));
listnode_add(list, rinfo);
@ -2631,6 +2661,36 @@ struct rip *rip_lookup_by_vrf_name(const char *vrf_name)
return RB_FIND(rip_instance_head, &rip_instances, &rip);
}
/* Update ECMP routes to zebra when `allow-ecmp` changed. */
void rip_ecmp_change(struct rip *rip)
{
struct route_node *rp;
struct rip_info *rinfo;
struct list *list;
struct listnode *node, *nextnode;
for (rp = route_top(rip->table); rp; rp = route_next(rp)) {
list = rp->info;
if (list && listcount(list) > 1) {
while (listcount(list) > rip->ecmp) {
struct rip_info *from_highest = NULL;
for (ALL_LIST_ELEMENTS(list, node, nextnode,
rinfo)) {
if (!from_highest ||
(from_highest &&
IPV4_ADDR_CMP(
&rinfo->from,
&from_highest->from) > 0))
from_highest = rinfo;
}
rip_ecmp_delete(rip, from_highest);
}
}
}
}
/* Create new RIP instance and set it to global variable. */
struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket)
{
@ -2640,7 +2700,7 @@ struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket)
rip->vrf_name = XSTRDUP(MTYPE_RIP_VRF_NAME, vrf_name);
/* Set initial value. */
rip->ecmp = yang_get_default_bool("%s/allow-ecmp", RIP_INSTANCE);
rip->ecmp = yang_get_default_uint8("%s/allow-ecmp", RIP_INSTANCE);
rip->default_metric =
yang_get_default_uint8("%s/default-metric", RIP_INSTANCE);
rip->distance =

View File

@ -141,7 +141,7 @@ struct rip {
struct route_table *distance_table;
/* RIP ECMP flag */
bool ecmp;
uint8_t ecmp;
/* Are we in passive-interface default mode? */
bool passive_default;
@ -537,4 +537,6 @@ extern struct event_loop *master;
DECLARE_HOOK(rip_ifaddr_add, (struct connected * ifc), (ifc));
DECLARE_HOOK(rip_ifaddr_del, (struct connected * ifc), (ifc));
extern void rip_ecmp_change(struct rip *rip);
#endif /* _ZEBRA_RIP_H */

View File

@ -23,7 +23,7 @@
"instance": [
{
"vrf": "default",
"allow-ecmp": "true",
"allow-ecmp": 1,
"distance": {
"source": [
{

View File

@ -19,7 +19,7 @@
<ripd xmlns="http://frrouting.org/yang/ripd">
<instance>
<vrf>default</vrf>
<allow-ecmp>true</allow-ecmp>
<allow-ecmp>1</allow-ecmp>
<static-route>10.0.1.0/24</static-route>
<distance>
<source>

View File

@ -119,8 +119,8 @@ module frr-ripd {
"VRF name.";
}
leaf allow-ecmp {
type boolean;
default "false";
type uint8;
default 0;
description
"Allow equal-cost multi-path.";
}