]> git.proxmox.com Git - mirror_frr.git/blobdiff - babeld/route.c
Merge pull request #5793 from ton31337/fix/formatting_show_bgp_summary_failed
[mirror_frr.git] / babeld / route.c
index fe2b9cebb7b18d7d6e8965853605a2dea90fa20f..ab104aa2b10f8216d28664b8a62132c4a81a926d 100644 (file)
@@ -1,20 +1,4 @@
-/*  
- *  This file is free software: you may copy, redistribute and/or modify it  
- *  under the terms of the GNU General Public License as published by the  
- *  Free Software Foundation, either version 2 of the License, or (at your  
- *  option) any later version.  
- *  
- *  This file is distributed in the hope that it will be useful, but  
- *  WITHOUT ANY WARRANTY; without even the implied warranty of  
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU  
- *  General Public License for more details.  
- *  
- *  You should have received a copy of the GNU General Public License  
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.  
- *  
- * This file incorporates work covered by the following copyright and  
- * permission notice:  
- *  
+/*
 Copyright (c) 2007, 2008 by Juliusz Chroboczek
 Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
 
@@ -50,17 +34,20 @@ THE SOFTWARE.
 #include "xroute.h"
 #include "message.h"
 #include "resend.h"
+#include "babel_errors.h"
 
 static void consider_route(struct babel_route *route);
 
 struct babel_route **routes = NULL;
 static int route_slots = 0, max_route_slots = 0;
 int kernel_metric = 0;
-int allow_duplicates = -1;
-int diversity_kind = DIVERSITY_NONE;
-int diversity_factor = 256;     /* in units of 1/256 */
+enum babel_diversity diversity_kind = DIVERSITY_NONE;
+int diversity_factor = BABEL_DEFAULT_DIVERSITY_FACTOR;
 int keep_unfeasible = 0;
 
+int smoothing_half_life = 0;
+static int two_to_the_one_over_hl = 0; /* 2^(1/hl) * 0x10000 */
+
 /* We maintain a list of "slots", ordered by prefix.  Every slot
    contains a linked list of the routes to this prefix, with the
    installed route, if any, at the head of the list. */
@@ -190,6 +177,7 @@ insert_route(struct babel_route *route)
             resize_route_table(max_route_slots < 1 ? 8 : 2 * max_route_slots);
         if(route_slots >= max_route_slots)
             return NULL;
+        assert(routes);
         route->next = NULL;
         if(n < route_slots)
             memmove(routes + n + 1, routes + n,
@@ -260,7 +248,7 @@ flush_route(struct babel_route *route)
 }
 
 void
-flush_all_routes()
+flush_all_routes(void)
 {
     int i;
 
@@ -270,7 +258,7 @@ flush_all_routes()
         while(i < route_slots) {
         /* Uninstall first, to avoid calling route_lost. */
             if(routes[i]->installed)
-                uninstall_route(routes[0]);
+                uninstall_route(routes[i]);
             flush_route(routes[i]);
         }
         i--;
@@ -324,30 +312,58 @@ flush_interface_routes(struct interface *ifp, int v4only)
     }
 }
 
