]> git.proxmox.com Git - mirror_frr.git/commitdiff
ripd: Implement `allow-ecmp X` command
authorDonatas Abraitis <donatas@opensourcerouting.org>
Thu, 4 May 2023 06:13:07 +0000 (09:13 +0300)
committerDonatas Abraitis <donatas@opensourcerouting.org>
Thu, 4 May 2023 12:43:02 +0000 (15:43 +0300)
Allow setting an arbitrary number of paths to be installed instead of ALL.

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
ripd/rip_cli.c
ripd/rip_nb_config.c
ripd/ripd.c
ripd/ripd.h
yang/example/ripd.json
yang/example/ripd.xml
yang/frr-ripd.yang

index ac9fc4b1a818ba651e60f633bdbc5dee2bd0ddb5..6f45bb5d9eeacbde2d68f9654a03e085e73b5b71 100644 (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",
+       "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);
index 8fe34705ca1ea38fa9d8fa91fbac2accf9fb6ab8..19578d56c7a37915c31f5d37aed9fc2e3c1b0084 100644 (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;
 }
index cb8dd4945d4f7ccdb5a7f6aeb4c63c031f439afe..3e8f8a934084eaaa7a591a412e45badce3a91b92 100644 (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 =
index bba3c280697b9a080625371a8ff5c9ab52b83c60..2db1e5a6b0ee62bb956c03b5fc28374f7da2e569 100644 (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 */
index 00040622e5d7e7c1e6bf9702c9dc3ecf354758e0..799f46a6dc7ed9bcae452c6e88ac0ef9d47cd549 100644 (file)
@@ -23,7 +23,7 @@
         "instance": [
             {
                 "vrf": "default",
-                "allow-ecmp": "true",
+                "allow-ecmp": 1,
                 "distance": {
                     "source": [
                         {
index 2feddde2d86b8fc263d8fd751a4fe3bc7f3d1cad..dad83619cea6f504e3dcfdffd8af0fdd7282fe45 100644 (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>
index a4bf50d958be3807c341eb3428f573d75e1e5109..5f85a4cabc1b092f1f7cf38439176ec934468301 100644 (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.";
       }