2003-12-23 09:09:43 +01:00
|
|
|
/*
|
|
|
|
* IS-IS Rout(e)ing protocol - isis_lsp.c
|
|
|
|
* LSP processing
|
|
|
|
*
|
|
|
|
* Copyright (C) 2001,2002 Sampo Saaristo
|
2017-07-17 14:03:14 +02:00
|
|
|
* Tampere University of Technology
|
2003-12-23 09:09:43 +01:00
|
|
|
* Institute of Communications Engineering
|
2015-11-12 14:24:22 +01:00
|
|
|
* Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org>
|
2003-12-23 09:09:43 +01:00
|
|
|
*
|
2017-07-17 14:03:14 +02:00
|
|
|
* This program 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 of the License, or (at your option)
|
2003-12-23 09:09:43 +01:00
|
|
|
* any later version.
|
|
|
|
*
|
2017-07-17 14:03:14 +02:00
|
|
|
* This program 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
|
2003-12-23 09:09:43 +01:00
|
|
|
* more details.
|
2017-05-13 10:25:29 +02:00
|
|
|
*
|
|
|
|
* 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
|
2003-12-23 09:09:43 +01:00
|
|
|
*/
|
2005-05-03 11:27:23 +02:00
|
|
|
|
2003-12-23 09:09:43 +01:00
|
|
|
#include <zebra.h>
|
|
|
|
|
|
|
|
#include "linklist.h"
|
|
|
|
#include "thread.h"
|
|
|
|
#include "vty.h"
|
|
|
|
#include "stream.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "prefix.h"
|
|
|
|
#include "command.h"
|
|
|
|
#include "hash.h"
|
|
|
|
#include "if.h"
|
2008-08-13 20:09:10 +02:00
|
|
|
#include "checksum.h"
|
2012-03-24 16:35:20 +01:00
|
|
|
#include "md5.h"
|
2015-11-12 14:24:22 +01:00
|
|
|
#include "table.h"
|
2018-07-22 21:49:02 +02:00
|
|
|
#include "srcdest_table.h"
|
2018-06-18 20:27:21 +02:00
|
|
|
#include "lib_errors.h"
|
2003-12-23 09:09:43 +01:00
|
|
|
|
|
|
|
#include "isisd/dict.h"
|
|
|
|
#include "isisd/isis_constants.h"
|
|
|
|
#include "isisd/isis_common.h"
|
2012-03-24 16:35:20 +01:00
|
|
|
#include "isisd/isis_flags.h"
|
2003-12-23 09:09:43 +01:00
|
|
|
#include "isisd/isis_circuit.h"
|
|
|
|
#include "isisd/isisd.h"
|
|
|
|
#include "isisd/isis_lsp.h"
|
|
|
|
#include "isisd/isis_pdu.h"
|
|
|
|
#include "isisd/isis_dynhn.h"
|
|
|
|
#include "isisd/isis_misc.h"
|
|
|
|
#include "isisd/isis_csm.h"
|
|
|
|
#include "isisd/isis_adjacency.h"
|
|
|
|
#include "isisd/isis_spf.h"
|
2016-04-19 19:03:05 +02:00
|
|
|
#include "isisd/isis_te.h"
|
2017-04-27 13:56:38 +02:00
|
|
|
#include "isisd/isis_mt.h"
|
2017-07-23 15:06:09 +02:00
|
|
|
#include "isisd/isis_tlvs.h"
|
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
|
|
|
#include "isisd/fabricd.h"
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
#include "isisd/isis_tx_queue.h"
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2018-10-24 06:27:17 +02:00
|
|
|
static int lsp_refresh(struct thread *thread);
|
2017-07-17 14:03:14 +02:00
|
|
|
static int lsp_l1_refresh_pseudo(struct thread *thread);
|
|
|
|
static int lsp_l2_refresh_pseudo(struct thread *thread);
|
2012-03-24 16:35:20 +01:00
|
|
|
|
2018-03-27 21:13:34 +02:00
|
|
|
int lsp_id_cmp(uint8_t *id1, uint8_t *id2)
|
2004-09-10 22:48:21 +02:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
return memcmp(id1, id2, ISIS_SYS_ID_LEN + 2);
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
dict_t *lsp_db_init(void)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
dict_t *dict;
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
dict = dict_create(DICTCOUNT_T_MAX, (dict_comp_t)lsp_id_cmp);
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return dict;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2018-03-27 21:13:34 +02:00
|
|
|
struct isis_lsp *lsp_search(uint8_t *id, dict_t *lspdb)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
dnode_t *node;
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2004-09-10 22:48:21 +02:00
|
|
|
#ifdef EXTREME_DEBUG
|
2017-07-17 14:03:14 +02:00
|
|
|
dnode_t *dn;
|
|
|
|
|
|
|
|
zlog_debug("searching db");
|
|
|
|
for (dn = dict_first(lspdb); dn; dn = dict_next(lspdb, dn)) {
|
|
|
|
zlog_debug("%s\t%pX",
|
2018-03-27 21:13:34 +02:00
|
|
|
rawlspid_print((uint8_t *)dnode_getkey(dn)),
|
2017-07-17 14:03:14 +02:00
|
|
|
dnode_get(dn));
|
|
|
|
}
|
2003-12-23 09:09:43 +01:00
|
|
|
#endif /* EXTREME DEBUG */
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
node = dict_lookup(lspdb, id);
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (node)
|
|
|
|
return (struct isis_lsp *)dnode_get(node);
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return NULL;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static void lsp_clear_data(struct isis_lsp *lsp)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!lsp)
|
|
|
|
return;
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_free_tlvs(lsp->tlvs);
|
|
|
|
lsp->tlvs = NULL;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2018-10-16 17:36:45 +02:00
|
|
|
static void lsp_remove_frags(struct list *frags, dict_t *lspdb);
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static void lsp_destroy(struct isis_lsp *lsp)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-10-03 01:42:22 +02:00
|
|
|
struct listnode *cnode;
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_circuit *circuit;
|
|
|
|
|
|
|
|
if (!lsp)
|
|
|
|
return;
|
|
|
|
|
2017-10-03 01:42:22 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(lsp->area->circuit_list, cnode, circuit))
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
isis_tx_queue_del(circuit->tx_queue, lsp);
|
2017-10-03 01:42:22 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
ISIS_FLAGS_CLEAR_ALL(lsp->SSNflags);
|
|
|
|
|
|
|
|
lsp_clear_data(lsp);
|
|
|
|
|
2018-10-16 17:36:45 +02:00
|
|
|
if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) {
|
|
|
|
if (lsp->lspu.frags) {
|
|
|
|
lsp_remove_frags(lsp->lspu.frags,
|
|
|
|
lsp->area->lspdb[lsp->level - 1]);
|
|
|
|
list_delete(&lsp->lspu.frags);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (lsp->lspu.zero_lsp
|
|
|
|
&& lsp->lspu.zero_lsp->lspu.frags) {
|
|
|
|
listnode_delete(lsp->lspu.zero_lsp->lspu.frags, lsp);
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
isis_spf_schedule(lsp->area, lsp->level);
|
|
|
|
|
|
|
|
if (lsp->pdu)
|
|
|
|
stream_free(lsp->pdu);
|
2018-11-23 03:13:56 +01:00
|
|
|
|
|
|
|
fabricd_lsp_free(lsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
XFREE(MTYPE_ISIS_LSP, lsp);
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
void lsp_db_destroy(dict_t *lspdb)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
dnode_t *dnode, *next;
|
|
|
|
struct isis_lsp *lsp;
|
|
|
|
|
|
|
|
dnode = dict_first(lspdb);
|
|
|
|
while (dnode) {
|
|
|
|
next = dict_next(lspdb, dnode);
|
|
|
|
lsp = dnode_get(dnode);
|
|
|
|
lsp_destroy(lsp);
|
|
|
|
dict_delete_free(lspdb, dnode);
|
|
|
|
dnode = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
dict_free(lspdb);
|
|
|
|
|
|
|
|
return;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove all the frags belonging to the given lsp
|
|
|
|
*/
|
2017-07-17 14:03:14 +02:00
|
|
|
static void lsp_remove_frags(struct list *frags, dict_t *lspdb)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
dnode_t *dnode;
|
|
|
|
struct listnode *lnode, *lnnode;
|
|
|
|
struct isis_lsp *lsp;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS(frags, lnode, lnnode, lsp)) {
|
2017-07-05 18:37:36 +02:00
|
|
|
dnode = dict_lookup(lspdb, lsp->hdr.lsp_id);
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_destroy(lsp);
|
|
|
|
dnode_destroy(dict_delete(lspdb, dnode));
|
|
|
|
}
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2018-03-27 21:13:34 +02:00
|
|
|
void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
dnode_t *node;
|
|
|
|
struct isis_lsp *lsp;
|
|
|
|
|
|
|
|
node = dict_lookup(lspdb, id);
|
|
|
|
if (node) {
|
|
|
|
node = dict_delete(lspdb, node);
|
|
|
|
lsp = dnode_get(node);
|
|
|
|
/*
|
|
|
|
* If this is a zero lsp, remove all the frags now
|
|
|
|
*/
|
2017-07-05 18:37:36 +02:00
|
|
|
if (LSP_FRAGMENT(lsp->hdr.lsp_id) == 0) {
|
2017-07-17 14:03:14 +02:00
|
|
|
if (lsp->lspu.frags)
|
|
|
|
lsp_remove_frags(lsp->lspu.frags, lspdb);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* else just remove this frag, from the zero lsps' frag
|
|
|
|
* list
|
|
|
|
*/
|
|
|
|
if (lsp->lspu.zero_lsp
|
|
|
|
&& lsp->lspu.zero_lsp->lspu.frags)
|
|
|
|
listnode_delete(lsp->lspu.zero_lsp->lspu.frags,
|
|
|
|
lsp);
|
|
|
|
}
|
|
|
|
lsp_destroy(lsp);
|
|
|
|
dnode_destroy(node);
|
|
|
|
}
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compares a LSP to given values
|
|
|
|
* Params are given in net order
|
|
|
|
*/
|
2017-07-05 18:37:36 +02:00
|
|
|
int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno,
|
|
|
|
uint16_t checksum, uint16_t rem_lifetime)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-05 18:37:36 +02:00
|
|
|
if (lsp->hdr.seqno == seqno && lsp->hdr.checksum == checksum
|
|
|
|
&& ((lsp->hdr.rem_lifetime == 0 && rem_lifetime == 0)
|
|
|
|
|| (lsp->hdr.rem_lifetime != 0 && rem_lifetime != 0))) {
|
2017-07-17 14:03:14 +02:00
|
|
|
if (isis->debugs & DEBUG_SNP_PACKETS) {
|
|
|
|
zlog_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32
|
|
|
|
", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
|
|
|
|
"s",
|
|
|
|
areatag, rawlspid_print(lsp->hdr.lsp_id),
|
|
|
|
lsp->hdr.seqno, lsp->hdr.checksum,
|
|
|
|
lsp->hdr.rem_lifetime);
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS-Snp (%s): is equal to ours seq 0x%08" PRIx32
|
|
|
|
", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
|
|
|
|
"s",
|
|
|
|
areatag, seqno, checksum, rem_lifetime);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
return LSP_EQUAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LSPs with identical checksums should only be treated as newer if:
|
|
|
|
* a) The current LSP has a remaining lifetime != 0 and the other LSP
|
|
|
|
* has a
|
|
|
|
* remaining lifetime == 0. In this case, we should participate in
|
|
|
|
* the purge
|
|
|
|
* and should not treat the current LSP with remaining lifetime == 0
|
|
|
|
* as older.
|
|
|
|
* b) The LSP has an incorrect checksum. In this case, we need to react
|
|
|
|
* as given
|
|
|
|
* in 7.3.16.2.
|
|
|
|
*/
|
2017-07-05 18:37:36 +02:00
|
|
|
if (seqno > lsp->hdr.seqno
|
|
|
|
|| (seqno == lsp->hdr.seqno
|
|
|
|
&& ((lsp->hdr.rem_lifetime != 0 && rem_lifetime == 0)
|
2018-11-14 18:54:59 +01:00
|
|
|
|| (lsp->hdr.checksum != checksum
|
|
|
|
&& lsp->hdr.rem_lifetime)))) {
|
2017-07-17 14:03:14 +02:00
|
|
|
if (isis->debugs & DEBUG_SNP_PACKETS) {
|
|
|
|
zlog_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32
|
|
|
|
", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
|
|
|
|
"s",
|
|
|
|
areatag, rawlspid_print(lsp->hdr.lsp_id), seqno,
|
|
|
|
checksum, rem_lifetime);
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS-Snp (%s): is newer than ours seq 0x%08" PRIx32
|
|
|
|
", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
|
|
|
|
"s",
|
|
|
|
areatag, lsp->hdr.seqno, lsp->hdr.checksum,
|
|
|
|
lsp->hdr.rem_lifetime);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
return LSP_NEWER;
|
|
|
|
}
|
|
|
|
if (isis->debugs & DEBUG_SNP_PACKETS) {
|
2017-07-05 18:37:36 +02:00
|
|
|
zlog_debug("ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32
|
|
|
|
", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s",
|
|
|
|
areatag, rawlspid_print(lsp->hdr.lsp_id), seqno,
|
|
|
|
checksum, rem_lifetime);
|
2017-07-17 14:03:14 +02:00
|
|
|
zlog_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS-Snp (%s): is older than ours seq 0x%08" PRIx32
|
|
|
|
", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s",
|
|
|
|
areatag, lsp->hdr.seqno, lsp->hdr.checksum,
|
|
|
|
lsp->hdr.rem_lifetime);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return LSP_OLDER;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2017-08-29 00:14:43 +02:00
|
|
|
static void put_lsp_hdr(struct isis_lsp *lsp, size_t *len_pointer, bool keep)
|
2012-03-24 16:35:20 +01:00
|
|
|
{
|
2017-07-05 18:37:36 +02:00
|
|
|
uint8_t pdu_type =
|
|
|
|
(lsp->level == IS_LEVEL_1) ? L1_LINK_STATE : L2_LINK_STATE;
|
|
|
|
struct isis_lsp_hdr *hdr = &lsp->hdr;
|
|
|
|
struct stream *stream = lsp->pdu;
|
2018-02-02 18:10:09 +01:00
|
|
|
size_t orig_getp = 0, orig_endp = 0;
|
2017-08-29 00:14:43 +02:00
|
|
|
|
|
|
|
if (keep) {
|
|
|
|
orig_getp = stream_get_getp(lsp->pdu);
|
|
|
|
orig_endp = stream_get_endp(lsp->pdu);
|
|
|
|
}
|
|
|
|
|
|
|
|
stream_set_getp(lsp->pdu, 0);
|
|
|
|
stream_set_endp(lsp->pdu, 0);
|
2017-07-05 18:37:36 +02:00
|
|
|
|
|
|
|
fill_fixed_hdr(pdu_type, stream);
|
|
|
|
|
|
|
|
if (len_pointer)
|
|
|
|
*len_pointer = stream_get_endp(stream);
|
|
|
|
stream_putw(stream, hdr->pdu_len);
|
|
|
|
stream_putw(stream, hdr->rem_lifetime);
|
|
|
|
stream_put(stream, hdr->lsp_id, sizeof(hdr->lsp_id));
|
|
|
|
stream_putl(stream, hdr->seqno);
|
|
|
|
stream_putw(stream, hdr->checksum);
|
|
|
|
stream_putc(stream, hdr->lsp_bits);
|
2017-08-29 00:14:43 +02:00
|
|
|
|
|
|
|
if (keep) {
|
|
|
|
stream_set_endp(lsp->pdu, orig_endp);
|
|
|
|
stream_set_getp(lsp->pdu, orig_getp);
|
|
|
|
}
|
2012-03-24 16:35:20 +01:00
|
|
|
}
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
static void lsp_add_auth(struct isis_lsp *lsp)
|
2012-03-24 16:35:20 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_passwd *passwd;
|
2017-07-05 18:37:36 +02:00
|
|
|
passwd = (lsp->level == IS_LEVEL_1) ? &lsp->area->area_passwd
|
|
|
|
: &lsp->area->domain_passwd;
|
|
|
|
isis_tlvs_add_auth(lsp->tlvs, passwd);
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
static void lsp_pack_pdu(struct isis_lsp *lsp)
|
|
|
|
{
|
|
|
|
if (!lsp->tlvs)
|
|
|
|
lsp->tlvs = isis_alloc_tlvs();
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_add_auth(lsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
size_t len_pointer;
|
2017-08-29 00:14:43 +02:00
|
|
|
put_lsp_hdr(lsp, &len_pointer, false);
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_pack_tlvs(lsp->tlvs, lsp->pdu, len_pointer, false, true);
|
|
|
|
|
|
|
|
lsp->hdr.pdu_len = stream_get_endp(lsp->pdu);
|
|
|
|
lsp->hdr.checksum =
|
|
|
|
ntohs(fletcher_checksum(STREAM_DATA(lsp->pdu) + 12,
|
|
|
|
stream_get_endp(lsp->pdu) - 12, 12));
|
2012-03-24 16:35:20 +01:00
|
|
|
}
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-05 18:37:36 +02:00
|
|
|
uint32_t newseq;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
if (seqno == 0 || lsp->hdr.seqno > seqno)
|
|
|
|
newseq = lsp->hdr.seqno + 1;
|
2017-07-17 14:03:14 +02:00
|
|
|
else
|
2017-07-05 18:37:36 +02:00
|
|
|
newseq = seqno + 1;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-11-14 15:08:26 +01:00
|
|
|
#ifndef FABRICD
|
|
|
|
/* check for overflow */
|
|
|
|
if (newseq < lsp->hdr.seqno) {
|
|
|
|
/* send northbound notification */
|
|
|
|
isis_notif_lsp_exceed_max(lsp->area,
|
|
|
|
rawlspid_print(lsp->hdr.lsp_id));
|
|
|
|
}
|
|
|
|
#endif /* ifndef FABRICD */
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->hdr.seqno = newseq;
|
|
|
|
|
|
|
|
lsp_pack_pdu(lsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
isis_spf_schedule(lsp->area, lsp->level);
|
2017-07-05 18:37:36 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-05-31 15:14:26 +02:00
|
|
|
static void lsp_purge_add_poi(struct isis_lsp *lsp,
|
|
|
|
const uint8_t *sender)
|
|
|
|
{
|
|
|
|
if (!lsp->area->purge_originator)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* add purge originator identification */
|
|
|
|
if (!lsp->tlvs)
|
|
|
|
lsp->tlvs = isis_alloc_tlvs();
|
|
|
|
isis_tlvs_set_purge_originator(lsp->tlvs, isis->sysid, sender);
|
|
|
|
isis_tlvs_set_dynamic_hostname(lsp->tlvs, cmd_hostname_get());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void lsp_purge(struct isis_lsp *lsp, int level,
|
|
|
|
const uint8_t *sender)
|
2017-07-05 18:37:36 +02:00
|
|
|
{
|
|
|
|
/* reset stream */
|
|
|
|
lsp_clear_data(lsp);
|
|
|
|
stream_reset(lsp->pdu);
|
|
|
|
|
|
|
|
/* update header */
|
|
|
|
lsp->hdr.checksum = 0;
|
|
|
|
lsp->hdr.rem_lifetime = 0;
|
|
|
|
lsp->level = level;
|
|
|
|
lsp->age_out = lsp->area->max_lsp_lifetime[level - 1];
|
2018-11-24 00:36:37 +01:00
|
|
|
lsp->area->lsp_purge_count[level - 1]++;
|
2017-07-05 18:37:36 +02:00
|
|
|
|
2018-05-31 15:14:26 +02:00
|
|
|
lsp_purge_add_poi(lsp, sender);
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_pack_pdu(lsp);
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
lsp_flood(lsp, NULL);
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-07-05 18:37:36 +02:00
|
|
|
* Generates checksum for LSP and its frags
|
2003-12-23 09:09:43 +01:00
|
|
|
*/
|
2017-07-05 18:37:36 +02:00
|
|
|
static void lsp_seqno_update(struct isis_lsp *lsp0)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_lsp *lsp;
|
|
|
|
struct listnode *node;
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_inc_seqno(lsp0, 0);
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!lsp0->lspu.frags)
|
|
|
|
return;
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(lsp0->lspu.frags, node, lsp)) {
|
|
|
|
if (lsp->tlvs)
|
|
|
|
lsp_inc_seqno(lsp, 0);
|
2018-11-09 16:38:38 +01:00
|
|
|
else if (lsp->hdr.rem_lifetime) {
|
|
|
|
/* Purge should only be applied when the fragment has
|
|
|
|
* non-zero remaining lifetime.
|
|
|
|
*/
|
2018-05-31 15:14:26 +02:00
|
|
|
lsp_purge(lsp, lsp0->level, NULL);
|
2018-11-09 16:38:38 +01:00
|
|
|
}
|
2017-07-05 18:37:36 +02:00
|
|
|
}
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2018-03-27 21:13:34 +02:00
|
|
|
static uint8_t lsp_bits_generate(int level, int overload_bit, int attached_bit)
|
2012-03-28 08:48:05 +02:00
|
|
|
{
|
2018-03-27 21:13:34 +02:00
|
|
|
uint8_t lsp_bits = 0;
|
2017-07-17 14:03:14 +02:00
|
|
|
if (level == IS_LEVEL_1)
|
|
|
|
lsp_bits = IS_LEVEL_1;
|
|
|
|
else
|
|
|
|
lsp_bits = IS_LEVEL_1_AND_2;
|
|
|
|
if (overload_bit)
|
|
|
|
lsp_bits |= overload_bit;
|
|
|
|
if (attached_bit)
|
|
|
|
lsp_bits |= attached_bit;
|
|
|
|
return lsp_bits;
|
2012-03-28 08:48:05 +02:00
|
|
|
}
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
static void lsp_update_data(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr,
|
|
|
|
struct isis_tlvs *tlvs, struct stream *stream,
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_area *area, int level)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
/* free the old lsp data */
|
|
|
|
lsp_clear_data(lsp);
|
|
|
|
|
|
|
|
/* copying only the relevant part of our stream */
|
|
|
|
if (lsp->pdu != NULL)
|
|
|
|
stream_free(lsp->pdu);
|
|
|
|
lsp->pdu = stream_dup(stream);
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
memcpy(&lsp->hdr, hdr, sizeof(lsp->hdr));
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp->area = area;
|
|
|
|
lsp->level = level;
|
|
|
|
lsp->age_out = ZERO_AGE_LIFETIME;
|
|
|
|
lsp->installed = time(NULL);
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->tlvs = tlvs;
|
|
|
|
|
2018-05-31 15:14:26 +02:00
|
|
|
if (area->dynhostname && lsp->tlvs->hostname
|
|
|
|
&& lsp->hdr.rem_lifetime) {
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_dynhn_insert(lsp->hdr.lsp_id, lsp->tlvs->hostname,
|
|
|
|
(lsp->hdr.lsp_bits & LSPBIT_IST)
|
2017-07-17 14:03:14 +02:00
|
|
|
== IS_LEVEL_1_AND_2
|
|
|
|
? IS_LEVEL_2
|
|
|
|
: IS_LEVEL_1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2017-09-23 20:01:43 +02:00
|
|
|
static void lsp_link_fragment(struct isis_lsp *lsp, struct isis_lsp *lsp0)
|
|
|
|
{
|
|
|
|
if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) {
|
|
|
|
/* zero lsp -> create list to store fragments */
|
|
|
|
lsp->lspu.frags = list_new();
|
|
|
|
} else {
|
|
|
|
/* fragment -> set backpointer and add to zero lsps list */
|
|
|
|
assert(lsp0);
|
|
|
|
lsp->lspu.zero_lsp = lsp0;
|
|
|
|
listnode_add(lsp0->lspu.frags, lsp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr,
|
|
|
|
struct isis_tlvs *tlvs, struct stream *stream,
|
2017-07-30 19:49:19 +02:00
|
|
|
struct isis_area *area, int level, bool confusion)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
if (lsp->own_lsp) {
|
2018-08-03 20:03:29 +02:00
|
|
|
flog_err(
|
2018-09-13 21:34:28 +02:00
|
|
|
EC_LIB_DEVELOPMENT,
|
2017-07-17 14:03:14 +02:00
|
|
|
"ISIS-Upd (%s): BUG updating LSP %s still marked as own LSP",
|
2017-07-05 18:37:36 +02:00
|
|
|
area->area_tag, rawlspid_print(lsp->hdr.lsp_id));
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_clear_data(lsp);
|
|
|
|
lsp->own_lsp = 0;
|
|
|
|
}
|
|
|
|
|
2017-07-30 19:49:19 +02:00
|
|
|
if (confusion) {
|
2018-05-31 15:14:26 +02:00
|
|
|
lsp_purge(lsp, level, NULL);
|
|
|
|
} else {
|
|
|
|
lsp_update_data(lsp, hdr, tlvs, stream, area, level);
|
2017-07-30 19:49:19 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-09-23 20:01:43 +02:00
|
|
|
if (LSP_FRAGMENT(lsp->hdr.lsp_id) && !lsp->lspu.zero_lsp) {
|
|
|
|
uint8_t lspid[ISIS_SYS_ID_LEN + 2];
|
|
|
|
struct isis_lsp *lsp0;
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-09-23 20:01:43 +02:00
|
|
|
memcpy(lspid, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 1);
|
|
|
|
LSP_FRAGMENT(lspid) = 0;
|
|
|
|
lsp0 = lsp_search(lspid, area->lspdb[level - 1]);
|
|
|
|
if (lsp0)
|
|
|
|
lsp_link_fragment(lsp, lsp0);
|
2017-08-11 15:28:58 +02:00
|
|
|
}
|
2017-09-23 20:01:43 +02:00
|
|
|
|
2017-10-05 17:47:12 +02:00
|
|
|
if (lsp->hdr.seqno)
|
|
|
|
isis_spf_schedule(lsp->area, lsp->level);
|
2017-08-11 15:28:58 +02:00
|
|
|
}
|
|
|
|
|
2003-12-23 09:09:43 +01:00
|
|
|
/* creation of LSP directly from what we received */
|
2017-07-05 18:37:36 +02:00
|
|
|
struct isis_lsp *lsp_new_from_recv(struct isis_lsp_hdr *hdr,
|
|
|
|
struct isis_tlvs *tlvs,
|
|
|
|
struct stream *stream, struct isis_lsp *lsp0,
|
|
|
|
struct isis_area *area, int level)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_lsp *lsp;
|
|
|
|
|
|
|
|
lsp = XCALLOC(MTYPE_ISIS_LSP, sizeof(struct isis_lsp));
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_update_data(lsp, hdr, tlvs, stream, area, level);
|
2017-08-11 15:28:58 +02:00
|
|
|
lsp_link_fragment(lsp, lsp0);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
return lsp;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2018-10-16 19:17:10 +02:00
|
|
|
static void lsp_adjust_stream(struct isis_lsp *lsp)
|
|
|
|
{
|
|
|
|
if (lsp->pdu) {
|
|
|
|
if (STREAM_SIZE(lsp->pdu) == LLC_LEN + lsp->area->lsp_mtu)
|
|
|
|
return;
|
|
|
|
stream_free(lsp->pdu);
|
|
|
|
}
|
|
|
|
|
|
|
|
lsp->pdu = stream_new(LLC_LEN + lsp->area->lsp_mtu);
|
|
|
|
}
|
|
|
|
|
2018-03-27 21:13:34 +02:00
|
|
|
struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id,
|
2017-07-05 18:37:36 +02:00
|
|
|
uint16_t rem_lifetime, uint32_t seqno,
|
2017-08-11 15:28:58 +02:00
|
|
|
uint8_t lsp_bits, uint16_t checksum,
|
|
|
|
struct isis_lsp *lsp0, int level)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_lsp *lsp;
|
|
|
|
|
|
|
|
lsp = XCALLOC(MTYPE_ISIS_LSP, sizeof(struct isis_lsp));
|
|
|
|
lsp->area = area;
|
|
|
|
|
2018-10-16 19:17:10 +02:00
|
|
|
lsp_adjust_stream(lsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
/* Minimal LSP PDU size */
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->hdr.pdu_len = ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN;
|
|
|
|
memcpy(lsp->hdr.lsp_id, lsp_id, sizeof(lsp->hdr.lsp_id));
|
|
|
|
lsp->hdr.checksum = checksum;
|
|
|
|
lsp->hdr.seqno = seqno;
|
|
|
|
lsp->hdr.rem_lifetime = rem_lifetime;
|
|
|
|
lsp->hdr.lsp_bits = lsp_bits;
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp->level = level;
|
|
|
|
lsp->age_out = ZERO_AGE_LIFETIME;
|
2017-08-11 15:28:58 +02:00
|
|
|
lsp_link_fragment(lsp, lsp0);
|
2017-08-29 00:14:43 +02:00
|
|
|
put_lsp_hdr(lsp, NULL, false);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (isis->debugs & DEBUG_EVENTS)
|
|
|
|
zlog_debug("New LSP with ID %s-%02x-%02x len %d seqnum %08x",
|
2017-07-05 18:37:36 +02:00
|
|
|
sysid_print(lsp_id), LSP_PSEUDO_ID(lsp->hdr.lsp_id),
|
|
|
|
LSP_FRAGMENT(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
|
|
|
|
lsp->hdr.seqno);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
return lsp;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-05 18:37:36 +02:00
|
|
|
dict_alloc_insert(lspdb, lsp->hdr.lsp_id, lsp);
|
|
|
|
if (lsp->hdr.seqno)
|
2017-07-17 14:03:14 +02:00
|
|
|
isis_spf_schedule(lsp->area, lsp->level);
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build a list of LSPs with non-zero ht bounded by start and stop ids
|
|
|
|
*/
|
2018-03-27 21:13:34 +02:00
|
|
|
void lsp_build_list_nonzero_ht(uint8_t *start_id, uint8_t *stop_id,
|
2017-07-17 14:03:14 +02:00
|
|
|
struct list *list, dict_t *lspdb)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2018-11-10 16:14:40 +01:00
|
|
|
for (dnode_t *curr = dict_lower_bound(lspdb, start_id);
|
|
|
|
curr; curr = dict_next(lspdb, curr)) {
|
|
|
|
struct isis_lsp *lsp = curr->dict_data;
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2018-11-10 16:14:40 +01:00
|
|
|
if (memcmp(lsp->hdr.lsp_id, stop_id,
|
|
|
|
ISIS_SYS_ID_LEN + 2) > 0)
|
2017-07-17 14:03:14 +02:00
|
|
|
break;
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2018-11-10 16:14:40 +01:00
|
|
|
if (lsp->hdr.rem_lifetime)
|
|
|
|
listnode_add(list, lsp);
|
|
|
|
}
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static void lsp_set_time(struct isis_lsp *lsp)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
assert(lsp);
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
if (lsp->hdr.rem_lifetime == 0) {
|
2017-07-17 14:03:14 +02:00
|
|
|
if (lsp->age_out > 0)
|
|
|
|
lsp->age_out--;
|
|
|
|
return;
|
|
|
|
}
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->hdr.rem_lifetime--;
|
|
|
|
if (lsp->pdu && stream_get_endp(lsp->pdu) >= 12)
|
|
|
|
stream_putw_at(lsp->pdu, 10, lsp->hdr.rem_lifetime);
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2018-11-23 02:11:07 +01:00
|
|
|
void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_dynhn *dyn = NULL;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint8_t id[SYSID_STRLEN];
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (dynhost)
|
|
|
|
dyn = dynhn_find_by_id(lsp_id);
|
|
|
|
else
|
|
|
|
dyn = NULL;
|
|
|
|
|
|
|
|
if (dyn)
|
2017-07-05 18:37:36 +02:00
|
|
|
sprintf((char *)id, "%.14s", dyn->hostname);
|
2017-07-17 14:03:14 +02:00
|
|
|
else if (!memcmp(isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost)
|
2017-08-29 01:52:29 +02:00
|
|
|
sprintf((char *)id, "%.14s", cmd_hostname_get());
|
2017-07-17 14:03:14 +02:00
|
|
|
else
|
|
|
|
memcpy(id, sysid_print(lsp_id), 15);
|
|
|
|
if (frag)
|
2018-11-23 02:11:07 +01:00
|
|
|
sprintf(dest, "%s.%02x-%02x", id, LSP_PSEUDO_ID(lsp_id),
|
2017-07-17 14:03:14 +02:00
|
|
|
LSP_FRAGMENT(lsp_id));
|
|
|
|
else
|
2018-11-23 02:11:07 +01:00
|
|
|
sprintf(dest, "%s.%02x", id, LSP_PSEUDO_ID(lsp_id));
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2004-09-10 22:48:21 +02:00
|
|
|
/* Convert the lsp attribute bits to attribute string */
|
2018-06-19 18:22:01 +02:00
|
|
|
static const char *lsp_bits2string(uint8_t lsp_bits, char *buf, size_t buf_size)
|
2004-09-10 22:48:21 +02:00
|
|
|
{
|
2018-06-19 18:22:01 +02:00
|
|
|
char *pos = buf;
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
if (!lsp_bits)
|
2017-07-17 14:03:14 +02:00
|
|
|
return " none";
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2018-06-19 18:22:01 +02:00
|
|
|
if (buf_size < 2 * 3)
|
|
|
|
return " error";
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* we only focus on the default metric */
|
|
|
|
pos += sprintf(pos, "%d/",
|
2017-07-05 18:37:36 +02:00
|
|
|
ISIS_MASK_LSP_ATT_DEFAULT_BIT(lsp_bits) ? 1 : 0);
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
pos += sprintf(pos, "%d/",
|
2017-07-05 18:37:36 +02:00
|
|
|
ISIS_MASK_LSP_PARTITION_BIT(lsp_bits) ? 1 : 0);
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2018-06-29 17:01:29 +02:00
|
|
|
sprintf(pos, "%d", ISIS_MASK_LSP_OL_BIT(lsp_bits) ? 1 : 0);
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2018-06-19 18:22:01 +02:00
|
|
|
return buf;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* this function prints the lsp on show isis database */
|
2017-07-17 14:03:14 +02:00
|
|
|
void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2018-11-23 02:11:07 +01:00
|
|
|
char LSPid[255];
|
2017-07-17 14:03:14 +02:00
|
|
|
char age_out[8];
|
2018-06-19 18:22:01 +02:00
|
|
|
char b[200];
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lspid_print(lsp->hdr.lsp_id, LSPid, dynhost, 1);
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_out(vty, "%-21s%c ", LSPid, lsp->own_lsp ? '*' : ' ');
|
2017-07-05 18:37:36 +02:00
|
|
|
vty_out(vty, "%5" PRIu16 " ", lsp->hdr.pdu_len);
|
|
|
|
vty_out(vty, "0x%08" PRIx32 " ", lsp->hdr.seqno);
|
|
|
|
vty_out(vty, "0x%04" PRIx16 " ", lsp->hdr.checksum);
|
|
|
|
if (lsp->hdr.rem_lifetime == 0) {
|
|
|
|
snprintf(age_out, 8, "(%d)", lsp->age_out);
|
2017-07-17 14:03:14 +02:00
|
|
|
age_out[7] = '\0';
|
|
|
|
vty_out(vty, "%7s ", age_out);
|
|
|
|
} else
|
2017-07-05 18:37:36 +02:00
|
|
|
vty_out(vty, " %5" PRIu16 " ", lsp->hdr.rem_lifetime);
|
2018-06-19 18:22:01 +02:00
|
|
|
vty_out(vty, "%s\n", lsp_bits2string(lsp->hdr.lsp_bits, b, sizeof(b)));
|
2017-04-27 13:56:45 +02:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_print(lsp, vty, dynhost);
|
2017-07-05 18:37:36 +02:00
|
|
|
if (lsp->tlvs)
|
|
|
|
vty_multiline(vty, " ", "%s", isis_format_tlvs(lsp->tlvs));
|
2017-07-17 14:03:14 +02:00
|
|
|
vty_out(vty, "\n");
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* print all the lsps info in the local lspdb */
|
2017-07-17 14:03:14 +02:00
|
|
|
int lsp_print_all(struct vty *vty, dict_t *lspdb, char detail, char dynhost)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
dnode_t *node = dict_first(lspdb), *next;
|
|
|
|
int lsp_count = 0;
|
|
|
|
|
|
|
|
if (detail == ISIS_UI_LEVEL_BRIEF) {
|
|
|
|
while (node != NULL) {
|
|
|
|
/* I think it is unnecessary, so I comment it out */
|
|
|
|
/* dict_contains (lspdb, node); */
|
|
|
|
next = dict_next(lspdb, node);
|
|
|
|
lsp_print(dnode_get(node), vty, dynhost);
|
|
|
|
node = next;
|
|
|
|
lsp_count++;
|
|
|
|
}
|
|
|
|
} else if (detail == ISIS_UI_LEVEL_DETAIL) {
|
|
|
|
while (node != NULL) {
|
|
|
|
next = dict_next(lspdb, node);
|
|
|
|
lsp_print_detail(dnode_get(node), vty, dynhost);
|
|
|
|
node = next;
|
|
|
|
lsp_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return lsp_count;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2018-03-27 21:13:34 +02:00
|
|
|
static uint16_t lsp_rem_lifetime(struct isis_area *area, int level)
|
2012-03-24 16:35:20 +01:00
|
|
|
{
|
2018-03-27 21:13:34 +02:00
|
|
|
uint16_t rem_lifetime;
|
2012-03-24 16:35:20 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Add jitter to configured LSP lifetime */
|
|
|
|
rem_lifetime =
|
|
|
|
isis_jitter(area->max_lsp_lifetime[level - 1], MAX_AGE_JITTER);
|
2012-03-24 16:35:20 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* No jitter if the max refresh will be less than configure gen interval
|
|
|
|
*/
|
|
|
|
/* N.B. this calucation is acceptable since rem_lifetime is in
|
|
|
|
* [332,65535] at
|
|
|
|
* this point */
|
|
|
|
if (area->lsp_gen_interval[level - 1] > (rem_lifetime - 300))
|
|
|
|
rem_lifetime = area->max_lsp_lifetime[level - 1];
|
2012-03-24 16:35:20 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return rem_lifetime;
|
2012-03-24 16:35:20 +01:00
|
|
|
}
|
|
|
|
|
2018-03-27 21:13:34 +02:00
|
|
|
static uint16_t lsp_refresh_time(struct isis_lsp *lsp, uint16_t rem_lifetime)
|
2012-03-24 16:35:20 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_area *area = lsp->area;
|
|
|
|
int level = lsp->level;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint16_t refresh_time;
|
2012-03-24 16:35:20 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* Add jitter to LSP refresh time */
|
|
|
|
refresh_time =
|
|
|
|
isis_jitter(area->lsp_refresh[level - 1], MAX_LSP_GEN_JITTER);
|
2012-03-24 16:35:20 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* RFC 4444 : make sure the refresh time is at least less than 300
|
|
|
|
* of the remaining lifetime and more than gen interval */
|
|
|
|
if (refresh_time <= area->lsp_gen_interval[level - 1]
|
|
|
|
|| refresh_time > (rem_lifetime - 300))
|
|
|
|
refresh_time = rem_lifetime - 300;
|
2012-03-24 16:35:20 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/* In cornercases, refresh_time might be <= lsp_gen_interval, however
|
|
|
|
* we accept this violation to satisfy refresh_time <= rem_lifetime -
|
|
|
|
* 300 */
|
2012-03-24 16:35:20 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return refresh_time;
|
2012-03-24 16:35:20 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp,
|
2017-07-05 18:37:36 +02:00
|
|
|
struct isis_area *area)
|
2015-11-12 14:24:22 +01:00
|
|
|
{
|
2017-07-05 18:37:36 +02:00
|
|
|
struct route_table *er_table = get_ext_reach(area, AF_INET, lsp->level);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!er_table)
|
|
|
|
return;
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
for (struct route_node *rn = route_top(er_table); rn;
|
|
|
|
rn = route_next(rn)) {
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!rn->info)
|
|
|
|
continue;
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
struct prefix_ipv4 *ipv4 = (struct prefix_ipv4 *)&rn->p;
|
|
|
|
struct isis_ext_info *info = rn->info;
|
2015-11-12 14:24:22 +01:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
uint32_t metric = info->metric;
|
|
|
|
if (metric > MAX_WIDE_PATH_METRIC)
|
|
|
|
metric = MAX_WIDE_PATH_METRIC;
|
|
|
|
if (area->oldmetric && metric > 0x3f)
|
|
|
|
metric = 0x3f;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
if (area->oldmetric)
|
|
|
|
isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4,
|
|
|
|
metric);
|
|
|
|
if (area->newmetric)
|
|
|
|
isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4,
|
|
|
|
metric);
|
|
|
|
}
|
2017-04-27 13:56:45 +02:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp,
|
2017-07-05 18:37:36 +02:00
|
|
|
struct isis_area *area)
|
2015-11-12 14:24:22 +01:00
|
|
|
{
|
2017-07-05 18:37:36 +02:00
|
|
|
struct route_table *er_table =
|
|
|
|
get_ext_reach(area, AF_INET6, lsp->level);
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!er_table)
|
|
|
|
return;
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
for (struct route_node *rn = route_top(er_table); rn;
|
2018-07-22 21:49:02 +02:00
|
|
|
rn = srcdest_route_next(rn)) {
|
2017-07-17 14:03:14 +02:00
|
|
|
if (!rn->info)
|
|
|
|
continue;
|
2017-07-05 18:37:36 +02:00
|
|
|
struct isis_ext_info *info = rn->info;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-07-22 21:49:02 +02:00
|
|
|
struct prefix_ipv6 *p, *src_p;
|
|
|
|
srcdest_rnode_prefixes(rn, (const struct prefix **)&p,
|
|
|
|
(const struct prefix **)&src_p);
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
uint32_t metric = info->metric;
|
2017-07-17 14:03:14 +02:00
|
|
|
if (info->metric > MAX_WIDE_PATH_METRIC)
|
2017-07-05 18:37:36 +02:00
|
|
|
metric = MAX_WIDE_PATH_METRIC;
|
2018-07-22 21:49:02 +02:00
|
|
|
|
|
|
|
if (!src_p || !src_p->prefixlen) {
|
|
|
|
isis_tlvs_add_ipv6_reach(lsp->tlvs,
|
|
|
|
isis_area_ipv6_topology(area),
|
|
|
|
p, metric);
|
|
|
|
} else if (isis_area_ipv6_dstsrc_enabled(area)) {
|
|
|
|
isis_tlvs_add_ipv6_dstsrc_reach(lsp->tlvs,
|
|
|
|
ISIS_MT_IPV6_DSTSRC,
|
|
|
|
p, src_p, metric);
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2015-11-12 14:24:22 +01:00
|
|
|
}
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
static void lsp_build_ext_reach(struct isis_lsp *lsp, struct isis_area *area)
|
2015-11-12 14:24:22 +01:00
|
|
|
{
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_build_ext_reach_ipv4(lsp, area);
|
|
|
|
lsp_build_ext_reach_ipv6(lsp, area);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct isis_lsp *lsp_next_frag(uint8_t frag_num, struct isis_lsp *lsp0,
|
|
|
|
struct isis_area *area, int level)
|
|
|
|
{
|
|
|
|
struct isis_lsp *lsp;
|
|
|
|
uint8_t frag_id[ISIS_SYS_ID_LEN + 2];
|
|
|
|
|
|
|
|
memcpy(frag_id, lsp0->hdr.lsp_id, ISIS_SYS_ID_LEN + 1);
|
|
|
|
LSP_FRAGMENT(frag_id) = frag_num;
|
|
|
|
|
|
|
|
lsp = lsp_search(frag_id, area->lspdb[level - 1]);
|
|
|
|
if (lsp) {
|
|
|
|
lsp_clear_data(lsp);
|
2017-09-23 20:01:43 +02:00
|
|
|
if (!lsp->lspu.zero_lsp)
|
|
|
|
lsp_link_fragment(lsp, lsp0);
|
2017-07-05 18:37:36 +02:00
|
|
|
return lsp;
|
|
|
|
}
|
|
|
|
|
|
|
|
lsp = lsp_new(area, frag_id, lsp0->hdr.rem_lifetime, 0,
|
|
|
|
lsp_bits_generate(level, area->overload_bit,
|
|
|
|
area->attached_bit),
|
2017-08-11 15:28:58 +02:00
|
|
|
0, lsp0, level);
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->own_lsp = 1;
|
|
|
|
lsp_insert(lsp, area->lspdb[level - 1]);
|
|
|
|
return lsp;
|
2015-11-12 14:24:22 +01:00
|
|
|
}
|
|
|
|
|
2003-12-23 09:09:43 +01:00
|
|
|
/*
|
2017-07-17 14:03:14 +02:00
|
|
|
* Builds the LSP data part. This func creates a new frag whenever
|
2003-12-23 09:09:43 +01:00
|
|
|
* area->lsp_frag_threshold is exceeded.
|
|
|
|
*/
|
2017-07-17 14:03:14 +02:00
|
|
|
static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
int level = lsp->level;
|
2017-07-05 18:37:36 +02:00
|
|
|
char buf[PREFIX2STR_BUFFER];
|
|
|
|
struct listnode *node;
|
|
|
|
struct isis_lsp *frag;
|
|
|
|
|
|
|
|
lsp_clear_data(lsp);
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag))
|
|
|
|
lsp_clear_data(frag);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->tlvs = isis_alloc_tlvs();
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug("ISIS (%s): Constructing local system LSP for level %d",
|
|
|
|
area->area_tag, level);
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->hdr.lsp_bits = lsp_bits_generate(level, area->overload_bit,
|
|
|
|
area->attached_bit);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_add_auth(lsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_tlvs_add_area_addresses(lsp->tlvs, area->area_addrs);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
/* Protocols Supported */
|
|
|
|
if (area->ip_circuits > 0 || area->ipv6_circuits > 0) {
|
2017-07-05 18:37:36 +02:00
|
|
|
struct nlpids nlpids = {.count = 0};
|
2017-07-17 14:03:14 +02:00
|
|
|
if (area->ip_circuits > 0) {
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Found IPv4 circuit, adding IPv4 to NLPIDs",
|
|
|
|
area->area_tag);
|
2017-07-05 18:37:36 +02:00
|
|
|
nlpids.nlpids[nlpids.count] = NLPID_IP;
|
|
|
|
nlpids.count++;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
if (area->ipv6_circuits > 0) {
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Found IPv6 circuit, adding IPv6 to NLPIDs",
|
|
|
|
area->area_tag);
|
2017-07-05 18:37:36 +02:00
|
|
|
nlpids.nlpids[nlpids.count] = NLPID_IPV6;
|
|
|
|
nlpids.count++;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_tlvs_set_protocols_supported(lsp->tlvs, &nlpids);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (area_is_mt(area)) {
|
|
|
|
lsp_debug("ISIS (%s): Adding MT router tlv...", area->area_tag);
|
|
|
|
|
|
|
|
struct isis_area_mt_setting **mt_settings;
|
|
|
|
unsigned int mt_count;
|
|
|
|
|
|
|
|
mt_settings = area_mt_settings(area, &mt_count);
|
|
|
|
for (unsigned int i = 0; i < mt_count; i++) {
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_tlvs_add_mt_router_info(
|
|
|
|
lsp->tlvs, mt_settings[i]->mtid,
|
|
|
|
mt_settings[i]->overload, false);
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug("ISIS (%s): MT %s", area->area_tag,
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_mtid2str(mt_settings[i]->mtid));
|
2005-09-26 19:39:48 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
} else {
|
|
|
|
lsp_debug("ISIS (%s): Not adding MT router tlv (disabled)",
|
|
|
|
area->area_tag);
|
|
|
|
}
|
|
|
|
/* Dynamic Hostname */
|
|
|
|
if (area->dynhostname) {
|
2017-08-29 01:52:29 +02:00
|
|
|
isis_tlvs_set_dynamic_hostname(lsp->tlvs, cmd_hostname_get());
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_debug("ISIS (%s): Adding dynamic hostname '%s'",
|
2017-08-29 01:52:29 +02:00
|
|
|
area->area_tag, cmd_hostname_get());
|
2017-07-17 14:03:14 +02:00
|
|
|
} else {
|
|
|
|
lsp_debug("ISIS (%s): Not adding dynamic hostname (disabled)",
|
|
|
|
area->area_tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* IPv4 address and TE router ID TLVs. In case of the first one we don't
|
|
|
|
* follow "C" vendor, but "J" vendor behavior - one IPv4 address is put
|
|
|
|
* into
|
|
|
|
* LSP and this address is same as router id. */
|
|
|
|
if (isis->router_id != 0) {
|
2017-07-05 18:37:36 +02:00
|
|
|
struct in_addr id = {.s_addr = isis->router_id};
|
|
|
|
inet_ntop(AF_INET, &id, buf, sizeof(buf));
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug("ISIS (%s): Adding router ID %s as IPv4 tlv.",
|
|
|
|
area->area_tag, buf);
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_tlvs_add_ipv4_address(lsp->tlvs, &id);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
/* Exactly same data is put into TE router ID TLV, but only if
|
|
|
|
* new style
|
|
|
|
* TLV's are in use. */
|
|
|
|
if (area->newmetric) {
|
2017-07-05 18:37:36 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Adding router ID also as TE router ID tlv.",
|
|
|
|
area->area_tag);
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_tlvs_set_te_router_id(lsp->tlvs, &id);
|
2005-09-26 19:39:48 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
} else {
|
|
|
|
lsp_debug("ISIS (%s): Router ID is unset. Not adding tlv.",
|
|
|
|
area->area_tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
lsp_debug("ISIS (%s): Adding circuit specific information.",
|
|
|
|
area->area_tag);
|
|
|
|
|
2018-05-10 19:05:40 +02:00
|
|
|
if (fabricd) {
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Adding tier %" PRIu8 " spine-leaf-extension tlv.",
|
|
|
|
area->area_tag, fabricd_tier(area));
|
|
|
|
isis_tlvs_add_spine_leaf(lsp->tlvs, fabricd_tier(area), true,
|
|
|
|
false, false, false);
|
|
|
|
}
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
struct isis_circuit *circuit;
|
2017-07-17 14:03:14 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
|
|
|
|
if (!circuit->interface)
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Processing %s circuit %p with unknown interface",
|
|
|
|
area->area_tag,
|
|
|
|
circuit_type2string(circuit->circ_type),
|
|
|
|
circuit);
|
|
|
|
else
|
|
|
|
lsp_debug("ISIS (%s): Processing %s circuit %s",
|
|
|
|
area->area_tag,
|
|
|
|
circuit_type2string(circuit->circ_type),
|
|
|
|
circuit->interface->name);
|
|
|
|
|
|
|
|
if (circuit->state != C_STATE_UP) {
|
|
|
|
lsp_debug("ISIS (%s): Circuit is not up, ignoring.",
|
|
|
|
area->area_tag);
|
|
|
|
continue;
|
2005-09-26 19:39:48 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
uint32_t metric = area->oldmetric
|
|
|
|
? circuit->metric[level - 1]
|
|
|
|
: circuit->te_metric[level - 1];
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (circuit->ip_router && circuit->ip_addrs
|
|
|
|
&& circuit->ip_addrs->count > 0) {
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Circuit has IPv4 active, adding respective TLVs.",
|
|
|
|
area->area_tag);
|
2017-07-05 18:37:36 +02:00
|
|
|
struct listnode *ipnode;
|
|
|
|
struct prefix_ipv4 *ipv4;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode,
|
|
|
|
ipv4)) {
|
|
|
|
if (area->oldmetric) {
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS (%s): Adding old-style IP reachability for %s",
|
|
|
|
area->area_tag,
|
|
|
|
prefix2str(ipv4, buf,
|
|
|
|
sizeof(buf)));
|
|
|
|
isis_tlvs_add_oldstyle_ip_reach(
|
|
|
|
lsp->tlvs, ipv4, metric);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2017-07-05 18:37:36 +02:00
|
|
|
|
|
|
|
if (area->newmetric) {
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS (%s): Adding te-style IP reachability for %s",
|
|
|
|
area->area_tag,
|
|
|
|
prefix2str(ipv4, buf,
|
|
|
|
sizeof(buf)));
|
|
|
|
isis_tlvs_add_extended_ip_reach(
|
|
|
|
lsp->tlvs, ipv4, metric);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
}
|
2004-09-10 22:48:21 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (circuit->ipv6_router && circuit->ipv6_non_link
|
|
|
|
&& circuit->ipv6_non_link->count > 0) {
|
2017-07-05 18:37:36 +02:00
|
|
|
struct listnode *ipnode;
|
|
|
|
struct prefix_ipv6 *ipv6;
|
2017-07-17 14:03:14 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link,
|
|
|
|
ipnode, ipv6)) {
|
|
|
|
lsp_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS (%s): Adding IPv6 reachability for %s",
|
|
|
|
area->area_tag,
|
|
|
|
prefix2str(ipv6, buf, sizeof(buf)));
|
|
|
|
isis_tlvs_add_ipv6_reach(
|
|
|
|
lsp->tlvs,
|
|
|
|
isis_area_ipv6_topology(area), ipv6,
|
|
|
|
metric);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2005-09-26 19:39:48 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
switch (circuit->circ_type) {
|
|
|
|
case CIRCUIT_T_BROADCAST:
|
|
|
|
if (level & circuit->is_type) {
|
2017-07-05 18:37:36 +02:00
|
|
|
uint8_t *ne_id =
|
|
|
|
(level == IS_LEVEL_1)
|
|
|
|
? circuit->u.bc.l1_desig_is
|
|
|
|
: circuit->u.bc.l2_desig_is;
|
|
|
|
|
|
|
|
if (LSP_PSEUDO_ID(ne_id)) {
|
|
|
|
if (area->oldmetric) {
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Adding DIS %s.%02x as old-style neighbor",
|
|
|
|
area->area_tag,
|
2017-07-05 18:37:36 +02:00
|
|
|
sysid_print(ne_id),
|
|
|
|
LSP_PSEUDO_ID(ne_id));
|
|
|
|
isis_tlvs_add_oldstyle_reach(
|
|
|
|
lsp->tlvs, ne_id,
|
|
|
|
metric);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2017-07-05 18:37:36 +02:00
|
|
|
if (area->newmetric) {
|
|
|
|
uint8_t subtlvs[256];
|
|
|
|
uint8_t subtlv_len;
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (IS_MPLS_TE(isisMplsTE)
|
2018-10-03 16:38:49 +02:00
|
|
|
&& circuit->interface
|
2017-07-17 14:03:14 +02:00
|
|
|
&& HAS_LINK_PARAMS(
|
|
|
|
circuit->interface))
|
2017-07-05 18:37:36 +02:00
|
|
|
subtlv_len = add_te_subtlvs(
|
|
|
|
subtlvs,
|
2017-07-17 14:03:14 +02:00
|
|
|
circuit->mtc);
|
|
|
|
else
|
2017-07-05 18:37:36 +02:00
|
|
|
subtlv_len = 0;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
tlvs_add_mt_bcast(
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->tlvs, circuit,
|
|
|
|
level, ne_id, metric,
|
|
|
|
subtlvs, subtlv_len);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Circuit is not active for current level. Not adding IS neighbors",
|
|
|
|
area->area_tag);
|
|
|
|
}
|
|
|
|
break;
|
2017-07-05 18:37:36 +02:00
|
|
|
case CIRCUIT_T_P2P: {
|
|
|
|
struct isis_adjacency *nei = circuit->u.p2p.neighbor;
|
2018-03-05 21:01:21 +01:00
|
|
|
if (nei && nei->adj_state == ISIS_ADJ_UP
|
|
|
|
&& (level & nei->circuit_t)) {
|
2017-07-05 18:37:36 +02:00
|
|
|
uint8_t ne_id[7];
|
|
|
|
memcpy(ne_id, nei->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
LSP_PSEUDO_ID(ne_id) = 0;
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if (area->oldmetric) {
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Adding old-style is reach for %s",
|
|
|
|
area->area_tag,
|
2017-07-05 18:37:36 +02:00
|
|
|
sysid_print(ne_id));
|
|
|
|
isis_tlvs_add_oldstyle_reach(
|
|
|
|
lsp->tlvs, ne_id, metric);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
if (area->newmetric) {
|
2017-07-05 18:37:36 +02:00
|
|
|
uint8_t subtlvs[256];
|
|
|
|
uint8_t subtlv_len;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (IS_MPLS_TE(isisMplsTE)
|
2018-06-28 16:48:37 +02:00
|
|
|
&& circuit->interface != NULL
|
2017-07-17 14:03:14 +02:00
|
|
|
&& HAS_LINK_PARAMS(
|
|
|
|
circuit->interface))
|
|
|
|
/* Update Local and Remote IP
|
|
|
|
* address for MPLS TE circuit
|
|
|
|
* parameters */
|
|
|
|
/* NOTE sure that it is the
|
|
|
|
* pertinent place for that
|
|
|
|
* updates */
|
|
|
|
/* Local IP address could be
|
|
|
|
* updated in isis_circuit.c -
|
|
|
|
* isis_circuit_add_addr() */
|
|
|
|
/* But, where update remote IP
|
|
|
|
* address ? in isis_pdu.c -
|
|
|
|
* process_p2p_hello() ? */
|
|
|
|
|
|
|
|
/* Add SubTLVs & Adjust real
|
|
|
|
* size of SubTLVs */
|
2017-07-05 18:37:36 +02:00
|
|
|
subtlv_len = add_te_subtlvs(
|
|
|
|
subtlvs, circuit->mtc);
|
2017-07-17 14:03:14 +02:00
|
|
|
else
|
|
|
|
/* Or keep only TE metric with
|
|
|
|
* no SubTLVs if MPLS_TE is off
|
|
|
|
*/
|
2017-07-05 18:37:36 +02:00
|
|
|
subtlv_len = 0;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-05-23 15:37:45 +02:00
|
|
|
uint32_t neighbor_metric;
|
|
|
|
if (fabricd_tier(area) == 0) {
|
|
|
|
neighbor_metric = 0xffe;
|
|
|
|
} else {
|
|
|
|
neighbor_metric = metric;
|
|
|
|
}
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
tlvs_add_mt_p2p(lsp->tlvs, circuit,
|
2018-05-23 15:37:45 +02:00
|
|
|
ne_id, neighbor_metric,
|
|
|
|
subtlvs, subtlv_len);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): No adjacency for given level on this circuit. Not adding IS neighbors",
|
|
|
|
area->area_tag);
|
|
|
|
}
|
2017-07-05 18:37:36 +02:00
|
|
|
} break;
|
2017-07-17 14:03:14 +02:00
|
|
|
case CIRCUIT_T_LOOPBACK:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
zlog_warn("lsp_area_create: unknown circuit type");
|
2005-09-26 19:39:48 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_build_ext_reach(lsp, area);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
struct isis_tlvs *tlvs = lsp->tlvs;
|
|
|
|
lsp->tlvs = NULL;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-10-16 19:17:10 +02:00
|
|
|
lsp_adjust_stream(lsp);
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_pack_pdu(lsp);
|
|
|
|
size_t tlv_space = STREAM_WRITEABLE(lsp->pdu) - LLC_LEN;
|
|
|
|
lsp_clear_data(lsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
struct list *fragments = isis_fragment_tlvs(tlvs, tlv_space);
|
|
|
|
if (!fragments) {
|
|
|
|
zlog_warn("BUG: could not fragment own LSP:");
|
2018-03-06 20:02:52 +01:00
|
|
|
log_multiline(LOG_WARNING, " ", "%s",
|
|
|
|
isis_format_tlvs(tlvs));
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_free_tlvs(tlvs);
|
|
|
|
return;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_free_tlvs(tlvs);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-09-22 22:17:20 +02:00
|
|
|
bool fragment_overflow = false;
|
2017-07-05 18:37:36 +02:00
|
|
|
frag = lsp;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(fragments, node, tlvs)) {
|
|
|
|
if (node != listhead(fragments)) {
|
2017-09-22 22:17:20 +02:00
|
|
|
if (LSP_FRAGMENT(frag->hdr.lsp_id) == 255) {
|
|
|
|
if (!fragment_overflow) {
|
|
|
|
fragment_overflow = true;
|
2018-03-06 20:02:52 +01:00
|
|
|
zlog_warn(
|
|
|
|
"ISIS (%s): Too much information for 256 fragments",
|
|
|
|
area->area_tag);
|
2017-09-22 22:17:20 +02:00
|
|
|
}
|
|
|
|
isis_free_tlvs(tlvs);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
frag = lsp_next_frag(LSP_FRAGMENT(frag->hdr.lsp_id) + 1,
|
|
|
|
lsp, area, level);
|
2018-10-16 19:17:10 +02:00
|
|
|
lsp_adjust_stream(frag);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2017-07-05 18:37:36 +02:00
|
|
|
frag->tlvs = tlvs;
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
2018-10-02 11:39:51 +02:00
|
|
|
list_delete(&fragments);
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_debug("ISIS (%s): LSP construction is complete. Serializing...",
|
|
|
|
area->area_tag);
|
2017-07-17 14:03:14 +02:00
|
|
|
return;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-03-24 16:35:20 +01:00
|
|
|
* 7.3.7 and 7.3.9 Generation on non-pseudonode LSPs
|
2003-12-23 09:09:43 +01:00
|
|
|
*/
|
2017-07-17 14:03:14 +02:00
|
|
|
int lsp_generate(struct isis_area *area, int level)
|
2004-09-10 22:48:21 +02:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_lsp *oldlsp, *newlsp;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint32_t seq_num = 0;
|
|
|
|
uint8_t lspid[ISIS_SYS_ID_LEN + 2];
|
|
|
|
uint16_t rem_lifetime, refresh_time;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if ((area == NULL) || (area->is_type & level) != level)
|
|
|
|
return ISIS_ERROR;
|
|
|
|
|
|
|
|
memset(&lspid, 0, ISIS_SYS_ID_LEN + 2);
|
|
|
|
memcpy(&lspid, isis->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
|
|
|
|
/* only builds the lsp if the area shares the level */
|
|
|
|
oldlsp = lsp_search(lspid, area->lspdb[level - 1]);
|
|
|
|
if (oldlsp) {
|
|
|
|
/* FIXME: we should actually initiate a purge */
|
2017-07-05 18:37:36 +02:00
|
|
|
seq_num = oldlsp->hdr.seqno;
|
|
|
|
lsp_search_and_destroy(oldlsp->hdr.lsp_id,
|
2017-07-17 14:03:14 +02:00
|
|
|
area->lspdb[level - 1]);
|
|
|
|
}
|
|
|
|
rem_lifetime = lsp_rem_lifetime(area, level);
|
|
|
|
newlsp =
|
|
|
|
lsp_new(area, lspid, rem_lifetime, seq_num,
|
|
|
|
area->is_type | area->overload_bit | area->attached_bit,
|
2017-08-11 15:28:58 +02:00
|
|
|
0, NULL, level);
|
2017-07-17 14:03:14 +02:00
|
|
|
newlsp->area = area;
|
|
|
|
newlsp->own_lsp = 1;
|
|
|
|
|
|
|
|
lsp_insert(newlsp, area->lspdb[level - 1]);
|
|
|
|
/* build_lsp_data (newlsp, area); */
|
|
|
|
lsp_build(newlsp, area);
|
|
|
|
/* time to calculate our checksum */
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_seqno_update(newlsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
newlsp->last_generated = time(NULL);
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
lsp_flood(newlsp, NULL);
|
2018-11-23 21:36:26 +01:00
|
|
|
area->lsp_gen_count[level - 1]++;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
refresh_time = lsp_refresh_time(newlsp, rem_lifetime);
|
|
|
|
|
|
|
|
THREAD_TIMER_OFF(area->t_lsp_refresh[level - 1]);
|
|
|
|
area->lsp_regenerate_pending[level - 1] = 0;
|
2018-10-24 06:27:17 +02:00
|
|
|
thread_add_timer(master, lsp_refresh,
|
|
|
|
&area->lsp_refresh_arg[level - 1], refresh_time,
|
|
|
|
&area->t_lsp_refresh[level - 1]);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if (isis->debugs & DEBUG_UPDATE_PACKETS) {
|
2017-07-05 18:37:36 +02:00
|
|
|
zlog_debug("ISIS-Upd (%s): Building L%d LSP %s, len %" PRIu16
|
|
|
|
", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
|
|
|
|
", lifetime %" PRIu16 "s refresh %" PRIu16 "s",
|
|
|
|
area->area_tag, level,
|
|
|
|
rawlspid_print(newlsp->hdr.lsp_id),
|
|
|
|
newlsp->hdr.pdu_len, newlsp->hdr.seqno,
|
|
|
|
newlsp->hdr.checksum, newlsp->hdr.rem_lifetime,
|
|
|
|
refresh_time);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Built L%d LSP. Set triggered regenerate to non-pending.",
|
|
|
|
area->area_tag, level);
|
|
|
|
|
2018-11-14 15:28:55 +01:00
|
|
|
#ifndef FABRICD
|
|
|
|
/* send northbound notification */
|
|
|
|
isis_notif_lsp_gen(area, rawlspid_print(newlsp->hdr.lsp_id),
|
|
|
|
newlsp->hdr.seqno, newlsp->last_generated);
|
|
|
|
#endif /* ifndef FABRICD */
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return ISIS_OK;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
* Search own LSPs, update holding time and flood
|
2003-12-23 09:09:43 +01:00
|
|
|
*/
|
2017-07-17 14:03:14 +02:00
|
|
|
static int lsp_regenerate(struct isis_area *area, int level)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
dict_t *lspdb;
|
|
|
|
struct isis_lsp *lsp, *frag;
|
|
|
|
struct listnode *node;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint8_t lspid[ISIS_SYS_ID_LEN + 2];
|
|
|
|
uint16_t rem_lifetime, refresh_time;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if ((area == NULL) || (area->is_type & level) != level)
|
|
|
|
return ISIS_ERROR;
|
|
|
|
|
|
|
|
lspdb = area->lspdb[level - 1];
|
|
|
|
|
|
|
|
memset(lspid, 0, ISIS_SYS_ID_LEN + 2);
|
|
|
|
memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
|
|
|
|
lsp = lsp_search(lspid, lspdb);
|
|
|
|
|
|
|
|
if (!lsp) {
|
2018-09-13 21:34:28 +02:00
|
|
|
flog_err(EC_LIB_DEVELOPMENT,
|
2018-09-13 21:38:57 +02:00
|
|
|
"ISIS-Upd (%s): lsp_regenerate: no L%d LSP found!",
|
|
|
|
area->area_tag, level);
|
2017-07-17 14:03:14 +02:00
|
|
|
return ISIS_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
lsp_clear_data(lsp);
|
|
|
|
lsp_build(lsp, area);
|
|
|
|
rem_lifetime = lsp_rem_lifetime(area, level);
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->hdr.rem_lifetime = rem_lifetime;
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp->last_generated = time(NULL);
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
lsp_flood(lsp, NULL);
|
2018-11-23 21:36:26 +01:00
|
|
|
area->lsp_gen_count[level - 1]++;
|
2017-07-17 14:03:14 +02:00
|
|
|
for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) {
|
2018-11-09 16:38:38 +01:00
|
|
|
if (!frag->tlvs) {
|
|
|
|
/* Updating and flooding should only affect fragments
|
|
|
|
* carrying data
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
frag->hdr.lsp_bits = lsp_bits_generate(
|
2017-07-17 14:03:14 +02:00
|
|
|
level, area->overload_bit, area->attached_bit);
|
|
|
|
/* Set the lifetime values of all the fragments to the same
|
|
|
|
* value,
|
|
|
|
* so that no fragment expires before the lsp is refreshed.
|
|
|
|
*/
|
2017-07-05 18:37:36 +02:00
|
|
|
frag->hdr.rem_lifetime = rem_lifetime;
|
2017-09-23 20:01:43 +02:00
|
|
|
frag->age_out = ZERO_AGE_LIFETIME;
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
lsp_flood(frag, NULL);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_seqno_update(lsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
refresh_time = lsp_refresh_time(lsp, rem_lifetime);
|
2018-10-24 06:27:17 +02:00
|
|
|
thread_add_timer(master, lsp_refresh,
|
|
|
|
&area->lsp_refresh_arg[level - 1], refresh_time,
|
|
|
|
&area->t_lsp_refresh[level - 1]);
|
2017-07-17 14:03:14 +02:00
|
|
|
area->lsp_regenerate_pending[level - 1] = 0;
|
|
|
|
|
|
|
|
if (isis->debugs & DEBUG_UPDATE_PACKETS) {
|
|
|
|
zlog_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS-Upd (%s): Refreshed our L%d LSP %s, len %" PRIu16
|
|
|
|
", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
|
|
|
|
", lifetime %" PRIu16 "s refresh %" PRIu16 "s",
|
|
|
|
area->area_tag, level, rawlspid_print(lsp->hdr.lsp_id),
|
|
|
|
lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum,
|
|
|
|
lsp->hdr.rem_lifetime, refresh_time);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Rebuilt L%d LSP. Set triggered regenerate to non-pending.",
|
|
|
|
area->area_tag, level);
|
|
|
|
|
|
|
|
return ISIS_OK;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-03-24 16:35:20 +01:00
|
|
|
* Something has changed or periodic refresh -> regenerate LSP
|
2003-12-23 09:09:43 +01:00
|
|
|
*/
|
2018-10-24 06:27:17 +02:00
|
|
|
static int lsp_refresh(struct thread *thread)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2018-10-24 06:27:17 +02:00
|
|
|
struct lsp_refresh_arg *arg = THREAD_ARG(thread);
|
2004-02-11 21:26:31 +01:00
|
|
|
|
2018-10-24 06:27:17 +02:00
|
|
|
assert(arg);
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2018-10-24 06:27:17 +02:00
|
|
|
struct isis_area *area = arg->area;
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
assert(area);
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2018-10-24 06:27:17 +02:00
|
|
|
int level = arg->level;
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2018-10-24 06:27:17 +02:00
|
|
|
area->t_lsp_refresh[level - 1] = NULL;
|
|
|
|
area->lsp_regenerate_pending[level - 1] = 0;
|
|
|
|
|
|
|
|
if ((area->is_type & level) == 0)
|
2017-07-17 14:03:14 +02:00
|
|
|
return ISIS_ERROR;
|
2012-03-24 16:35:20 +01:00
|
|
|
|
2018-11-09 17:12:53 +01:00
|
|
|
if (monotime_since(&area->last_lsp_refresh_event[level - 1], NULL) < 100000L) {
|
2018-10-24 07:19:22 +02:00
|
|
|
sched_debug("ISIS (%s): Still unstable, postpone LSP L%d refresh",
|
|
|
|
area->area_tag, level);
|
|
|
|
_lsp_regenerate_schedule(area, level, 0, false,
|
|
|
|
__func__, __FILE__, __LINE__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
sched_debug(
|
2018-10-24 06:27:17 +02:00
|
|
|
"ISIS (%s): LSP L%d refresh timer expired. Refreshing LSP...",
|
|
|
|
area->area_tag, level);
|
|
|
|
return lsp_regenerate(area, level);
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2018-10-24 05:38:53 +02:00
|
|
|
int _lsp_regenerate_schedule(struct isis_area *area, int level,
|
2018-10-24 07:19:22 +02:00
|
|
|
int all_pseudo, bool postpone,
|
|
|
|
const char *func, const char *file,
|
|
|
|
int line)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_lsp *lsp;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint8_t id[ISIS_SYS_ID_LEN + 2];
|
2017-07-17 14:03:14 +02:00
|
|
|
time_t now, diff;
|
|
|
|
long timeout;
|
|
|
|
struct listnode *cnode;
|
|
|
|
struct isis_circuit *circuit;
|
|
|
|
int lvl;
|
|
|
|
|
|
|
|
if (area == NULL)
|
|
|
|
return ISIS_ERROR;
|
|
|
|
|
|
|
|
sched_debug(
|
2018-10-24 05:38:53 +02:00
|
|
|
"ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs"
|
|
|
|
" Caller: %s %s:%d",
|
2017-07-17 14:03:14 +02:00
|
|
|
area->area_tag, circuit_t2string(level),
|
2018-10-24 05:38:53 +02:00
|
|
|
all_pseudo ? "" : "not ",
|
|
|
|
func, file, line);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
LSP_PSEUDO_ID(id) = LSP_FRAGMENT(id) = 0;
|
|
|
|
now = time(NULL);
|
|
|
|
|
|
|
|
for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) {
|
|
|
|
if (!((level & lvl) && (area->is_type & lvl)))
|
|
|
|
continue;
|
|
|
|
|
2018-10-24 07:19:22 +02:00
|
|
|
if (postpone) {
|
|
|
|
monotime(&area->last_lsp_refresh_event[lvl - 1]);
|
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Checking whether L%d needs to be scheduled",
|
|
|
|
area->area_tag, lvl);
|
|
|
|
|
|
|
|
if (area->lsp_regenerate_pending[lvl - 1]) {
|
|
|
|
struct timeval remain = thread_timer_remain(
|
|
|
|
area->t_lsp_refresh[lvl - 1]);
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Regeneration is already pending, nothing todo."
|
|
|
|
" (Due in %lld.%03lld seconds)",
|
|
|
|
area->area_tag, (long long)remain.tv_sec,
|
|
|
|
(long long)remain.tv_usec / 1000);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
lsp = lsp_search(id, area->lspdb[lvl - 1]);
|
|
|
|
if (!lsp) {
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): We do not have any LSPs to regenerate, nothing todo.",
|
|
|
|
area->area_tag);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Throttle avoidance
|
|
|
|
*/
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Will schedule regen timer. Last run was: %lld, Now is: %lld",
|
|
|
|
area->area_tag, (long long)lsp->last_generated,
|
|
|
|
(long long)now);
|
|
|
|
THREAD_TIMER_OFF(area->t_lsp_refresh[lvl - 1]);
|
|
|
|
diff = now - lsp->last_generated;
|
|
|
|
if (diff < area->lsp_gen_interval[lvl - 1]) {
|
|
|
|
timeout =
|
|
|
|
1000 * (area->lsp_gen_interval[lvl - 1] - diff);
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Scheduling in %ld ms to match configured lsp_gen_interval",
|
|
|
|
area->area_tag, timeout);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* lsps are not regenerated if lsp_regenerate function
|
|
|
|
* is called
|
|
|
|
* directly. However if the lsp_regenerate call is
|
|
|
|
* queued for
|
|
|
|
* later execution it works.
|
|
|
|
*/
|
|
|
|
timeout = 100;
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Last generation was more than lsp_gen_interval ago."
|
|
|
|
" Scheduling for execution in %ld ms.",
|
|
|
|
area->area_tag, timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
area->lsp_regenerate_pending[lvl - 1] = 1;
|
2018-10-24 06:27:17 +02:00
|
|
|
thread_add_timer_msec(master, lsp_refresh,
|
|
|
|
&area->lsp_refresh_arg[lvl - 1],
|
|
|
|
timeout,
|
|
|
|
&area->t_lsp_refresh[lvl - 1]);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (all_pseudo) {
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit))
|
|
|
|
lsp_regenerate_schedule_pseudo(circuit, level);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ISIS_OK;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Funcs for pseudonode LSPs
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2017-07-17 14:03:14 +02:00
|
|
|
* 7.3.8 and 7.3.10 Generation of level 1 and 2 pseudonode LSPs
|
2003-12-23 09:09:43 +01:00
|
|
|
*/
|
2017-07-17 14:03:14 +02:00
|
|
|
static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
|
|
|
|
int level)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_adjacency *adj;
|
|
|
|
struct list *adj_list;
|
|
|
|
struct listnode *node;
|
|
|
|
struct isis_area *area = circuit->area;
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_clear_data(lsp);
|
|
|
|
lsp->tlvs = isis_alloc_tlvs();
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Constructing pseudo LSP %s for interface %s level %d",
|
2017-07-05 18:37:36 +02:00
|
|
|
area->area_tag, rawlspid_print(lsp->hdr.lsp_id),
|
2017-07-17 14:03:14 +02:00
|
|
|
circuit->interface->name, level);
|
|
|
|
|
|
|
|
lsp->level = level;
|
|
|
|
/* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->hdr.lsp_bits =
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_bits_generate(level, 0, circuit->area->attached_bit);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add self to IS neighbours
|
|
|
|
*/
|
2017-07-05 18:37:36 +02:00
|
|
|
uint8_t ne_id[ISIS_SYS_ID_LEN + 1];
|
|
|
|
|
|
|
|
memcpy(ne_id, isis->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
LSP_PSEUDO_ID(ne_id) = 0;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
if (circuit->area->oldmetric) {
|
|
|
|
isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0);
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Adding %s.%02x as old-style neighbor (self)",
|
2017-07-05 18:37:36 +02:00
|
|
|
area->area_tag, sysid_print(ne_id),
|
|
|
|
LSP_PSEUDO_ID(ne_id));
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
if (circuit->area->newmetric) {
|
2017-07-05 18:37:36 +02:00
|
|
|
isis_tlvs_add_extended_reach(lsp->tlvs, ISIS_MT_IPV4_UNICAST,
|
|
|
|
ne_id, 0, NULL, 0);
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Adding %s.%02x as te-style neighbor (self)",
|
2017-07-05 18:37:36 +02:00
|
|
|
area->area_tag, sysid_print(ne_id),
|
|
|
|
LSP_PSEUDO_ID(ne_id));
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
adj_list = list_new();
|
|
|
|
isis_adj_build_up_list(circuit->u.bc.adjdb[level - 1], adj_list);
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) {
|
2017-07-05 18:37:36 +02:00
|
|
|
if (!(adj->level & level)) {
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Ignoring neighbor %s, level does not intersect",
|
|
|
|
area->area_tag, sysid_print(adj->sysid));
|
2017-07-05 18:37:36 +02:00
|
|
|
continue;
|
2004-09-10 22:48:21 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
if (!(level == IS_LEVEL_1
|
|
|
|
&& adj->sys_type == ISIS_SYSTYPE_L1_IS)
|
|
|
|
&& !(level == IS_LEVEL_1
|
|
|
|
&& adj->sys_type == ISIS_SYSTYPE_L2_IS
|
|
|
|
&& adj->adj_usage == ISIS_ADJ_LEVEL1AND2)
|
|
|
|
&& !(level == IS_LEVEL_2
|
|
|
|
&& adj->sys_type == ISIS_SYSTYPE_L2_IS)) {
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Ignoring neighbor %s, level does not match",
|
|
|
|
area->area_tag, sysid_print(adj->sysid));
|
|
|
|
continue;
|
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
memcpy(ne_id, adj->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
if (circuit->area->oldmetric) {
|
|
|
|
isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0);
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Adding %s.%02x as old-style neighbor (peer)",
|
|
|
|
area->area_tag, sysid_print(ne_id),
|
|
|
|
LSP_PSEUDO_ID(ne_id));
|
|
|
|
}
|
|
|
|
if (circuit->area->newmetric) {
|
|
|
|
isis_tlvs_add_extended_reach(lsp->tlvs,
|
|
|
|
ISIS_MT_IPV4_UNICAST,
|
|
|
|
ne_id, 0, NULL, 0);
|
|
|
|
lsp_debug(
|
|
|
|
"ISIS (%s): Adding %s.%02x as te-style neighbor (peer)",
|
|
|
|
area->area_tag, sysid_print(ne_id),
|
|
|
|
LSP_PSEUDO_ID(ne_id));
|
|
|
|
}
|
|
|
|
}
|
2018-10-02 11:39:51 +02:00
|
|
|
list_delete(&adj_list);
|
2017-07-17 14:03:14 +02:00
|
|
|
return;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
int lsp_generate_pseudo(struct isis_circuit *circuit, int level)
|
2012-03-24 16:35:20 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
dict_t *lspdb = circuit->area->lspdb[level - 1];
|
|
|
|
struct isis_lsp *lsp;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
|
|
|
|
uint16_t rem_lifetime, refresh_time;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if ((circuit->is_type & level) != level
|
|
|
|
|| (circuit->state != C_STATE_UP)
|
|
|
|
|| (circuit->circ_type != CIRCUIT_T_BROADCAST)
|
|
|
|
|| (circuit->u.bc.is_dr[level - 1] == 0))
|
|
|
|
return ISIS_ERROR;
|
|
|
|
|
|
|
|
memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
LSP_FRAGMENT(lsp_id) = 0;
|
|
|
|
LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If for some reason have a pseudo LSP in the db already -> regenerate
|
|
|
|
*/
|
|
|
|
if (lsp_search(lsp_id, lspdb))
|
|
|
|
return lsp_regenerate_schedule_pseudo(circuit, level);
|
|
|
|
|
|
|
|
rem_lifetime = lsp_rem_lifetime(circuit->area, level);
|
|
|
|
/* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */
|
|
|
|
lsp = lsp_new(circuit->area, lsp_id, rem_lifetime, 1,
|
|
|
|
circuit->area->is_type | circuit->area->attached_bit, 0,
|
2017-08-11 15:28:58 +02:00
|
|
|
NULL, level);
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp->area = circuit->area;
|
|
|
|
|
|
|
|
lsp_build_pseudo(lsp, circuit, level);
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_pack_pdu(lsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp->own_lsp = 1;
|
|
|
|
lsp_insert(lsp, lspdb);
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
lsp_flood(lsp, NULL);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
refresh_time = lsp_refresh_time(lsp, rem_lifetime);
|
|
|
|
THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
|
|
|
|
circuit->lsp_regenerate_pending[level - 1] = 0;
|
|
|
|
if (level == IS_LEVEL_1)
|
|
|
|
thread_add_timer(
|
|
|
|
master, lsp_l1_refresh_pseudo, circuit, refresh_time,
|
|
|
|
&circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
|
|
|
|
else if (level == IS_LEVEL_2)
|
|
|
|
thread_add_timer(
|
|
|
|
master, lsp_l2_refresh_pseudo, circuit, refresh_time,
|
|
|
|
&circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
|
|
|
|
|
|
|
|
if (isis->debugs & DEBUG_UPDATE_PACKETS) {
|
|
|
|
zlog_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS-Upd (%s): Built L%d Pseudo LSP %s, len %" PRIu16
|
|
|
|
", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
|
|
|
|
", lifetime %" PRIu16 "s, refresh %" PRIu16 "s",
|
2017-07-17 14:03:14 +02:00
|
|
|
circuit->area->area_tag, level,
|
2017-07-05 18:37:36 +02:00
|
|
|
rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
|
|
|
|
lsp->hdr.seqno, lsp->hdr.checksum,
|
|
|
|
lsp->hdr.rem_lifetime, refresh_time);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ISIS_OK;
|
2012-03-24 16:35:20 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
dict_t *lspdb = circuit->area->lspdb[level - 1];
|
|
|
|
struct isis_lsp *lsp;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
|
|
|
|
uint16_t rem_lifetime, refresh_time;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
if ((circuit->is_type & level) != level
|
|
|
|
|| (circuit->state != C_STATE_UP)
|
|
|
|
|| (circuit->circ_type != CIRCUIT_T_BROADCAST)
|
|
|
|
|| (circuit->u.bc.is_dr[level - 1] == 0))
|
|
|
|
return ISIS_ERROR;
|
|
|
|
|
|
|
|
memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id;
|
|
|
|
LSP_FRAGMENT(lsp_id) = 0;
|
|
|
|
|
|
|
|
lsp = lsp_search(lsp_id, lspdb);
|
|
|
|
|
|
|
|
if (!lsp) {
|
2018-09-13 21:34:28 +02:00
|
|
|
flog_err(EC_LIB_DEVELOPMENT,
|
2018-09-13 21:38:57 +02:00
|
|
|
"lsp_regenerate_pseudo: no l%d LSP %s found!", level,
|
|
|
|
rawlspid_print(lsp_id));
|
2017-07-17 14:03:14 +02:00
|
|
|
return ISIS_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
rem_lifetime = lsp_rem_lifetime(circuit->area, level);
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->hdr.rem_lifetime = rem_lifetime;
|
|
|
|
lsp_build_pseudo(lsp, circuit, level);
|
|
|
|
lsp_inc_seqno(lsp, 0);
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp->last_generated = time(NULL);
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
lsp_flood(lsp, NULL);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
refresh_time = lsp_refresh_time(lsp, rem_lifetime);
|
|
|
|
if (level == IS_LEVEL_1)
|
|
|
|
thread_add_timer(
|
|
|
|
master, lsp_l1_refresh_pseudo, circuit, refresh_time,
|
|
|
|
&circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
|
|
|
|
else if (level == IS_LEVEL_2)
|
|
|
|
thread_add_timer(
|
|
|
|
master, lsp_l2_refresh_pseudo, circuit, refresh_time,
|
|
|
|
&circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
|
|
|
|
|
|
|
|
if (isis->debugs & DEBUG_UPDATE_PACKETS) {
|
|
|
|
zlog_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS-Upd (%s): Refreshed L%d Pseudo LSP %s, len %" PRIu16
|
|
|
|
", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
|
|
|
|
", lifetime %" PRIu16 "s, refresh %" PRIu16 "s",
|
2017-07-17 14:03:14 +02:00
|
|
|
circuit->area->area_tag, level,
|
2017-07-05 18:37:36 +02:00
|
|
|
rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
|
|
|
|
lsp->hdr.seqno, lsp->hdr.checksum,
|
|
|
|
lsp->hdr.rem_lifetime, refresh_time);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ISIS_OK;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2012-03-24 16:35:20 +01:00
|
|
|
/*
|
|
|
|
* Something has changed or periodic refresh -> regenerate pseudo LSP
|
|
|
|
*/
|
2017-07-17 14:03:14 +02:00
|
|
|
static int lsp_l1_refresh_pseudo(struct thread *thread)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_circuit *circuit;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint8_t id[ISIS_SYS_ID_LEN + 2];
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
circuit = THREAD_ARG(thread);
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
circuit->u.bc.t_refresh_pseudo_lsp[0] = NULL;
|
|
|
|
circuit->lsp_regenerate_pending[0] = 0;
|
2004-09-10 23:19:13 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if ((circuit->u.bc.is_dr[0] == 0)
|
|
|
|
|| (circuit->is_type & IS_LEVEL_1) == 0) {
|
|
|
|
memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
LSP_PSEUDO_ID(id) = circuit->circuit_id;
|
|
|
|
LSP_FRAGMENT(id) = 0;
|
|
|
|
lsp_purge_pseudo(id, circuit, IS_LEVEL_1);
|
|
|
|
return ISIS_ERROR;
|
|
|
|
}
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return lsp_regenerate_pseudo(circuit, IS_LEVEL_1);
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
static int lsp_l2_refresh_pseudo(struct thread *thread)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_circuit *circuit;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint8_t id[ISIS_SYS_ID_LEN + 2];
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
circuit = THREAD_ARG(thread);
|
2004-09-10 22:48:21 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
circuit->u.bc.t_refresh_pseudo_lsp[1] = NULL;
|
|
|
|
circuit->lsp_regenerate_pending[1] = 0;
|
2004-09-10 23:19:13 +02:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
if ((circuit->u.bc.is_dr[1] == 0)
|
|
|
|
|| (circuit->is_type & IS_LEVEL_2) == 0) {
|
|
|
|
memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
LSP_PSEUDO_ID(id) = circuit->circuit_id;
|
|
|
|
LSP_FRAGMENT(id) = 0;
|
|
|
|
lsp_purge_pseudo(id, circuit, IS_LEVEL_2);
|
|
|
|
return ISIS_ERROR;
|
|
|
|
}
|
2003-12-23 09:09:43 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
return lsp_regenerate_pseudo(circuit, IS_LEVEL_2);
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_lsp *lsp;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
|
2017-07-17 14:03:14 +02:00
|
|
|
time_t now, diff;
|
|
|
|
long timeout;
|
|
|
|
int lvl;
|
|
|
|
struct isis_area *area = circuit->area;
|
|
|
|
|
|
|
|
if (circuit->circ_type != CIRCUIT_T_BROADCAST
|
|
|
|
|| circuit->state != C_STATE_UP)
|
|
|
|
return ISIS_OK;
|
|
|
|
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Scheduling regeneration of %s pseudo LSP for interface %s",
|
|
|
|
area->area_tag, circuit_t2string(level),
|
|
|
|
circuit->interface->name);
|
|
|
|
|
|
|
|
memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
|
|
|
|
LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id;
|
|
|
|
LSP_FRAGMENT(lsp_id) = 0;
|
|
|
|
now = time(NULL);
|
|
|
|
|
|
|
|
for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) {
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Checking whether L%d pseudo LSP needs to be scheduled",
|
|
|
|
area->area_tag, lvl);
|
|
|
|
|
|
|
|
if (!((level & lvl) && (circuit->is_type & lvl))) {
|
|
|
|
sched_debug("ISIS (%s): Level is not active on circuit",
|
|
|
|
area->area_tag);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (circuit->u.bc.is_dr[lvl - 1] == 0) {
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): This IS is not DR, nothing to do.",
|
|
|
|
area->area_tag);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (circuit->lsp_regenerate_pending[lvl - 1]) {
|
|
|
|
struct timeval remain = thread_timer_remain(
|
|
|
|
circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]);
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Regenerate is already pending, nothing todo."
|
|
|
|
" (Due in %lld.%03lld seconds)",
|
|
|
|
area->area_tag, (long long)remain.tv_sec,
|
|
|
|
(long long)remain.tv_usec / 1000);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
lsp = lsp_search(lsp_id, circuit->area->lspdb[lvl - 1]);
|
|
|
|
if (!lsp) {
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Pseudonode LSP does not exist yet, nothing to regenerate.",
|
|
|
|
area->area_tag);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Throttle avoidance
|
|
|
|
*/
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Will schedule PSN regen timer. Last run was: %lld, Now is: %lld",
|
|
|
|
area->area_tag, (long long)lsp->last_generated,
|
|
|
|
(long long)now);
|
|
|
|
THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]);
|
|
|
|
diff = now - lsp->last_generated;
|
|
|
|
if (diff < circuit->area->lsp_gen_interval[lvl - 1]) {
|
2017-07-22 14:52:33 +02:00
|
|
|
timeout =
|
|
|
|
1000 * (circuit->area->lsp_gen_interval[lvl - 1]
|
|
|
|
- diff);
|
2017-07-17 14:03:14 +02:00
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Sechduling in %ld ms to match configured lsp_gen_interval",
|
|
|
|
area->area_tag, timeout);
|
|
|
|
} else {
|
|
|
|
timeout = 100;
|
|
|
|
sched_debug(
|
|
|
|
"ISIS (%s): Last generation was more than lsp_gen_interval ago."
|
|
|
|
" Scheduling for execution in %ld ms.",
|
|
|
|
area->area_tag, timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
circuit->lsp_regenerate_pending[lvl - 1] = 1;
|
|
|
|
|
|
|
|
if (lvl == IS_LEVEL_1) {
|
|
|
|
thread_add_timer_msec(
|
|
|
|
master, lsp_l1_refresh_pseudo, circuit, timeout,
|
|
|
|
&circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]);
|
|
|
|
} else if (lvl == IS_LEVEL_2) {
|
|
|
|
thread_add_timer_msec(
|
|
|
|
master, lsp_l2_refresh_pseudo, circuit, timeout,
|
|
|
|
&circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ISIS_OK;
|
2004-09-10 22:48:21 +02:00
|
|
|
}
|
2003-12-23 09:09:43 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Walk through LSPs for an area
|
|
|
|
* - set remaining lifetime
|
|
|
|
*/
|
2017-07-17 14:03:14 +02:00
|
|
|
int lsp_tick(struct thread *thread)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_area *area;
|
|
|
|
struct isis_lsp *lsp;
|
|
|
|
dnode_t *dnode, *dnode_next;
|
|
|
|
int level;
|
2018-03-27 21:13:34 +02:00
|
|
|
uint16_t rem_lifetime;
|
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
|
|
|
bool fabricd_sync_incomplete = false;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
area = THREAD_ARG(thread);
|
|
|
|
assert(area);
|
|
|
|
area->t_tick = NULL;
|
|
|
|
thread_add_timer(master, lsp_tick, area, 1, &area->t_tick);
|
|
|
|
|
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
|
|
|
struct isis_circuit *fabricd_init_c = fabricd_initial_sync_circuit(area);
|
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
/*
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
* Remove LSPs which have aged out
|
2017-07-17 14:03:14 +02:00
|
|
|
*/
|
|
|
|
for (level = 0; level < ISIS_LEVELS; level++) {
|
|
|
|
if (area->lspdb[level] && dict_count(area->lspdb[level]) > 0) {
|
|
|
|
for (dnode = dict_first(area->lspdb[level]);
|
|
|
|
dnode != NULL; dnode = dnode_next) {
|
|
|
|
dnode_next =
|
|
|
|
dict_next(area->lspdb[level], dnode);
|
|
|
|
lsp = dnode_get(dnode);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The lsp rem_lifetime is kept at 0 for MaxAge
|
|
|
|
* or
|
|
|
|
* ZeroAgeLifetime depending on explicit purge
|
|
|
|
* or
|
|
|
|
* natural age out. So schedule spf only once
|
|
|
|
* when
|
|
|
|
* the first time rem_lifetime becomes 0.
|
|
|
|
*/
|
2017-07-05 18:37:36 +02:00
|
|
|
rem_lifetime = lsp->hdr.rem_lifetime;
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_set_time(lsp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Schedule may run spf which should be done
|
|
|
|
* only after
|
|
|
|
* the lsp rem_lifetime becomes 0 for the first
|
|
|
|
* time.
|
|
|
|
* ISO 10589 - 7.3.16.4 first paragraph.
|
|
|
|
*/
|
2017-07-05 18:37:36 +02:00
|
|
|
if (rem_lifetime == 1 && lsp->hdr.seqno != 0) {
|
2017-07-17 14:03:14 +02:00
|
|
|
/* 7.3.16.4 a) set SRM flags on all */
|
2018-05-31 15:14:26 +02:00
|
|
|
/* 7.3.16.4 b) retain only the header */
|
|
|
|
if (lsp->area->purge_originator)
|
|
|
|
lsp_purge(lsp, lsp->level, NULL);
|
|
|
|
else
|
|
|
|
lsp_flood(lsp, NULL);
|
2017-07-17 14:03:14 +02:00
|
|
|
/* 7.3.16.4 c) record the time to purge
|
|
|
|
* FIXME */
|
2018-05-31 15:14:26 +02:00
|
|
|
isis_spf_schedule(lsp->area, lsp->level);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (lsp->age_out == 0) {
|
|
|
|
zlog_debug(
|
2017-07-05 18:37:36 +02:00
|
|
|
"ISIS-Upd (%s): L%u LSP %s seq "
|
|
|
|
"0x%08" PRIx32 " aged out",
|
2017-07-17 14:03:14 +02:00
|
|
|
area->area_tag, lsp->level,
|
2017-07-05 18:37:36 +02:00
|
|
|
rawlspid_print(lsp->hdr.lsp_id),
|
|
|
|
lsp->hdr.seqno);
|
2017-07-17 14:03:14 +02:00
|
|
|
lsp_destroy(lsp);
|
|
|
|
lsp = NULL;
|
|
|
|
dict_delete_free(area->lspdb[level],
|
|
|
|
dnode);
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
}
|
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
|
|
|
|
2018-09-20 15:39:00 +02:00
|
|
|
if (fabricd_init_c && lsp) {
|
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
|
|
|
fabricd_sync_incomplete |=
|
|
|
|
ISIS_CHECK_FLAG(lsp->SSNflags,
|
|
|
|
fabricd_init_c);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
if (fabricd_init_c
|
|
|
|
&& !fabricd_sync_incomplete
|
|
|
|
&& !isis_tx_queue_len(fabricd_init_c->tx_queue)) {
|
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
|
|
|
fabricd_initial_sync_finish(area);
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
}
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
return ISIS_OK;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
2018-03-27 21:13:34 +02:00
|
|
|
void lsp_purge_pseudo(uint8_t *id, struct isis_circuit *circuit, int level)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_lsp *lsp;
|
|
|
|
|
|
|
|
lsp = lsp_search(id, circuit->area->lspdb[level - 1]);
|
|
|
|
if (!lsp)
|
|
|
|
return;
|
|
|
|
|
2018-05-31 15:14:26 +02:00
|
|
|
lsp_purge(lsp, level, NULL);
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-07-17 14:03:14 +02:00
|
|
|
* Purge own LSP that is received and we don't have.
|
2003-12-23 09:09:43 +01:00
|
|
|
* -> Do as in 7.3.16.4
|
|
|
|
*/
|
2017-07-05 18:37:36 +02:00
|
|
|
void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr,
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_area *area)
|
2003-12-23 09:09:43 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct isis_lsp *lsp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to create the LSP to be purged
|
|
|
|
*/
|
|
|
|
lsp = XCALLOC(MTYPE_ISIS_LSP, sizeof(struct isis_lsp));
|
|
|
|
lsp->area = area;
|
|
|
|
lsp->level = level;
|
2018-10-16 19:17:10 +02:00
|
|
|
lsp_adjust_stream(lsp);
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp->age_out = ZERO_AGE_LIFETIME;
|
2018-11-24 00:36:37 +01:00
|
|
|
lsp->area->lsp_purge_count[level - 1]++;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
memcpy(&lsp->hdr, hdr, sizeof(lsp->hdr));
|
|
|
|
lsp->hdr.rem_lifetime = 0;
|
2017-07-17 14:03:14 +02:00
|
|
|
|
2018-05-31 15:14:26 +02:00
|
|
|
lsp_purge_add_poi(lsp, NULL);
|
|
|
|
|
2017-07-05 18:37:36 +02:00
|
|
|
lsp_pack_pdu(lsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
lsp_insert(lsp, area->lspdb[lsp->level - 1]);
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
lsp_flood(lsp, NULL);
|
2017-07-17 14:03:14 +02:00
|
|
|
|
|
|
|
return;
|
2003-12-23 09:09:43 +01:00
|
|
|
}
|
|
|
|
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set)
|
2012-03-24 16:35:20 +01:00
|
|
|
{
|
2017-07-17 14:03:14 +02:00
|
|
|
struct listnode *node;
|
|
|
|
struct isis_circuit *circuit;
|
2012-03-24 16:35:20 +01:00
|
|
|
|
2017-07-17 14:03:14 +02:00
|
|
|
assert(lsp);
|
2012-03-24 16:35:20 +01:00
|
|
|
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
if (!lsp->area)
|
|
|
|
return;
|
2012-03-24 16:35:20 +01:00
|
|
|
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
struct list *circuit_list = lsp->area->circuit_list;
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(circuit_list, node, circuit)) {
|
|
|
|
if (set) {
|
|
|
|
isis_tx_queue_add(circuit->tx_queue, lsp,
|
|
|
|
TX_LSP_NORMAL);
|
|
|
|
} else {
|
|
|
|
isis_tx_queue_del(circuit->tx_queue, lsp);
|
2017-07-17 14:03:14 +02:00
|
|
|
}
|
|
|
|
}
|
2012-03-24 16:35:20 +01:00
|
|
|
}
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
|
2018-11-16 16:31:37 +01:00
|
|
|
void _lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit,
|
|
|
|
const char *func, const char *file, int line)
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
{
|
2018-11-16 16:31:37 +01:00
|
|
|
if (isis->debugs & DEBUG_FLOODING) {
|
|
|
|
zlog_debug("Flooding LSP %s%s%s (From %s %s:%d)",
|
|
|
|
rawlspid_print(lsp->hdr.lsp_id),
|
|
|
|
circuit ? " except on " : "",
|
|
|
|
circuit ? circuit->interface->name : "",
|
|
|
|
func, file, line);
|
|
|
|
}
|
|
|
|
|
2018-11-09 16:40:17 +01:00
|
|
|
if (!fabricd)
|
2018-05-10 17:40:04 +02:00
|
|
|
lsp_set_all_srmflags(lsp, true);
|
2018-11-09 16:40:17 +01:00
|
|
|
else
|
2018-11-23 03:13:56 +01:00
|
|
|
fabricd_lsp_flood(lsp, circuit);
|
2018-11-09 16:40:17 +01:00
|
|
|
|
|
|
|
if (circuit)
|
|
|
|
isis_tx_queue_del(circuit->tx_queue, lsp);
|
fabricd: reimplement LSP transmission logic
Before this commit, isisd/fabricd maintained a bitfield for each LSP
to track the SRM bit for each circuit, which specifies whether an LSP
needs to be sent on that circuit. Every second, it would scan over all
LSPs in `lsp_tick` and queue them up for transmission accordingly.
This design has two drawbacks: a) it scales poorly b) it adds
unacceptable latency to the update process: each router takes a random
amount of time between 0 and 1 seconds to forward an update. In a
network with a diamter of 10, it might already take 10 seconds for an
update to traverse the network.
To mitigate this, a new design was chosen. Instead of tracking SRM in a
bitfield, have one tx_queue per circuit and declare that an LSP is in
that queue if and only if it would have SRM set for that circuit.
This way, we can track SRM similarly as we did before, however, on
insertion into the LSP queue, we can add a timer for (re)transmission,
alleviating the need for a periodic scan with LSP tick and reducing the
latency for forwarding of updates.
Signed-off-by: Christian Franke <chris@opensourcerouting.org>
2018-05-10 17:37:05 +02:00
|
|
|
}
|
2018-09-27 14:23:06 +02:00
|
|
|
|
|
|
|
static int lsp_handle_adj_state_change(struct isis_adjacency *adj)
|
|
|
|
{
|
|
|
|
lsp_regenerate_schedule(adj->circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lsp_init(void)
|
|
|
|
{
|
|
|
|
hook_register(isis_adj_state_change_hook,
|
|
|
|
lsp_handle_adj_state_change);
|
|
|
|
}
|