-/* Iterate a function over all routes. */
-void
-for_all_routes(void (*f)(struct babel_route*, void*), void *closure)
+struct route_stream {
+    int installed;
+    int index;
+    struct babel_route *next;
+};
+
+
+struct route_stream *
+route_stream(int installed)
 {
-    int i;
+    struct route_stream *stream;
 
-    for(i = 0; i < route_slots; i++) {
-        struct babel_route *r = routes[i];
-        while(r) {
-            (*f)(r, closure);
-            r = r->next;
+    stream = malloc(sizeof(struct route_stream));
+    if(stream == NULL)
+       return NULL;
+
+    stream->installed = installed;
+    stream->index = installed ? 0 : -1;
+    stream->next = NULL;
+
+    return stream;
+}
+
+struct babel_route *
+route_stream_next(struct route_stream *stream)
+{
+    if(stream->installed) {
+        while(stream->index < route_slots && !routes[stream->index]->installed)
+            stream->index++;
+
+        if(stream->index < route_slots)
+            return routes[stream->index++];
+        else
+            return NULL;
+    } else {
+        struct babel_route *next;
+        if(!stream->next) {
+            stream->index++;
+            if(stream->index >= route_slots)
+                return NULL;
+            stream->next = routes[stream->index];
         }
+        next = stream->next;
+        stream->next = next->next;
+        return next;
     }
 }
 
 void
-for_all_installed_routes(void (*f)(struct babel_route*, void*), void *closure)
+route_stream_done(struct route_stream *stream)
 {
-    int i;
-
-    for(i = 0; i < route_slots; i++) {
-        if(routes[i]->installed)
-            (*f)(routes[i], closure);
-    }
+    free(stream);
 }
 
 static int
@@ -383,15 +399,16 @@ install_route(struct babel_route *route)
         return;
 
     if(!route_feasible(route))
-        zlog_err("WARNING: installing unfeasible route "
-                 "(this shouldn't happen).");
+        flog_err(EC_BABEL_ROUTE, "WARNING: installing unfeasible route "
+                  "(this shouldn't happen).");
 
     i = find_route_slot(route->src->prefix, route->src->plen, NULL);
     assert(i >= 0 && i < route_slots);
 
     if(routes[i] != route && routes[i]->installed) {
-        fprintf(stderr, "WARNING: attempting to install duplicate route "
-                "(this shouldn't happen).");
+        flog_err(EC_BABEL_ROUTE,
+                 "WARNING: attempting to install duplicate route "
+                  "(this shouldn't happen).");
         return;
     }
 
@@ -401,7 +418,8 @@ install_route(struct babel_route *route)
                       metric_to_kernel(route_metric(route)), NULL, 0, 0);
     if(rc < 0) {
         int save = errno;
-        zlog_err("kernel_route(ADD): %s", safe_strerror(errno));
+        flog_err(EC_BABEL_ROUTE, "kernel_route(ADD): %s",
+                 safe_strerror(errno));
         if(save != EEXIST)
             return;
     }
@@ -423,7 +441,8 @@ uninstall_route(struct babel_route *route)
                       route->neigh->ifp->ifindex,
                       metric_to_kernel(route_metric(route)), NULL, 0, 0);
     if(rc < 0)
-        zlog_err("kernel_route(FLUSH): %s", safe_strerror(errno));
+        flog_err(EC_BABEL_ROUTE, "kernel_route(FLUSH): %s",
+                 safe_strerror(errno));
 
     route->installed = 0;
 }
@@ -446,8 +465,8 @@ switch_routes(struct babel_route *old, struct babel_route *new)
         return;
 
     if(!route_feasible(new))
-        zlog_err("WARNING: switching to unfeasible route "
-                 "(this shouldn't happen).");
+        flog_err(EC_BABEL_ROUTE, "WARNING: switching to unfeasible route "
+                  "(this shouldn't happen).");
 
     rc = kernel_route(ROUTE_MODIFY, old->src->prefix, old->src->plen,
                       old->nexthop, old->neigh->ifp->ifindex,
@@ -455,7 +474,8 @@ switch_routes(struct babel_route *old, struct babel_route *new)
                       new->nexthop, new->neigh->ifp->ifindex,
                       metric_to_kernel(route_metric(new)));
     if(rc < 0) {
-        zlog_err("kernel_route(MODIFY): %s", safe_strerror(errno));
+        flog_err(EC_BABEL_ROUTE, "kernel_route(MODIFY): %s",
+                 safe_strerror(errno));
         return;
     }
 
@@ -483,19 +503,30 @@ change_route_metric(struct babel_route *route,
                           route->nexthop, route->neigh->ifp->ifindex,
                           new);
         if(rc < 0) {
-            zlog_err("kernel_route(MODIFY metric): %s", safe_strerror(errno));
+            flog_err(EC_BABEL_ROUTE, "kernel_route(MODIFY metric): %s",
+                     safe_strerror(errno));
             return;
         }
     }
 
