diff options
author | Donatas Abraitis <donatas@opensourcerouting.org> | 2023-05-04 08:13:07 +0200 |
---|---|---|
committer | Donatas Abraitis <donatas@opensourcerouting.org> | 2023-05-04 14:43:02 +0200 |
commit | 75fce4645a7cf0a93ef0109d69365f51b84bc47c (patch) | |
tree | 3827f8977faca9c92dcb3513bceb43a7a264b392 /ripd | |
parent | doc: Add RIP `allow-ecmp` command (diff) | |
download | frr-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.c | 40 | ||||
-rw-r--r-- | ripd/rip_nb_config.c | 8 | ||||
-rw-r--r-- | ripd/ripd.c | 62 | ||||
-rw-r--r-- | ripd/ripd.h | 4 |
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 */ |