mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-03 23:47:16 +00:00 
			
		
		
		
	Done with a combination of regex'ing and banging my head against a wall. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
		
			
				
	
	
		
			1130 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1130 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: MIT
 | 
						|
/*
 | 
						|
Copyright (c) 2007, 2008 by Juliusz Chroboczek
 | 
						|
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
 | 
						|
*/
 | 
						|
 | 
						|
#include <zebra.h>
 | 
						|
#include "if.h"
 | 
						|
 | 
						|
#include "babeld.h"
 | 
						|
#include "util.h"
 | 
						|
#include "kernel.h"
 | 
						|
#include "babel_interface.h"
 | 
						|
#include "source.h"
 | 
						|
#include "neighbour.h"
 | 
						|
#include "route.h"
 | 
						|
#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;
 | 
						|
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. */
 | 
						|
 | 
						|
static int
 | 
						|
route_compare(const unsigned char *prefix, unsigned char plen,
 | 
						|
               struct babel_route *route)
 | 
						|
{
 | 
						|
    int i = memcmp(prefix, route->src->prefix, 16);
 | 
						|
    if(i != 0)
 | 
						|
        return i;
 | 
						|
 | 
						|
    if(plen < route->src->plen)
 | 
						|
        return -1;
 | 
						|
    else if(plen > route->src->plen)
 | 
						|
        return 1;
 | 
						|
    else
 | 
						|
        return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Performs binary search, returns -1 in case of failure.  In the latter
 | 
						|
   case, new_return is the place where to insert the new element. */
 | 
						|
 | 
						|
static int
 | 
						|
find_route_slot(const unsigned char *prefix, unsigned char plen,
 | 
						|
                int *new_return)
 | 
						|
{
 | 
						|
    int p, m, g, c;
 | 
						|
 | 
						|
    if(route_slots < 1) {
 | 
						|
        if(new_return)
 | 
						|
            *new_return = 0;
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    p = 0; g = route_slots - 1;
 | 
						|
 | 
						|
    do {
 | 
						|
        m = (p + g) / 2;
 | 
						|
        c = route_compare(prefix, plen, routes[m]);
 | 
						|
        if(c == 0)
 | 
						|
            return m;
 | 
						|
        else if(c < 0)
 | 
						|
            g = m - 1;
 | 
						|
        else
 | 
						|
            p = m + 1;
 | 
						|
    } while(p <= g);
 | 
						|
 | 
						|
    if(new_return)
 | 
						|
        *new_return = p;
 | 
						|
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
struct babel_route *
 | 
						|
find_route(const unsigned char *prefix, unsigned char plen,
 | 
						|
           struct neighbour *neigh, const unsigned char *nexthop)
 | 
						|
{
 | 
						|
    struct babel_route *route;
 | 
						|
    int i = find_route_slot(prefix, plen, NULL);
 | 
						|
 | 
						|
    if(i < 0)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    route = routes[i];
 | 
						|
 | 
						|
    while(route) {
 | 
						|
        if(route->neigh == neigh && memcmp(route->nexthop, nexthop, 16) == 0)
 | 
						|
            return route;
 | 
						|
        route = route->next;
 | 
						|
    }
 | 
						|
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
struct babel_route *
 | 
						|
find_installed_route(const unsigned char *prefix, unsigned char plen)
 | 
						|
{
 | 
						|
    int i = find_route_slot(prefix, plen, NULL);
 | 
						|
 | 
						|
    if(i >= 0 && routes[i]->installed)
 | 
						|
        return routes[i];
 | 
						|
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* Returns an overestimate of the number of installed routes. */
 | 
						|
int
 | 
						|
installed_routes_estimate(void)
 | 
						|
{
 | 
						|
    return route_slots;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
resize_route_table(int new_slots)
 | 
						|
{
 | 
						|
    struct babel_route **new_routes;
 | 
						|
    assert(new_slots >= route_slots);
 | 
						|
 | 
						|
    if(new_slots == 0) {
 | 
						|
        new_routes = NULL;
 | 
						|
        free(routes);
 | 
						|
    } else {
 | 
						|
        new_routes = realloc(routes, new_slots * sizeof(struct babel_route*));
 | 
						|
        if(new_routes == NULL)
 | 
						|
            return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    max_route_slots = new_slots;
 | 
						|
    routes = new_routes;
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
/* Insert a route into the table.  If successful, retains the route.
 | 
						|
   On failure, caller must free the route. */
 | 
						|
static struct babel_route *
 | 
						|
insert_route(struct babel_route *route)
 | 
						|
{
 | 
						|
    int i, n = 0;
 | 
						|
 | 
						|
    assert(!route->installed);
 | 
						|
 | 
						|
    i = find_route_slot(route->src->prefix, route->src->plen, &n);
 | 
						|
 | 
						|
    if(i < 0) {
 | 
						|
        if(route_slots >= max_route_slots)
 | 
						|
            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,
 | 
						|
                    (route_slots - n) * sizeof(struct babel_route*));
 | 
						|
        route_slots++;
 | 
						|
        routes[n] = route;
 | 
						|
    } else {
 | 
						|
        struct babel_route *r;
 | 
						|
        r = routes[i];
 | 
						|
        while(r->next)
 | 
						|
            r = r->next;
 | 
						|
        r->next = route;
 | 
						|
        route->next = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    return route;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
flush_route(struct babel_route *route)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
    struct source *src;
 | 
						|
    unsigned oldmetric;
 | 
						|
    int lost = 0;
 | 
						|
 | 
						|
    oldmetric = route_metric(route);
 | 
						|
    src = route->src;
 | 
						|
 | 
						|
    if(route->installed) {
 | 
						|
        uninstall_route(route);
 | 
						|
        lost = 1;
 | 
						|
    }
 | 
						|
 | 
						|
    i = find_route_slot(route->src->prefix, route->src->plen, NULL);
 | 
						|
    assert(i >= 0 && i < route_slots);
 | 
						|
 | 
						|
    if(route == routes[i]) {
 | 
						|
        routes[i] = route->next;
 | 
						|
        route->next = NULL;
 | 
						|
        free(route);
 | 
						|
 | 
						|
        if(routes[i] == NULL) {
 | 
						|
            if(i < route_slots - 1)
 | 
						|
                memmove(routes + i, routes + i + 1,
 | 
						|
                        (route_slots - i - 1) * sizeof(struct babel_route*));
 | 
						|
            routes[route_slots - 1] = NULL;
 | 
						|
            route_slots--;
 | 
						|
        }
 | 
						|
 | 
						|
        if(route_slots == 0)
 | 
						|
            resize_route_table(0);
 | 
						|
        else if(max_route_slots > 8 && route_slots < max_route_slots / 4)
 | 
						|
            resize_route_table(max_route_slots / 2);
 | 
						|
    } else {
 | 
						|
        struct babel_route *r = routes[i];
 | 
						|
        while(r->next != route)
 | 
						|
            r = r->next;
 | 
						|
        r->next = route->next;
 | 
						|
        route->next = NULL;
 | 
						|
        free(route);
 | 
						|
    }
 | 
						|
 | 
						|
    if(lost)
 | 
						|
        route_lost(src, oldmetric);
 | 
						|
 | 
						|
    release_source(src);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
flush_all_routes(void)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    /* Start from the end, to avoid shifting the table. */
 | 
						|
    i = route_slots - 1;
 | 
						|
    while(i >= 0) {
 | 
						|
        while(i < route_slots) {
 | 
						|
        /* Uninstall first, to avoid calling route_lost. */
 | 
						|
            if(routes[i]->installed)
 | 
						|
                uninstall_route(routes[i]);
 | 
						|
            flush_route(routes[i]);
 | 
						|
        }
 | 
						|
        i--;
 | 
						|
    }
 | 
						|
 | 
						|
    check_sources_released();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
flush_neighbour_routes(struct neighbour *neigh)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    i = 0;
 | 
						|
    while(i < route_slots) {
 | 
						|
        struct babel_route *r;
 | 
						|
        r = routes[i];
 | 
						|
        while(r) {
 | 
						|
            if(r->neigh == neigh) {
 | 
						|
                flush_route(r);
 | 
						|
                goto again;
 | 
						|
            }
 | 
						|
            r = r->next;
 | 
						|
        }
 | 
						|
        i++;
 | 
						|
    again:
 | 
						|
        ;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
flush_interface_routes(struct interface *ifp, int v4only)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    i = 0;
 | 
						|
    while(i < route_slots) {
 | 
						|
        struct babel_route *r;
 | 
						|
        r = routes[i];
 | 
						|
        while(r) {
 | 
						|
            if(r->neigh->ifp == ifp &&
 | 
						|
               (!v4only || v4mapped(r->nexthop))) {
 | 
						|
                flush_route(r);
 | 
						|
                goto again;
 | 
						|
            }
 | 
						|
            r = r->next;
 | 
						|
        }
 | 
						|
        i++;
 | 
						|
    again:
 | 
						|
        ;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
struct route_stream {
 | 
						|
    int installed;
 | 
						|
    int index;
 | 
						|
    struct babel_route *next;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
struct route_stream *
 | 
						|
route_stream(int installed)
 | 
						|
{
 | 
						|
    struct route_stream *stream;
 | 
						|
 | 
						|
    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
 | 
						|
route_stream_done(struct route_stream *stream)
 | 
						|
{
 | 
						|
    free(stream);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
metric_to_kernel(int metric)
 | 
						|
{
 | 
						|
    return metric < INFINITY ? kernel_metric : KERNEL_INFINITY;
 | 
						|
}
 | 
						|
 | 
						|
/* This is used to maintain the invariant that the installed route is at
 | 
						|
   the head of the list. */
 | 
						|
static void
 | 
						|
move_installed_route(struct babel_route *route, int i)
 | 
						|
{
 | 
						|
    assert(i >= 0 && i < route_slots);
 | 
						|
    assert(route->installed);
 | 
						|
 | 
						|
    if(route != routes[i]) {
 | 
						|
        struct babel_route *r = routes[i];
 | 
						|
        while(r->next != route)
 | 
						|
            r = r->next;
 | 
						|
        r->next = route->next;
 | 
						|
        route->next = routes[i];
 | 
						|
        routes[i] = route;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
install_route(struct babel_route *route)
 | 
						|
{
 | 
						|
    int i, rc;
 | 
						|
 | 
						|
    if(route->installed)
 | 
						|
        return;
 | 
						|
 | 
						|
    if(!route_feasible(route))
 | 
						|
	    flog_err(EC_BABEL_ROUTE,
 | 
						|
		     "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) {
 | 
						|
	    flog_err(
 | 
						|
		    EC_BABEL_ROUTE,
 | 
						|
		    "Attempting to install duplicate route (this shouldn't happen).");
 | 
						|
	    return;
 | 
						|
    }
 | 
						|
 | 
						|
    rc = kernel_route(ROUTE_ADD, route->src->prefix, route->src->plen,
 | 
						|
                      route->nexthop,
 | 
						|
                      route->neigh->ifp->ifindex,
 | 
						|
                      metric_to_kernel(route_metric(route)), NULL, 0, 0);
 | 
						|
    if(rc < 0) {
 | 
						|
        int save = errno;
 | 
						|
        flog_err(EC_BABEL_ROUTE, "kernel_route(ADD): %s",
 | 
						|
		  safe_strerror(errno));
 | 
						|
        if(save != EEXIST)
 | 
						|
            return;
 | 
						|
    }
 | 
						|
    route->installed = 1;
 | 
						|
    move_installed_route(route, i);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
uninstall_route(struct babel_route *route)
 | 
						|
{
 | 
						|
    int rc;
 | 
						|
 | 
						|
    if(!route->installed)
 | 
						|
        return;
 | 
						|
 | 
						|
    rc = kernel_route(ROUTE_FLUSH, route->src->prefix, route->src->plen,
 | 
						|
                      route->nexthop,
 | 
						|
                      route->neigh->ifp->ifindex,
 | 
						|
                      metric_to_kernel(route_metric(route)), NULL, 0, 0);
 | 
						|
    if(rc < 0)
 | 
						|
        flog_err(EC_BABEL_ROUTE, "kernel_route(FLUSH): %s",
 | 
						|
		  safe_strerror(errno));
 | 
						|
 | 
						|
    route->installed = 0;
 | 
						|
}
 | 
						|
 | 
						|
/* This is equivalent to uninstall_route followed with install_route,
 | 
						|
   but without the race condition.  The destination of both routes
 | 
						|
   must be the same. */
 | 
						|
 | 
						|
static void
 | 
						|
switch_routes(struct babel_route *old, struct babel_route *new)
 | 
						|
{
 | 
						|
    int rc;
 | 
						|
 | 
						|
    if(!old) {
 | 
						|
        install_route(new);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if(!old->installed)
 | 
						|
        return;
 | 
						|
 | 
						|
    if(!route_feasible(new))
 | 
						|
	    flog_err(EC_BABEL_ROUTE,
 | 
						|
		     "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,
 | 
						|
                      metric_to_kernel(route_metric(old)),
 | 
						|
                      new->nexthop, new->neigh->ifp->ifindex,
 | 
						|
                      metric_to_kernel(route_metric(new)));
 | 
						|
    if(rc < 0) {
 | 
						|
        flog_err(EC_BABEL_ROUTE, "kernel_route(MODIFY): %s",
 | 
						|
		  safe_strerror(errno));
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    old->installed = 0;
 | 
						|
    new->installed = 1;
 | 
						|
    move_installed_route(new, find_route_slot(new->src->prefix, new->src->plen,
 | 
						|
                                              NULL));
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
change_route_metric(struct babel_route *route,
 | 
						|
                    unsigned refmetric, unsigned cost, unsigned add)
 | 
						|
{
 | 
						|
    int old, new;
 | 
						|
    int newmetric = MIN(refmetric + cost + add, INFINITY);
 | 
						|
 | 
						|
    old = metric_to_kernel(route_metric(route));
 | 
						|
    new = metric_to_kernel(newmetric);
 | 
						|
 | 
						|
    if(route->installed && old != new) {
 | 
						|
        int rc;
 | 
						|
        rc = kernel_route(ROUTE_MODIFY, route->src->prefix, route->src->plen,
 | 
						|
                          route->nexthop, route->neigh->ifp->ifindex,
 | 
						|
                          old,
 | 
						|
                          route->nexthop, route->neigh->ifp->ifindex,
 | 
						|
                          new);
 | 
						|
        if(rc < 0) {
 | 
						|
            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);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
route_feasible(struct babel_route *route)
 | 
						|
{
 | 
						|
    return update_feasible(route->src, route->seqno, route->refmetric);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
route_old(struct babel_route *route)
 | 
						|
{
 | 
						|
    return route->time < babel_now.tv_sec - route->hold_time * 7 / 8;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
route_expired(struct babel_route *route)
 | 
						|
{
 | 
						|
    return route->time < babel_now.tv_sec - route->hold_time;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
channels_interfere(int ch1, int ch2)
 | 
						|
{
 | 
						|
    if(ch1 == BABEL_IF_CHANNEL_NONINTERFERING
 | 
						|
       || ch2 == BABEL_IF_CHANNEL_NONINTERFERING)
 | 
						|
        return 0;
 | 
						|
    if(ch1 == BABEL_IF_CHANNEL_INTERFERING
 | 
						|
       || ch2 == BABEL_IF_CHANNEL_INTERFERING)
 | 
						|
        return 1;
 | 
						|
    return ch1 == ch2;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
route_interferes(struct babel_route *route, struct interface *ifp)
 | 
						|
{
 | 
						|
    struct babel_interface *babel_ifp = NULL;
 | 
						|
    switch(diversity_kind) {
 | 
						|
    case DIVERSITY_NONE:
 | 
						|
        return 1;
 | 
						|
    case DIVERSITY_INTERFACE_1:
 | 
						|
        return route->neigh->ifp == ifp;
 | 
						|
    case DIVERSITY_CHANNEL_1:
 | 
						|
    case DIVERSITY_CHANNEL:
 | 
						|
        if(route->neigh->ifp == ifp)
 | 
						|
            return 1;
 | 
						|
        babel_ifp = babel_get_if_nfo(ifp);
 | 
						|
        if(channels_interfere(babel_ifp->channel,
 | 
						|
                              babel_get_if_nfo(route->neigh->ifp)->channel))
 | 
						|
            return 1;
 | 
						|
        if(diversity_kind == DIVERSITY_CHANNEL) {
 | 
						|
            int i;
 | 
						|
            for(i = 0; i < DIVERSITY_HOPS; i++) {
 | 
						|
                if(route->channels[i] == 0)
 | 
						|
                    break;
 | 
						|
                if(channels_interfere(babel_ifp->channel, route->channels[i]))
 | 
						|
                    return 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
update_feasible(struct source *src,
 | 
						|
                unsigned short seqno, unsigned short refmetric)
 | 
						|
{
 | 
						|
    if(src == NULL)
 | 
						|
        return 1;
 | 
						|
 | 
						|
    if(src->time < babel_now.tv_sec - SOURCE_GC_TIME)
 | 
						|
        /* Never mind what is probably stale data */
 | 
						|
        return 1;
 | 
						|
 | 
						|
    if(refmetric >= INFINITY)
 | 
						|
        /* Retractions are always feasible */
 | 
						|
        return 1;
 | 
						|
 | 
						|
    return (seqno_compare(seqno, src->seqno) > 0 ||
 | 
						|
            (src->seqno == seqno && refmetric < src->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)
 | 
						|
{
 | 
						|
    struct babel_route *route = NULL, *r = NULL;
 | 
						|
    int i = find_route_slot(prefix, plen, NULL);
 | 
						|
 | 
						|
    if(i < 0)
 | 
						|
        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_acceptable(r, feasible, exclude) &&
 | 
						|
           (route_smoothed_metric(r) < route_smoothed_metric(route)))
 | 
						|
            route = r;
 | 
						|
        r = r->next;
 | 
						|
    }
 | 
						|
 | 
						|
    return route;
 | 
						|
}
 | 
						|
 | 
						|
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) {
 | 
						|
            route->seqno = seqno_plus(route->src->seqno, 1);
 | 
						|
            retract_route(route);
 | 
						|
            if(oldmetric < INFINITY)
 | 
						|
                route_changed(route, route->src, oldmetric);
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        struct neighbour *neigh = route->neigh;
 | 
						|
        int add_metric = input_filter(route->src->id,
 | 
						|
                                      route->src->prefix, route->src->plen,
 | 
						|
                                      neigh->address,
 | 
						|
                                      neigh->ifp->ifindex);
 | 
						|
        change_route_metric(route, route->refmetric,
 | 
						|
                            neighbour_cost(route->neigh), add_metric);
 | 
						|
        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. */
 | 
						|
void
 | 
						|
update_neighbour_metric(struct neighbour *neigh, int changed)
 | 
						|
{
 | 
						|
 | 
						|
    if(changed) {
 | 
						|
        int i;
 | 
						|
 | 
						|
        for(i = 0; i < route_slots; i++) {
 | 
						|
            struct babel_route *r = routes[i];
 | 
						|
            while(r) {
 | 
						|
                if(r->neigh == neigh)
 | 
						|
                    update_route_metric(r);
 | 
						|
                r = r->next;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
update_interface_metric(struct interface *ifp)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    for(i = 0; i < route_slots; i++) {
 | 
						|
        struct babel_route *r = routes[i];
 | 
						|
        while(r) {
 | 
						|
            if(r->neigh->ifp == ifp)
 | 
						|
                update_route_metric(r);
 | 
						|
            r = r->next;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* This is called whenever we receive an update. */
 | 
						|
struct babel_route *
 | 
						|
update_route(const unsigned char *router_id,
 | 
						|
             const unsigned char *prefix, unsigned char plen,
 | 
						|
             unsigned short seqno, unsigned short refmetric,
 | 
						|
             unsigned short interval,
 | 
						|
             struct neighbour *neigh, const unsigned char *nexthop,
 | 
						|
             const unsigned char *channels, int channels_len)
 | 
						|
{
 | 
						|
    struct babel_route *route;
 | 
						|
    struct source *src;
 | 
						|
    int metric, feasible;
 | 
						|
    int add_metric;
 | 
						|
    int hold_time = MAX((4 * interval) / 100 + interval / 50, 15);
 | 
						|
 | 
						|
    if(memcmp(router_id, myid, 8) == 0)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    if(martian_prefix(prefix, plen)) {
 | 
						|
        flog_err(EC_BABEL_ROUTE, "Rejecting martian route to %s through %s.",
 | 
						|
                 format_prefix(prefix, plen), format_address(nexthop));
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    add_metric = input_filter(router_id, prefix, plen,
 | 
						|
                              neigh->address, neigh->ifp->ifindex);
 | 
						|
    if(add_metric >= INFINITY)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    route = find_route(prefix, plen, neigh, nexthop);
 | 
						|
 | 
						|
    if(route && memcmp(route->src->id, router_id, 8) == 0)
 | 
						|
        /* Avoid scanning the source table. */
 | 
						|
        src = route->src;
 | 
						|
    else
 | 
						|
        src = find_source(router_id, prefix, plen, 1, seqno);
 | 
						|
 | 
						|
    if(src == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    feasible = update_feasible(src, seqno, refmetric);
 | 
						|
    metric = MIN((int)refmetric + neighbour_cost(neigh) + add_metric, INFINITY);
 | 
						|
 | 
						|
    if(route) {
 | 
						|
        struct source *oldsrc;
 | 
						|
        unsigned short oldmetric;
 | 
						|
        int lost = 0;
 | 
						|
 | 
						|
        oldsrc = route->src;
 | 
						|
        oldmetric = route_metric(route);
 | 
						|
 | 
						|
        /* If a successor switches sources, we must accept his update even
 | 
						|
           if it makes a route unfeasible in order to break any routing loops
 | 
						|
           in a timely manner.  If the source remains the same, we ignore
 | 
						|
           the update. */
 | 
						|
        if(!feasible && route->installed) {
 | 
						|
            debugf(BABEL_DEBUG_COMMON,"Unfeasible update for installed route to %s (%s %d %d -> %s %d %d).",
 | 
						|
                   format_prefix(src->prefix, src->plen),
 | 
						|
                   format_eui64(route->src->id),
 | 
						|
                   route->seqno, route->refmetric,
 | 
						|
                   format_eui64(src->id), seqno, refmetric);
 | 
						|
            if(src != route->src) {
 | 
						|
                uninstall_route(route);
 | 
						|
                lost = 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        route->src = retain_source(src);
 | 
						|
        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;
 | 
						|
 | 
						|
        route_changed(route, oldsrc, oldmetric);
 | 
						|
        if(lost)
 | 
						|
            route_lost(oldsrc, oldmetric);
 | 
						|
 | 
						|
        if(!feasible)
 | 
						|
            send_unfeasible_request(neigh, route->installed && route_old(route),
 | 
						|
                                    seqno, metric, src);
 | 
						|
        release_source(oldsrc);
 | 
						|
    } else {
 | 
						|
        struct babel_route *new_route;
 | 
						|
 | 
						|
        if(refmetric >= INFINITY)
 | 
						|
            /* Somebody's retracting a route we never saw. */
 | 
						|
            return NULL;
 | 
						|
        if(!feasible) {
 | 
						|
            send_unfeasible_request(neigh, 0, seqno, metric, src);
 | 
						|
            if(!keep_unfeasible)
 | 
						|
                return NULL;
 | 
						|
        }
 | 
						|
 | 
						|
        route = malloc(sizeof(struct babel_route));
 | 
						|
        if(route == NULL) {
 | 
						|
            perror("malloc(route)");
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
 | 
						|
        route->src = retain_source(src);
 | 
						|
        route->refmetric = refmetric;
 | 
						|
        route->cost = neighbour_cost(neigh);
 | 
						|
        route->add_metric = add_metric;
 | 
						|
        route->seqno = seqno;
 | 
						|
        route->neigh = neigh;
 | 
						|
        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)
 | 
						|
            memcpy(&route->channels, channels,
 | 
						|
                   MIN(channels_len, DIVERSITY_HOPS));
 | 
						|
        route->next = NULL;
 | 
						|
        new_route = insert_route(route);
 | 
						|
        if(new_route == NULL) {
 | 
						|
            flog_err(EC_BABEL_ROUTE, "Couldn't insert route.");
 | 
						|
            free(route);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        consider_route(route);
 | 
						|
    }
 | 
						|
    return route;
 | 
						|
}
 | 
						|
 | 
						|
/* We just received an unfeasible update.  If it's any good, send
 | 
						|
   a request for a new seqno. */
 | 
						|
void
 | 
						|
send_unfeasible_request(struct neighbour *neigh, int force,
 | 
						|
                        unsigned short seqno, unsigned short metric,
 | 
						|
                        struct source *src)
 | 
						|
{
 | 
						|
    struct babel_route *route = find_installed_route(src->prefix, src->plen);
 | 
						|
 | 
						|
    if(seqno_minus(src->seqno, seqno) > 100) {
 | 
						|
        /* Probably a source that lost its seqno.  Let it time-out. */
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if(force || !route || route_metric(route) >= metric + 512) {
 | 
						|
        send_unicast_multihop_request(neigh, src->prefix, src->plen,
 | 
						|
                                      src->metric >= INFINITY ?
 | 
						|
                                      src->seqno :
 | 
						|
                                      seqno_plus(src->seqno, 1),
 | 
						|
                                      src->id, 127);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* 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)
 | 
						|
{
 | 
						|
    struct babel_route *installed;
 | 
						|
    struct xroute *xroute;
 | 
						|
 | 
						|
    if(route->installed)
 | 
						|
        return;
 | 
						|
 | 
						|
    if(!route_feasible(route))
 | 
						|
        return;
 | 
						|
 | 
						|
    xroute = find_xroute(route->src->prefix, route->src->plen);
 | 
						|
    if(xroute)
 | 
						|
        return;
 | 
						|
 | 
						|
    installed = find_installed_route(route->src->prefix, route->src->plen);
 | 
						|
 | 
						|
    if(installed == NULL)
 | 
						|
        goto install;
 | 
						|
 | 
						|
    if(route_metric(route) >= INFINITY)
 | 
						|
        return;
 | 
						|
 | 
						|
    if(route_metric(installed) >= INFINITY)
 | 
						|
        goto install;
 | 
						|
 | 
						|
    if(route_metric(installed) >= route_metric(route) &&
 | 
						|
       route_smoothed_metric(installed) > route_smoothed_metric(route))
 | 
						|
        goto install;
 | 
						|
 | 
						|
    return;
 | 
						|
 | 
						|
 install:
 | 
						|
    switch_routes(installed, route);
 | 
						|
    if(installed && route->installed)
 | 
						|
        send_triggered_update(route, installed->src, route_metric(installed));
 | 
						|
    else
 | 
						|
        send_update(NULL, 1, route->src->prefix, route->src->plen);
 | 
						|
    return;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
retract_neighbour_routes(struct neighbour *neigh)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
 | 
						|
    for(i = 0; i < route_slots; i++) {
 | 
						|
        struct babel_route *r = routes[i];
 | 
						|
        while(r) {
 | 
						|
            if(r->neigh == neigh) {
 | 
						|
                if(r->refmetric != INFINITY) {
 | 
						|
                    unsigned short oldmetric = route_metric(r);
 | 
						|
                    retract_route(r);
 | 
						|
                    if(oldmetric != INFINITY)
 | 
						|
                        route_changed(r, r->src, oldmetric);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            r = r->next;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
send_triggered_update(struct babel_route *route, struct source *oldsrc,
 | 
						|
                      unsigned oldmetric)
 | 
						|
{
 | 
						|
    unsigned newmetric, diff;
 | 
						|
    /* 1 means send speedily, 2 means resend */
 | 
						|
    int urgent;
 | 
						|
 | 
						|
    if(!route->installed)
 | 
						|
        return;
 | 
						|
 | 
						|
    newmetric = route_metric(route);
 | 
						|
    diff =
 | 
						|
        newmetric >= oldmetric ? newmetric - oldmetric : oldmetric - newmetric;
 | 
						|
 | 
						|
    if(route->src != oldsrc || (oldmetric < INFINITY && newmetric >= INFINITY))
 | 
						|
        /* Switching sources can cause transient routing loops.
 | 
						|
           Retractions can cause blackholes. */
 | 
						|
        urgent = 2;
 | 
						|
    else if(newmetric > oldmetric && oldmetric < 6 * 256 && diff >= 512)
 | 
						|
        /* Route getting significantly worse */
 | 
						|
        urgent = 1;
 | 
						|
    else if(unsatisfied_request(route->src->prefix, route->src->plen,
 | 
						|
                                route->seqno, route->src->id))
 | 
						|
        /* Make sure that requests are satisfied speedily */
 | 
						|
        urgent = 1;
 | 
						|
    else if(oldmetric >= INFINITY && newmetric < INFINITY)
 | 
						|
        /* New route */
 | 
						|
        urgent = 0;
 | 
						|
    else if(newmetric < oldmetric && diff < 1024)
 | 
						|
        /* Route getting better.  This may be a transient fluctuation, so
 | 
						|
           don't advertise it to avoid making routes unfeasible later on. */
 | 
						|
        return;
 | 
						|
    else if(diff < 384)
 | 
						|
        /* Don't fret about trivialities */
 | 
						|
        return;
 | 
						|
    else
 | 
						|
        urgent = 0;
 | 
						|
 | 
						|
    if(urgent >= 2)
 | 
						|
        send_update_resend(NULL, route->src->prefix, route->src->plen);
 | 
						|
    else
 | 
						|
        send_update(NULL, urgent, route->src->prefix, route->src->plen);
 | 
						|
 | 
						|
    if(oldmetric < INFINITY) {
 | 
						|
        if(newmetric >= oldmetric + 512) {
 | 
						|
            send_request_resend(NULL, route->src->prefix, route->src->plen,
 | 
						|
                                route->src->metric >= INFINITY ?
 | 
						|
                                route->src->seqno :
 | 
						|
                                seqno_plus(route->src->seqno, 1),
 | 
						|
                                route->src->id);
 | 
						|
        } else if(newmetric >= oldmetric + 288) {
 | 
						|
            send_request(NULL, route->src->prefix, route->src->plen);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* A route has just changed.  Decide whether to switch to a different route or
 | 
						|
   send an update. */
 | 
						|
void
 | 
						|
route_changed(struct babel_route *route,
 | 
						|
              struct source *oldsrc, unsigned short oldmetric)
 | 
						|
{
 | 
						|
    if(route->installed) {
 | 
						|
        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);
 | 
						|
    } else {
 | 
						|
        /* Reconsider routes even when their metric didn't decrease,
 | 
						|
           they may not have been feasible before. */
 | 
						|
        consider_route(route);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* We just lost the installed route to a given destination. */
 | 
						|
void
 | 
						|
route_lost(struct source *src, unsigned oldmetric)
 | 
						|
{
 | 
						|
    struct babel_route *new_route;
 | 
						|
    new_route = find_best_route(src->prefix, src->plen, 1, NULL);
 | 
						|
    if(new_route) {
 | 
						|
        consider_route(new_route);
 | 
						|
    } else if(oldmetric < INFINITY) {
 | 
						|
        /* Avoid creating a blackhole. */
 | 
						|
        send_update_resend(NULL, src->prefix, src->plen);
 | 
						|
        /* 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);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* This is called periodically to flush old routes.  It will also send
 | 
						|
   requests for routes that are about to expire. */
 | 
						|
void
 | 
						|
expire_routes(void)
 | 
						|
{
 | 
						|
    struct babel_route *r;
 | 
						|
    int i;
 | 
						|
 | 
						|
    debugf(BABEL_DEBUG_COMMON,"Expiring old routes.");
 | 
						|
 | 
						|
    i = 0;
 | 
						|
    while(i < route_slots) {
 | 
						|
        r = routes[i];
 | 
						|
        while(r) {
 | 
						|
            /* Protect against clock being stepped. */
 | 
						|
            if(r->time > babel_now.tv_sec || route_old(r)) {
 | 
						|
                flush_route(r);
 | 
						|
                goto again;
 | 
						|
            }
 | 
						|
 | 
						|
            update_route_metric(r);
 | 
						|
 | 
						|
            if(r->installed && r->refmetric < INFINITY) {
 | 
						|
                if(route_old(r))
 | 
						|
                    /* Route about to expire, send a request. */
 | 
						|
                    send_unicast_request(r->neigh,
 | 
						|
                                         r->src->prefix, r->src->plen);
 | 
						|
            }
 | 
						|
            r = r->next;
 | 
						|
        }
 | 
						|
        i++;
 | 
						|
    again:
 | 
						|
        ;
 | 
						|
    }
 | 
						|
}
 |