+    /* Update route->smoothed_metric using the old metric. */
+    route_smoothed_metric(route);
+
     route->refmetric = refmetric;
     route->cost = cost;
     route->add_metric = add;
+
+    if(smoothing_half_life == 0) {
+        route->smoothed_metric = route_metric(route);
+        route->smoothed_metric_time = babel_now.tv_sec;
+    }
 }
 
 static void
 retract_route(struct babel_route *route)
 {
+    /* We cannot simply remove the route from the kernel, as that might
+       cause a routing loop -- see RFC 6126 Sections 2.8 and 3.5.5. */
     change_route_metric(route, INFINITY, INFINITY, 0);
 }
 
@@ -556,10 +587,9 @@ route_interferes(struct babel_route *route, struct interface *ifp)
             }
         }
         return 0;
-    default:
-        fprintf(stderr, "Unknown kind of diversity.\n");
-        return 1;
     }
+
+    return 1;
 }
 
 int
@@ -581,7 +611,84 @@ update_feasible(struct source *src,
             (src->seqno == seqno && refmetric < src->metric));
 }
 
-/* This returns the feasible route with the smallest metric. */
+void
+change_smoothing_half_life(int half_life)
+{
+    if(half_life <= 0) {
+        smoothing_half_life = 0;
+        two_to_the_one_over_hl = 0;
+        return;
+    }
+
+    smoothing_half_life = half_life;
+    switch(smoothing_half_life) {
+    case 1: two_to_the_one_over_hl = 131072; break;
+    case 2: two_to_the_one_over_hl = 92682; break;
+    case 3: two_to_the_one_over_hl = 82570; break;
+    case 4: two_to_the_one_over_hl = 77935; break;
+    default:
+        /* 2^(1/x) is 1 + log(2)/x + O(1/x^2) at infinity. */
+        two_to_the_one_over_hl = 0x10000 + 45426 / half_life;
+    }
+}
+
+/* Update the smoothed metric, return the new value. */
+int
+route_smoothed_metric(struct babel_route *route)
+{
+    int metric = route_metric(route);
+
+    if(smoothing_half_life <= 0 ||                 /* no smoothing */
+       metric >= INFINITY ||                       /* route retracted */
+       route->smoothed_metric_time > babel_now.tv_sec || /* clock stepped */
+       route->smoothed_metric == metric) {         /* already converged */
+        route->smoothed_metric = metric;
+        route->smoothed_metric_time = babel_now.tv_sec;
+    } else {
+        int diff;
+        /* We randomise the computation, to minimise global synchronisation
+           and hence oscillations. */
+        while(route->smoothed_metric_time <=
+              babel_now.tv_sec - smoothing_half_life) {
+            diff = metric - route->smoothed_metric;
+            route->smoothed_metric += roughly(diff) / 2;
+            route->smoothed_metric_time += smoothing_half_life;
+        }
+        while(route->smoothed_metric_time < babel_now.tv_sec) {
+            diff = metric - route->smoothed_metric;
+            route->smoothed_metric +=
+                roughly(diff) * (two_to_the_one_over_hl - 0x10000) / 0x10000;
+            route->smoothed_metric_time++;
+        }
+
+        diff = metric - route->smoothed_metric;
+        if(diff > -4 && diff < 4)
+            route->smoothed_metric = metric;
+    }
+
+    /* change_route_metric relies on this */
+    assert(route->smoothed_metric_time == babel_now.tv_sec);
+    return route->smoothed_metric;
+}
+
+static int
+route_acceptable(struct babel_route *route, int feasible,
+                 struct neighbour *exclude)
+{
+    if(route_expired(route))
+        return 0;
+    if(feasible && !route_feasible(route))
+        return 0;
+    if(exclude && route->neigh == exclude)
+        return 0;
+    return 1;
+}
+
+/* Find the best route according to the weak ordering.  Any
+   linearisation of the strong ordering (see consider_route) will do,
+   we use sm <= sm'.  We could probably use a lexical ordering, but
+   that's probably overkill. */
+
 struct babel_route *
 find_best_route(const unsigned char *prefix, unsigned char plen, int feasible,
                 struct neighbour *exclude)
