summaryrefslogtreecommitdiffstats
path: root/ripd
diff options
context:
space:
mode:
authorDonatas Abraitis <donatas@opensourcerouting.org>2023-05-04 08:13:07 +0200
committerDonatas Abraitis <donatas@opensourcerouting.org>2023-05-04 14:43:02 +0200
commit75fce4645a7cf0a93ef0109d69365f51b84bc47c (patch)
tree3827f8977faca9c92dcb3513bceb43a7a264b392 /ripd
parentdoc: Add RIP `allow-ecmp` command (diff)
downloadfrr-75fce4645a7cf0a93ef0109d69365f51b84bc47c.tar.xz
frr-75fce4645a7cf0a93ef0109d69365f51b84bc47c.zip
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>
Diffstat (limited to 'ripd')
-rw-r--r--ripd/rip_cli.c40
-rw-r--r--ripd/rip_nb_config.c8
-rw-r--r--ripd/ripd.c62
-rw-r--r--ripd/ripd.h4
4 files changed, 102 insertions, 12 deletions
diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c
index ac9fc4b1a..6f45bb5d9 100644
--- a/ripd/rip_cli.c
+++ b/ripd/rip_cli.c
@@ -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",
+ "allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]",
+ "Allow Equal Cost MultiPath\n"
+ "Number of paths\n")
+{
+ 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")
+ "Allow Equal Cost MultiPath\n"
+ "Number of paths\n")
{
- nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY,
- no ? "false" : "true");
+ 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;
+
+ paths = yang_dnode_get_uint8(dnode, NULL);
- vty_out(vty, " allow-ecmp\n");
+ 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);
diff --git a/ripd/rip_nb_config.c b/ripd/rip_nb_config.c
index 8fe34705c..19578d56c 100644
--- a/ripd/rip_nb_config.c
+++ b/ripd/rip_nb_config.c
@@ -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;
}
diff --git a/ripd/ripd.c b/ripd/ripd.c
index cb8dd4945..3e8f8a934 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -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 =
diff --git a/ripd/ripd.h b/ripd/ripd.h
index bba3c2806..2db1e5a6b 100644
--- a/ripd/ripd.h
+++ b/ripd/ripd.h
@@ -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 */