Allow setting an arbitrary number of paths to be installed instead of ALL.
Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
/*
* 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);
}
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);
}
/*
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);
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;
}
{
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();
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);
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)
{
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 =
struct route_table *distance_table;
/* RIP ECMP flag */
- bool ecmp;
+ uint8_t ecmp;
/* Are we in passive-interface default mode? */
bool passive_default;
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 */
"instance": [
{
"vrf": "default",
- "allow-ecmp": "true",
+ "allow-ecmp": 1,
"distance": {
"source": [
{
<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>
"VRF name.";
}
leaf allow-ecmp {
- type boolean;
- default "false";
+ type uint8;
+ default 0;
description
"Allow equal-cost multi-path.";
}