@@ -593,16 +700,20 @@ find_best_route(const unsigned char *prefix, unsigned char plen, int feasible,
         return NULL;
 
     route = routes[i];
+    while(route && !route_acceptable(route, feasible, exclude))
+        route = route->next;
+
+    if(!route)
+        return NULL;
 
     r = route->next;
     while(r) {
-        if(!route_expired(r) &&
-           (!feasible || route_feasible(r)) &&
-           (!exclude || r->neigh != exclude) &&
-           (route_metric(r) < route_metric(route)))
+        if(route_acceptable(r, feasible, exclude) &&
+           (route_smoothed_metric(r) < route_smoothed_metric(route)))
             route = r;
         r = r->next;
     }
+
     return route;
 }
 
@@ -610,6 +721,7 @@ void
 update_route_metric(struct babel_route *route)
 {
     int oldmetric = route_metric(route);
+    int old_smoothed_metric = route_smoothed_metric(route);
 
     if(route_expired(route)) {
         if(route->refmetric < INFINITY) {
@@ -626,13 +738,14 @@ update_route_metric(struct babel_route *route)
                                       neigh->ifp->ifindex);
         change_route_metric(route, route->refmetric,
                             neighbour_cost(route->neigh), add_metric);
-        if(route_metric(route) != oldmetric)
+        if(route_metric(route) != oldmetric ||
+           route_smoothed_metric(route) != old_smoothed_metric)
             route_changed(route, route->src, oldmetric);
     }
 }
 
 /* Called whenever a neighbour's cost changes, to update the metric of
-   all routes through that neighbour.  Calls local_notify_neighbour. */
+   all routes through that neighbour. */
 void
 update_neighbour_metric(struct neighbour *neigh, int changed)
 {
@@ -685,8 +798,8 @@ update_route(const unsigned char *router_id,
         return NULL;
 
     if(martian_prefix(prefix, plen)) {
-        zlog_err("Rejecting martian route to %s through %s.",
-                 format_prefix(prefix, plen), format_address(router_id));
+        flog_err(EC_BABEL_ROUTE, "Rejecting martian route to %s through %s.",
+                 format_prefix(prefix, plen), format_address(nexthop));
         return NULL;
     }
 
@@ -725,9 +838,9 @@ update_route(const unsigned char *router_id,
             debugf(BABEL_DEBUG_COMMON,"Unfeasible update for installed route to %s "
                    "(%s %d %d -> %s %d %d).",
                    format_prefix(src->prefix, src->plen),
-                   format_address(route->src->id),
+                   format_eui64(route->src->id),
                    route->seqno, route->refmetric,
-                   format_address(src->id), seqno, refmetric);
+                   format_eui64(src->id), seqno, refmetric);
             if(src != route->src) {
                 uninstall_route(route);
                 lost = 1;
@@ -738,6 +851,12 @@ update_route(const unsigned char *router_id,
         if((feasible || keep_unfeasible) && refmetric < INFINITY)
             route->time = babel_now.tv_sec;
         route->seqno = seqno;
+
+        memset(&route->channels, 0, sizeof(route->channels));
+        if(channels_len > 0)
+            memcpy(&route->channels, channels,
+                   MIN(channels_len, DIVERSITY_HOPS));
+
         change_route_metric(route,
                             refmetric, neighbour_cost(neigh), add_metric);
         route->hold_time = hold_time;
@@ -777,6 +896,8 @@ update_route(const unsigned char *router_id,
         memcpy(route->nexthop, nexthop, 16);
         route->time = babel_now.tv_sec;
         route->hold_time = hold_time;
+        route->smoothed_metric = MAX(route_metric(route), INFINITY / 2);
+        route->smoothed_metric_time = babel_now.tv_sec;
         route->installed = 0;
         memset(&route->channels, 0, sizeof(route->channels));
         if(channels_len > 0)
@@ -785,7 +906,7 @@ update_route(const unsigned char *router_id,
         route->next = NULL;
         new_route = insert_route(route);
         if(new_route == NULL) {
-            fprintf(stderr, "Couldn't insert route.\n");
+            flog_err(EC_BABEL_ROUTE, "Couldn't insert route.");
             free(route);
             return NULL;
         }
@@ -817,7 +938,11 @@ send_unfeasible_request(struct neighbour *neigh, int force,
     }
 }
 
-/* This takes a feasible route and decides whether to install it. */
+/* This takes a feasible route and decides whether to install it.
+   This uses the strong ordering, which is defined by sm <= sm' AND
+   m <= m'.  This ordering is not total, which is what causes
+   hysteresis. */
+
 static void
 consider_route(struct babel_route *route)
 {
@@ -831,7 +956,7 @@ consider_route(struct babel_route *route)
         return;
 
     xroute = find_xroute(route->src->prefix, route->src->plen);
-    if(xroute && (allow_duplicates < 0 || xroute->metric >= allow_duplicates))
+    if(xroute)
         return;
 
     installed = find_installed_route(route->src->prefix, route->src->plen);
@@ -845,7 +970,8 @@ consider_route(struct babel_route *route)
     if(route_metric(installed) >= INFINITY)
         goto install;
 
-    if(route_metric(installed) >= route_metric(route) + 64)
+    if(route_metric(installed) >= route_metric(route) &&
+       route_smoothed_metric(installed) > route_smoothed_metric(route))
         goto install;
 
     return;
@@ -877,7 +1003,6 @@ retract_neighbour_routes(struct neighbour *neigh)
             }
             r = r->next;
         }
-        i++;
     }
 }
 
@@ -945,18 +1070,17 @@ route_changed(struct babel_route *route,
               struct source *oldsrc, unsigned short oldmetric)
 {
     if(route->installed) {
-        if(route_metric(route) > oldmetric) {
-            struct babel_route *better_route;
-            better_route =
-                find_best_route(route->src->prefix, route->src->plen, 1, NULL);
-            if(better_route &&
-               route_metric(better_route) <= route_metric(route) - 96)
-                consider_route(better_route);
-        }
+        struct babel_route *better_route;
+        /* Do this unconditionally -- microoptimisation is not worth it. */
+        better_route =
+            find_best_route(route->src->prefix, route->src->plen, 1, NULL);
+        if(better_route && route_metric(better_route) < route_metric(route))
+            consider_route(better_route);
+    }
 
-        if(route->installed)
-            /* We didn't change routes after all. */
-            send_triggered_update(route, oldsrc, oldmetric);
+    if(route->installed) {
+        /* We didn't change routes after all. */
+        send_triggered_update(route, oldsrc, oldmetric);
     } else {
         /* Reconsider routes even when their metric didn't decrease,
            they may not have been feasible before. */
@@ -973,12 +1097,16 @@ route_lost(struct source *src, unsigned oldmetric)
     if(new_route) {
         consider_route(new_route);
     } else if(oldmetric < INFINITY) {
-        /* Complain loudly. */
+        /* Avoid creating a blackhole. */
         send_update_resend(NULL, src->prefix, src->plen);
-        send_request_resend(NULL, src->prefix, src->plen,
-                            src->metric >= INFINITY ?
-                            src->seqno : seqno_plus(src->seqno, 1),
-                            src->id);
+        /* If the route was usable enough, try to get an alternate one.
+           If it was not, we could be dealing with oscillations around
+           the value of INFINITY. */
+        if(oldmetric <= INFINITY / 2)
+            send_request_resend(NULL, src->prefix, src->plen,
+                                src->metric >= INFINITY ?
+                                src->seqno : seqno_plus(src->seqno, 1),
+                                src->id);
     }
 }