frr/isisd/fabricd.c

137 lines
3.6 KiB
C
Raw Normal View History

fabricd: adjacency formation optimization as per section 2.4 OpenFabric changes IS-IS's initial database synchronization. While regular IS-IS will simultaneuously exchange LSPs with all neighboring routers during startup, this is considered too much churn for a densely connected fabric. To mitigate this, OpenFabric prescribes that a router should only bring up an adjacency with a single neighbor and perform a full synchronization with that neighbor, before bringing up further adjacencies. This is implemented by having a field `initial_sync_state` in the fabricd datastructure which tracks whether an initial sync is still pending, currently in progress, or complete. When an initial sync is pending, the state will transition to the in-progress state when the first IIH is received. During this state, all IIHs from other routers are ignored. Any IIHs transmitted on any link other than the one to the router with which we are performing the initial sync will always report the far end as DOWN in their threeway handshake state, avoiding the formation of additional adjacencies. The state will be left if all the SRM and SSN flags on the initial-sync circuit are cleared (meaning that initial sync has completed). This is checked in `lsp_tick`. When this condition occurrs, we progress to the initial-sync-complete state, allowing other adjacencies to form. The state can also be left if the initial synchronization is taking too long to succeed, for whatever reason. In that case, we fall back to the initial-sync-pending state and will reattempt initial synchronization with a different neighbor. Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-04-02 17:55:26 +02:00
/*
* IS-IS Rout(e)ing protocol - OpenFabric extensions
*
* Copyright (C) 2018 Christian Franke
*
* This file is part of FreeRangeRouting (FRR)
*
* FRR is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* FRR 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; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
#include "isisd/fabricd.h"
#include "isisd/isisd.h"
#include "isisd/isis_memory.h"
#include "isisd/isis_circuit.h"
#include "isisd/isis_misc.h"
#include "isisd/isis_adjacency.h"
DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric")
/* Tracks initial synchronization as per section 2.4
*
* We declare the sync complete once we have seen at least one
* CSNP and there are no more LSPs with SSN or SRM set.
*/
enum fabricd_sync_state {
FABRICD_SYNC_PENDING,
FABRICD_SYNC_STARTED,
FABRICD_SYNC_COMPLETE
};
struct fabricd {
enum fabricd_sync_state initial_sync_state;
time_t initial_sync_start;
struct isis_circuit *initial_sync_circuit;
struct thread *initial_sync_timeout;
};
struct fabricd *fabricd_new(void)
{
struct fabricd *rv = XCALLOC(MTYPE_FABRICD_STATE, sizeof(*rv));
rv->initial_sync_state = FABRICD_SYNC_PENDING;
return rv;
};
static int fabricd_initial_sync_timeout(struct thread *thread)
{
struct fabricd *f = THREAD_ARG(thread);
zlog_info("OpenFabric: Initial synchronization on %s timed out!",
f->initial_sync_circuit->interface->name);
f->initial_sync_state = FABRICD_SYNC_PENDING;
f->initial_sync_circuit = NULL;
f->initial_sync_timeout = NULL;
return 0;
}
void fabricd_initial_sync_hello(struct isis_circuit *circuit)
{
struct fabricd *f = circuit->area->fabricd;
if (!f)
return;
if (f->initial_sync_state > FABRICD_SYNC_PENDING)
return;
f->initial_sync_state = FABRICD_SYNC_STARTED;
long timeout = 2 * circuit->hello_interval[1] * circuit->hello_multiplier[1];
f->initial_sync_circuit = circuit;
if (f->initial_sync_timeout)
return;
thread_add_timer(master, fabricd_initial_sync_timeout, f,
timeout, &f->initial_sync_timeout);
f->initial_sync_start = monotime(NULL);
zlog_info("OpenFabric: Started initial synchronization with %s on %s",
sysid_print(circuit->u.p2p.neighbor->sysid),
circuit->interface->name);
}
bool fabricd_initial_sync_is_in_progress(struct isis_area *area)
{
struct fabricd *f = area->fabricd;
if (!f)
return false;
if (f->initial_sync_state > FABRICD_SYNC_PENDING
&& f->initial_sync_state < FABRICD_SYNC_COMPLETE)
return true;
return false;
}
struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area)
{
struct fabricd *f = area->fabricd;
if (!f)
return NULL;
return f->initial_sync_circuit;
}
void fabricd_initial_sync_finish(struct isis_area *area)
{
struct fabricd *f = area->fabricd;
if (!f)
return;
if (monotime(NULL) - f->initial_sync_start < 5)
return;
zlog_info("OpenFabric: Initial synchronization on %s complete.",
f->initial_sync_circuit->interface->name);
f->initial_sync_state = FABRICD_SYNC_COMPLETE;
f->initial_sync_circuit = NULL;
thread_cancel(f->initial_sync_timeout);
f->initial_sync_timeout = NULL;
}