]> git.proxmox.com Git - mirror_frr.git/blobdiff - ripd/ripd.c
Merge pull request #13555 from LabNConsulting/aceelindem/ospf-p2mp-delayed-reflooding...
[mirror_frr.git] / ripd / ripd.c
index 7dbe2bbccf7d696bf0f127bc5049fa8e5e307670..04a8cad560fe25ae06be0cde144b5dfd9e8e3017 100644 (file)
@@ -34,6 +34,7 @@
 
 #include "ripd/ripd.h"
 #include "ripd/rip_nb.h"
+#include "ripd/rip_bfd.h"
 #include "ripd/rip_debug.h"
 #include "ripd/rip_errors.h"
 #include "ripd/rip_interface.h"
@@ -155,7 +156,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 +170,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);
@@ -1123,7 +1154,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
        if (from->sin_port != htons(RIP_PORT_DEFAULT)) {
                zlog_info("response doesn't come from RIP port: %d",
                          from->sin_port);
-               rip_peer_bad_packet(rip, from);
+               rip_peer_bad_packet(rip, ri, from);
                return;
        }
 
@@ -1137,7 +1168,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
                zlog_info(
                        "This datagram doesn't come from a valid neighbor: %pI4",
                        &from->sin_addr);
-               rip_peer_bad_packet(rip, from);
+               rip_peer_bad_packet(rip, ri, from);
                return;
        }
 
@@ -1147,7 +1178,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
        ; /* Alredy done in rip_read () */
 
        /* Update RIP peer. */
-       rip_peer_update(rip, from, packet->version);
+       rip_peer_update(rip, ri, from, packet->version);
 
        /* Set RTE pointer. */
        rte = packet->rte;
@@ -1171,12 +1202,18 @@ static void rip_response_process(struct rip_packet *packet, int size,
                        continue;
                }
 
+               if (packet->version == RIPv1 && rte->tag != 0) {
+                       zlog_warn("RIPv1 reserved field is nonzero: %d",
+                                 ntohs(rte->tag));
+                       continue;
+               }
+
                /* - is the destination address valid (e.g., unicast; not net 0
                   or 127) */
                if (!rip_destination_check(rte->prefix)) {
                        zlog_info(
                                "Network is net 0 or net 127 or it is not unicast network");
-                       rip_peer_bad_route(rip, from);
+                       rip_peer_bad_route(rip, ri, from);
                        continue;
                }
 
@@ -1186,7 +1223,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
                /* - is the metric valid (i.e., between 1 and 16, inclusive) */
                if (!(rte->metric >= 1 && rte->metric <= 16)) {
                        zlog_info("Route's metric is not in the 1-16 range.");
-                       rip_peer_bad_route(rip, from);
+                       rip_peer_bad_route(rip, ri, from);
                        continue;
                }
 
@@ -1195,7 +1232,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
                    && rte->nexthop.s_addr != INADDR_ANY) {
                        zlog_info("RIPv1 packet with nexthop value %pI4",
                                  &rte->nexthop);
-                       rip_peer_bad_route(rip, from);
+                       rip_peer_bad_route(rip, ri, from);
                        continue;
                }
 
@@ -1326,7 +1363,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
                        zlog_warn(
                                "RIPv2 address %pI4 is not mask /%d applied one",
                                &rte->prefix, ip_masklen(rte->mask));
-                       rip_peer_bad_route(rip, from);
+                       rip_peer_bad_route(rip, ri, from);
                        continue;
                }
 
@@ -1643,7 +1680,7 @@ static void rip_request_process(struct rip_packet *packet, int size,
                return;
 
        /* RIP peer update. */
-       rip_peer_update(rip, from, packet->version);
+       rip_peer_update(rip, ri, from, packet->version);
 
        lim = ((caddr_t)packet) + size;
        rte = packet->rte;
@@ -1711,7 +1748,7 @@ static void rip_read(struct event *t)
        socklen_t fromlen;
        struct interface *ifp = NULL;
        struct connected *ifc;
-       struct rip_interface *ri;
+       struct rip_interface *ri = NULL;
        struct prefix p;
 
        /* Fetch socket then register myself. */
@@ -1743,8 +1780,10 @@ static void rip_read(struct event *t)
        /* Which interface is this packet comes from. */
        ifc = if_lookup_address((void *)&from.sin_addr, AF_INET,
                                rip->vrf->vrf_id);
-       if (ifc)
+       if (ifc) {
                ifp = ifc->ifp;
+               ri = ifp->info;
+       }
 
        /* RIP packet received */
        if (IS_RIP_DEBUG_EVENT)
@@ -1753,7 +1792,7 @@ static void rip_read(struct event *t)
                           ifp ? ifp->name : "unknown", rip->vrf_name);
 
        /* If this packet come from unknown interface, ignore it. */
