]> git.proxmox.com Git - mirror_frr.git/commitdiff
fabricd: adjacency formation optimization as per section 2.4
authorChristian Franke <chris@opensourcerouting.org>
Mon, 2 Apr 2018 15:55:26 +0000 (17:55 +0200)
committerChristian Franke <chris@opensourcerouting.org>
Wed, 5 Sep 2018 09:38:13 +0000 (11:38 +0200)
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>
isisd/fabricd.c [new file with mode: 0644]
isisd/fabricd.h [new file with mode: 0644]
isisd/isis_adjacency.c
isisd/isis_lsp.c
isisd/isis_pdu.c
isisd/isisd.c
isisd/isisd.h
isisd/subdir.am

diff --git a/isisd/fabricd.c b/isisd/fabricd.c
new file mode 100644 (file)
index 0000000..d37e6a7
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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;
+}
diff --git a/isisd/fabricd.h b/isisd/fabricd.h
new file mode 100644 (file)
index 0000000..35a5bb6
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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
+ */
+#ifndef FABRICD_H
+#define FABRICD_H
+
+struct fabricd;
+
+struct isis_circuit;
+struct isis_area;
+
+struct fabricd *fabricd_new(void);
+void fabricd_initial_sync_hello(struct isis_circuit *circuit);
+bool fabricd_initial_sync_is_in_progress(struct isis_area *area);
+struct isis_circuit *fabricd_initial_sync_circuit(struct isis_area *area);
+void fabricd_initial_sync_finish(struct isis_area *area);
+
+#endif
index 4b3d78421e8cf3a451b6c22feef551d3bd274376..56dbc25829c850ad19b49105ee1b7ddd237518ad 100644 (file)
@@ -48,6 +48,7 @@
 #include "isisd/isis_events.h"
 #include "isisd/isis_mt.h"
 #include "isisd/isis_tlvs.h"
+#include "isisd/fabricd.h"
 
 extern struct isis *isis;
 
@@ -193,6 +194,9 @@ void isis_adj_process_threeway(struct isis_adjacency *adj,
                }
        }
 