-       if (ifp == NULL) {
+       if (ifp == NULL || ri == NULL) {
                zlog_info(
                        "%s: cannot find interface for packet from %pI4 port %d (VRF %s)",
                        __func__, &from.sin_addr, ntohs(from.sin_port),
@@ -1779,13 +1818,13 @@ static void rip_read(struct event *t)
        if (len < RIP_PACKET_MINSIZ) {
                zlog_warn("packet size %d is smaller than minimum size %d", len,
                          RIP_PACKET_MINSIZ);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
        if (len > RIP_PACKET_MAXSIZ) {
                zlog_warn("packet size %d is larger than max size %d", len,
                          RIP_PACKET_MAXSIZ);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1793,7 +1832,7 @@ static void rip_read(struct event *t)
        if ((len - RIP_PACKET_MINSIZ) % 20) {
                zlog_warn("packet size %d is wrong for RIP packet alignment",
                          len);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1807,7 +1846,7 @@ static void rip_read(struct event *t)
        if (packet->version == 0) {
                zlog_info("version 0 with command %d received.",
                          packet->command);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1823,12 +1862,11 @@ static void rip_read(struct event *t)
                packet->version = RIPv2;
 
        /* Is RIP running or is this RIP neighbor ?*/
-       ri = ifp->info;
        if (!ri->running && !rip_neighbor_lookup(rip, &from)) {
                if (IS_RIP_DEBUG_EVENT)
                        zlog_debug("RIP is not enabled on interface %s.",
                                   ifp->name);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1842,7 +1880,7 @@ static void rip_read(struct event *t)
                        zlog_debug(
                                "  packet's v%d doesn't fit to if version spec",
                                packet->version);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1857,7 +1895,7 @@ static void rip_read(struct event *t)
                                "packet RIPv%d is dropped because authentication disabled",
                                packet->version);
                ripd_notif_send_auth_type_failure(ifp->name);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                return;
        }
 
@@ -1893,7 +1931,7 @@ static void rip_read(struct event *t)
                                zlog_debug(
                                        "RIPv1 dropped because authentication enabled");
                        ripd_notif_send_auth_type_failure(ifp->name);
-                       rip_peer_bad_packet(rip, &from);
+                       rip_peer_bad_packet(rip, ri, &from);
                        return;
                }
        } else if (ri->auth_type != RIP_NO_AUTH) {
@@ -1906,7 +1944,7 @@ static void rip_read(struct event *t)
                                zlog_debug(
                                        "RIPv2 authentication failed: no auth RTE in packet");
                        ripd_notif_send_auth_type_failure(ifp->name);
-                       rip_peer_bad_packet(rip, &from);
+                       rip_peer_bad_packet(rip, ri, &from);
                        return;
                }
 
@@ -1916,7 +1954,7 @@ static void rip_read(struct event *t)
                                zlog_debug(
                                        "RIPv2 dropped because authentication enabled");
                        ripd_notif_send_auth_type_failure(ifp->name);
-                       rip_peer_bad_packet(rip, &from);
+                       rip_peer_bad_packet(rip, ri, &from);
                        return;
                }
 
@@ -1952,7 +1990,7 @@ static void rip_read(struct event *t)
                                zlog_debug("RIPv2 %s authentication failure",
                                           auth_desc);
                        ripd_notif_send_auth_failure(ifp->name);
-                       rip_peer_bad_packet(rip, &from);
+                       rip_peer_bad_packet(rip, ri, &from);
                        return;
                }
        }
@@ -1971,16 +2009,16 @@ static void rip_read(struct event *t)
                zlog_info(
                        "Obsolete command %s received, please sent it to routed",
                        lookup_msg(rip_msg, packet->command, NULL));
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                break;
        case RIP_POLL_ENTRY:
                zlog_info("Obsolete command %s received",
                          lookup_msg(rip_msg, packet->command, NULL));
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                break;
        default:
                zlog_info("Unknown RIP command %d received", packet->command);
-               rip_peer_bad_packet(rip, &from);
+               rip_peer_bad_packet(rip, ri, &from);
                break;
        }
 }
@@ -2624,6 +2662,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)
 {
@@ -2633,7 +2701,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 =
@@ -3204,9 +3272,6 @@ static int config_write_rip(struct vty *vty)
                /* Distribute configuration. */
                config_write_distribute(vty, rip->distribute_ctx);
 
-               /* Interface routemap configuration */
-               config_write_if_rmap(vty, rip->if_rmap_ctx);
-
                vty_out(vty, "exit\n");
 
                write = 1;
@@ -3342,6 +3407,7 @@ void rip_clean(struct rip *rip)
        route_table_finish(rip->distance_table);
 
        RB_REMOVE(rip_instance_head, &rip_instances, rip);
+       XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
        XFREE(MTYPE_RIP_VRF_NAME, rip->vrf_name);
        XFREE(MTYPE_RIP, rip);
 }