+       if (next_tw_state != ISIS_THREEWAY_DOWN)
+               fabricd_initial_sync_hello(adj->circuit);
+
        if (next_tw_state == ISIS_THREEWAY_DOWN) {
                isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Neighbor restarted");
                return;
@@ -306,10 +310,15 @@ void isis_adj_state_change(struct isis_adjacency *adj,
                                adj->last_flap = time(NULL);
                                adj->flaps++;
 
-                               /* 7.3.17 - going up on P2P -> send CSNP */
-                               /* FIXME: yup, I know its wrong... but i will do
-                                * it! (for now) */
-                               send_csnp(circuit, level);
+                               if (level == IS_LEVEL_1) {
+                                       thread_add_timer(master, send_l1_csnp,
+                                                        circuit, 0,
+                                                        &circuit->t_send_csnp[0]);
+                               } else {
+                                       thread_add_timer(master, send_l2_csnp,
+                                                        circuit, 0,
+                                                        &circuit->t_send_csnp[1]);
+                               }
                        } else if (new_state == ISIS_ADJ_DOWN) {
                                if (adj->circuit->u.p2p.neighbor == adj)
                                        adj->circuit->u.p2p.neighbor = NULL;
index 66c97ae8974413610db366e68b64c5abdc2cbd45..90bcece15dfd87e0a2c7755962189e905cb77050 100644 (file)
@@ -56,6 +56,7 @@
 #include "isisd/isis_te.h"
 #include "isisd/isis_mt.h"
 #include "isisd/isis_tlvs.h"
+#include "isisd/fabricd.h"
 
 static int lsp_l1_refresh(struct thread *thread);
 static int lsp_l2_refresh(struct thread *thread);
@@ -1813,6 +1814,7 @@ int lsp_tick(struct thread *thread)
        int level;
        uint16_t rem_lifetime;
        time_t now = monotime(NULL);
+       bool fabricd_sync_incomplete = false;
 
        lsp_list = list_new();
 
@@ -1821,6 +1823,8 @@ int lsp_tick(struct thread *thread)
        area->t_tick = NULL;
        thread_add_timer(master, lsp_tick, area, 1, &area->t_tick);
 
+       struct isis_circuit *fabricd_init_c = fabricd_initial_sync_circuit(area);
+
        /*
         * Build a list of LSPs with (any) SRMflag set
         * and removed the ones that have aged out
@@ -1880,6 +1884,15 @@ int lsp_tick(struct thread *thread)
                                                         dnode);
                                } else if (flags_any_set(lsp->SRMflags))
                                        listnode_add(lsp_list, lsp);
+
+                               if (fabricd_init_c) {
+                                       fabricd_sync_incomplete |=
+                                               ISIS_CHECK_FLAG(lsp->SSNflags,
+                                                               fabricd_init_c);
+                                       fabricd_sync_incomplete |=
+                                               ISIS_CHECK_FLAG(lsp->SRMflags,
+                                                               fabricd_init_c);
+                               }
                        }
 
                        /*
@@ -1915,6 +1928,9 @@ int lsp_tick(struct thread *thread)
                }
        }
 
+       if (fabricd_init_c && !fabricd_sync_incomplete)
+               fabricd_initial_sync_finish(area);
+
        list_delete_and_null(&lsp_list);
 
        return ISIS_OK;
index 5c4e3a35bcb44baf60bd4374df7ff909f4ea97af..8a5db3c6f953aa54960994a6724542f1d2d7577c 100644 (file)
@@ -56,6 +56,7 @@
 #include "isisd/isis_mt.h"
 #include "isisd/isis_tlvs.h"
 #include "isisd/isis_errors.h"
+#include "isisd/fabricd.h"
 
 static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit,
                   int level)
@@ -207,6 +208,12 @@ static int process_p2p_hello(struct iih_info *iih)
        thread_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time,
                         &adj->t_expire);
 
+       /* While fabricds initial sync is in progress, ignore hellos from other
+        * interfaces than the one we are performing the initial sync on. */
+       if (fabricd_initial_sync_is_in_progress(iih->circuit->area)
+           && fabricd_initial_sync_circuit(iih->circuit->area) != iih->circuit)
+               return ISIS_OK;
+
        /* 8.2.5.2 a) a match was detected */
        if (isis_tlvs_area_addresses_match(iih->tlvs,
                                           iih->circuit->area->area_addrs)) {
@@ -1157,7 +1164,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
                                     circuit->u.bc.adjdb[level - 1]))
                        return ISIS_OK; /* Silently discard */
        } else {
-               if (!circuit->u.p2p.neighbor) {
+               if (!fabricd && !circuit->u.p2p.neighbor) {
                        zlog_warn("no p2p neighbor on circuit %s",
                                  circuit->interface->name);
                        return ISIS_OK; /* Silently discard */
@@ -1582,8 +1589,15 @@ int send_hello(struct isis_circuit *circuit, int level)
                   && !circuit->disable_threeway_adj) {
                uint32_t ext_circuit_id = circuit->idx;
                if (circuit->u.p2p.neighbor) {
+                       uint8_t threeway_state;
+
+                       if (fabricd_initial_sync_is_in_progress(circuit->area)
+                           && fabricd_initial_sync_circuit(circuit->area) != circuit)
+                               threeway_state = ISIS_THREEWAY_DOWN;
+                       else
+                               threeway_state = circuit->u.p2p.neighbor->threeway_state;
                        isis_tlvs_add_threeway_adj(tlvs,
-                                       circuit->u.p2p.neighbor->threeway_state,
+                                       threeway_state,
                                        ext_circuit_id,
                                        circuit->u.p2p.neighbor->sysid,
                                        circuit->u.p2p.neighbor->ext_circuit_id);
@@ -1889,8 +1903,9 @@ int send_l1_csnp(struct thread *thread)
 
        circuit->t_send_csnp[0] = NULL;
 
-       if (circuit->circ_type == CIRCUIT_T_BROADCAST
-           && circuit->u.bc.is_dr[0]) {
+       if ((circuit->circ_type == CIRCUIT_T_BROADCAST
+            && circuit->u.bc.is_dr[0])
+            || circuit->circ_type == CIRCUIT_T_P2P) {
                send_csnp(circuit, 1);
        }
        /* set next timer thread */
@@ -1911,8 +1926,9 @@ int send_l2_csnp(struct thread *thread)
 
        circuit->t_send_csnp[1] = NULL;
 
-       if (circuit->circ_type == CIRCUIT_T_BROADCAST
-           && circuit->u.bc.is_dr[1]) {
+       if ((circuit->circ_type == CIRCUIT_T_BROADCAST
+            && circuit->u.bc.is_dr[1])
+             || circuit->circ_type == CIRCUIT_T_P2P) {
                send_csnp(circuit, 2);
        }
        /* set next timer thread */
index 609ca450df29ff9f54d916ae56896d0c858714b4..e8efa391e0dee3dd2ef20035c37916a65896ab3e 100644 (file)
@@ -56,6 +56,7 @@
 #include "isisd/isis_events.h"
 #include "isisd/isis_te.h"
 #include "isisd/isis_mt.h"
+#include "isisd/fabricd.h"
 
 struct isis *isis = NULL;
 
@@ -95,6 +96,7 @@ void isis_new(unsigned long process_id)
         */
        /* isis->debugs = 0xFFFF; */
        isisMplsTE.status = disable; /* Only support TE metric */
+
        QOBJ_REG(isis, isis);
 }
 
@@ -156,6 +158,8 @@ struct isis_area *isis_area_create(const char *area_tag)
        listnode_add(isis->area_list, area);
        area->isis = isis;
 
+       if (fabricd)
+               area->fabricd = fabricd_new();
        QOBJ_REG(area, isis_area);
 
        return area;
index b76b0b7847b3cabcfb9e2440a1b1f82b0ddbb5fe..0e75b870a5e03d2f6a4213895f4de3aeda16a1ee 100644 (file)
@@ -57,6 +57,8 @@ extern struct zebra_privs_t isisd_privs;
 /* #define EXTREME_DEBUG  */
 /* #define EXTREME_DICT_DEBUG */
 
+struct fabricd;
+
 struct isis {
        unsigned long process_id;
        int sysid_set;
@@ -111,6 +113,8 @@ struct isis_area {
         */
        int lsp_regenerate_pending[ISIS_LEVELS];
 
+       struct fabricd *fabricd;
+
        /*
         * Configurables
         */
index 5593f2a4ed9e41c1b679c273d1af440b5b2aac02..cd8bfdbddc2e484187a377aa9a8e596bbd9e71bc 100644 (file)
@@ -43,6 +43,7 @@ noinst_HEADERS += \
        isisd/isis_zebra.h \
        isisd/isisd.h \
        isisd/iso_checksum.h \
+       isisd/fabricd.h \
        # end
 
 LIBISIS_SOURCES = \
@@ -71,6 +72,7 @@ LIBISIS_SOURCES = \
        isisd/isis_zebra.c \
        isisd/isisd.c \
        isisd/iso_checksum.c \
+       isisd/fabricd.c \
        # end
 
 ISIS_SOURCES = \