#include "isisd/isis_lsp.h"
#include "isisd/isis_spf_private.h"
#include "isisd/isis_tx_queue.h"
+#include "isisd/isis_csm.h"
DEFINE_MTYPE_STATIC(ISISD, FABRICD_STATE, "ISIS OpenFabric")
DEFINE_MTYPE_STATIC(ISISD, FABRICD_NEIGHBOR, "ISIS OpenFabric Neighbor Entry")
+DEFINE_MTYPE_STATIC(ISISD, FABRICD_FLOODING_INFO, "ISIS OpenFabric Flooding Log")
/* Tracks initial synchronization as per section 2.4
*
uint8_t tier_pending;
struct thread *tier_calculation_timer;
struct thread *tier_set_timer;
+
+ int csnp_delay;
+ bool always_send_csnp;
};
/* Code related to maintaining the neighbor lists */
struct neighbor_entry {
- struct isis_vertex *vertex;
+ uint8_t id[ISIS_SYS_ID_LEN];
+ struct isis_adjacency *adj;
bool present;
};
-static struct neighbor_entry *neighbor_entry_new(struct isis_vertex *vertex)
+static struct neighbor_entry *neighbor_entry_new(const uint8_t *id,
+ struct isis_adjacency *adj)
{
- struct neighbor_entry *rv = XMALLOC(MTYPE_FABRICD_NEIGHBOR, sizeof(*rv));
+ struct neighbor_entry *rv = XMALLOC(MTYPE_FABRICD_NEIGHBOR,
+ sizeof(*rv));
+
+ memcpy(rv->id, id, sizeof(rv->id));
+ rv->adj = adj;
- rv->vertex = vertex;
return rv;
}
hash_clean(f->neighbors_neighbors, neighbor_entry_del_void);
}
-static unsigned neighbor_entry_hash_key(void *np)
+static unsigned neighbor_entry_hash_key(const void *np)
{
- struct neighbor_entry *n = np;
+ const struct neighbor_entry *n = np;
- return jhash(n->vertex->N.id, ISIS_SYS_ID_LEN, 0x55aa5a5a);
+ return jhash(n->id, sizeof(n->id), 0x55aa5a5a);
}
static bool neighbor_entry_hash_cmp(const void *a, const void *b)
{
const struct neighbor_entry *na = a, *nb = b;
- return memcmp(na->vertex->N.id, nb->vertex->N.id, ISIS_SYS_ID_LEN) == 0;
+ return memcmp(na->id, nb->id, sizeof(na->id)) == 0;
}
static int neighbor_entry_list_cmp(void *a, void *b)
{
struct neighbor_entry *na = a, *nb = b;
- return -memcmp(na->vertex->N.id, nb->vertex->N.id, ISIS_SYS_ID_LEN);
+ return -memcmp(na->id, nb->id, sizeof(na->id));
}
static struct neighbor_entry *neighbor_entry_lookup_list(struct skiplist *list,
const uint8_t *id)
{
- struct isis_vertex querier;
- isis_vertex_id_init(&querier, id, VTYPE_NONPSEUDO_TE_IS);
+ struct neighbor_entry n = { {0} };
- struct neighbor_entry n = {
- .vertex = &querier
- };
+ memcpy(n.id, id, sizeof(n.id));
struct neighbor_entry *rv;
static struct neighbor_entry *neighbor_entry_lookup_hash(struct hash *hash,
const uint8_t *id)
{
- struct isis_vertex querier;
- isis_vertex_id_init(&querier, id, VTYPE_NONPSEUDO_TE_IS);
+ struct neighbor_entry n = {{0}};
- struct neighbor_entry n = {
- .vertex = &querier
- };
+ memcpy(n.id, id, sizeof(n.id));
struct neighbor_entry *rv = hash_lookup(hash, &n);
return rv;
}
-static void neighbor_lists_update(struct fabricd *f)
+static int fabricd_handle_adj_state_change(struct isis_adjacency *arg)
{
- neighbor_lists_clear(f);
+ struct fabricd *f = arg->circuit->area->fabricd;
+
+ if (!f)
+ return 0;
+
+ while (!skiplist_empty(f->neighbors))
+ skiplist_delete_first(f->neighbors);
+
+ struct listnode *node;
+ struct isis_circuit *circuit;
+
+ for (ALL_LIST_ELEMENTS_RO(f->area->circuit_list, node, circuit)) {
+ if (circuit->state != C_STATE_UP)
+ continue;
+
+ struct isis_adjacency *adj = circuit->u.p2p.neighbor;
+
+ if (!adj || adj->adj_state != ISIS_ADJ_UP)
+ continue;
+
+ struct neighbor_entry *n = neighbor_entry_new(adj->sysid, adj);
+
+ skiplist_insert(f->neighbors, n, n);
+ }
+
+ return 0;
+}
+
+static void neighbors_neighbors_update(struct fabricd *f)
+{
+ hash_clean(f->neighbors_neighbors, neighbor_entry_del_void);
struct listnode *node;
struct isis_vertex *v;
for (ALL_QUEUE_ELEMENTS_RO(&f->spftree->paths, node, v)) {
- if (!v->d_N || !VTYPE_IS(v->type))
+ if (v->d_N < 2 || !VTYPE_IS(v->type))
continue;
if (v->d_N > 2)
break;
- struct neighbor_entry *n = neighbor_entry_new(v);
- if (v->d_N == 1) {
- skiplist_insert(f->neighbors, n, n);
- } else {
- struct neighbor_entry *inserted;
- inserted = hash_get(f->neighbors_neighbors, n, hash_alloc_intern);
- assert(inserted == n);
- }
+ struct neighbor_entry *n = neighbor_entry_new(v->N.id, NULL);
+ struct neighbor_entry *inserted;
+ inserted = hash_get(f->neighbors_neighbors, n,
+ hash_alloc_intern);
+ assert(inserted == n);
}
}
"Fabricd Neighbors");
rv->tier = rv->tier_config = ISIS_TIER_UNDEFINED;
+
+ rv->csnp_delay = FABRICD_DEFAULT_CSNP_DELAY;
return rv;
};
return;
isis_run_hopcount_spf(area, isis->sysid, f->spftree);
- neighbor_lists_update(f);
+ neighbors_neighbors_update(f);
fabricd_bump_tier_calculation_timer(f);
}
written++;
}
+ if (f->csnp_delay != FABRICD_DEFAULT_CSNP_DELAY
+ || f->always_send_csnp) {
+ vty_out(vty, " triggered-csnp-delay %d%s\n", f->csnp_delay,
+ f->always_send_csnp ? " always" : "");
+ }
+
return written;
}
-static void move_to_dnr(struct isis_lsp *lsp, struct neighbor_entry *n)
+static void move_to_queue(struct isis_lsp *lsp, struct neighbor_entry *n,
+ enum isis_tx_type type, struct isis_circuit *circuit)
{
- struct isis_adjacency *adj = listnode_head(n->vertex->Adj_N);
-
n->present = false;
- if (isis->debugs & DEBUG_FABRICD_FLOODING) {
- char buff[PREFIX2STR_BUFFER];
- zlog_debug("OpenFabric: Adding %s to DNR",
- vid2string(n->vertex, buff, sizeof(buff)));
- }
+ if (n->adj && n->adj->circuit == circuit)
+ return;
- if (adj) {
- isis_tx_queue_add(adj->circuit->tx_queue, lsp,
- TX_LSP_CIRCUIT_SCOPED);
+ if (isis->debugs & DEBUG_FLOODING) {
+ zlog_debug("OpenFabric: Adding %s to %s",
+ print_sys_hostname(n->id),
+ (type == TX_LSP_NORMAL) ? "RF" : "DNR");
}
-}
-static void move_to_rf(struct isis_lsp *lsp, struct neighbor_entry *n)
-{
- struct isis_adjacency *adj = listnode_head(n->vertex->Adj_N);
+ if (n->adj)
+ isis_tx_queue_add(n->adj->circuit->tx_queue, lsp, type);
- n->present = false;
-
- if (isis->debugs & DEBUG_FABRICD_FLOODING) {
- char buff[PREFIX2STR_BUFFER];
- zlog_debug("OpenFabric: Adding %s to RF",
- vid2string(n->vertex, buff, sizeof(buff)));
- }
+ uint8_t *neighbor_id = XMALLOC(MTYPE_FABRICD_FLOODING_INFO,
+ sizeof(n->id));
- if (adj) {
- isis_tx_queue_add(adj->circuit->tx_queue, lsp,
- TX_LSP_NORMAL);
- }
+ memcpy(neighbor_id, n->id, sizeof(n->id));
+ listnode_add(lsp->flooding_neighbors[type], neighbor_id);
}
-static void mark_neighbor_as_present(struct hash_backet *backet, void *arg)
+static void mark_neighbor_as_present(struct hash_bucket *bucket, void *arg)
{
- struct neighbor_entry *n = backet->data;
+ struct neighbor_entry *n = bucket->data;
n->present = true;
}
-static void handle_firsthops(struct hash_backet *backet, void *arg)
+static void handle_firsthops(struct hash_bucket *bucket, void *arg)
{
struct isis_lsp *lsp = arg;
struct fabricd *f = lsp->area->fabricd;
- struct isis_vertex *vertex = backet->data;
+ struct isis_vertex *vertex = bucket->data;
struct neighbor_entry *n;
n = neighbor_entry_lookup_list(f->neighbors, vertex->N.id);
if (n) {
- if (isis->debugs & DEBUG_FABRICD_FLOODING) {
- char buff[PREFIX2STR_BUFFER];
+ if (isis->debugs & DEBUG_FLOODING) {
zlog_debug("Removing %s from NL as its in the reverse path",
- vid2string(vertex, buff, sizeof(buff)));
+ print_sys_hostname(n->id));
}
n->present = false;
}
n = neighbor_entry_lookup_hash(f->neighbors_neighbors, vertex->N.id);
if (n) {
- if (isis->debugs & DEBUG_FABRICD_FLOODING) {
- char buff[PREFIX2STR_BUFFER];
+ if (isis->debugs & DEBUG_FLOODING) {
zlog_debug("Removing %s from NN as its in the reverse path",
- vid2string(vertex, buff, sizeof(buff)));
+ print_sys_hostname(n->id));
}
n->present = false;
}
}
-void fabricd_lsp_flood(struct isis_lsp *lsp)
+static struct isis_lsp *lsp_for_neighbor(struct fabricd *f,
+ struct neighbor_entry *n)
{
- struct fabricd *f = lsp->area->fabricd;
- assert(f);
+ uint8_t id[ISIS_SYS_ID_LEN + 1] = {0};
- void *cursor = NULL;
- struct neighbor_entry *n;
+ memcpy(id, n->id, sizeof(n->id));
- if (isis->debugs & DEBUG_FABRICD_FLOODING) {
- zlog_debug("OpenFabric: Flooding LSP %s",
- rawlspid_print(lsp->hdr.lsp_id));
- }
+ struct isis_vertex vertex = {0};
- /* Mark all elements in NL as present and move T0s into DNR */
- while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) {
- n->present = true;
+ isis_vertex_id_init(&vertex, id, VTYPE_NONPSEUDO_TE_IS);
- struct isis_lsp *node_lsp = lsp_for_vertex(f->spftree,
- n->vertex);
- if (!node_lsp
- || !node_lsp->tlvs
- || !node_lsp->tlvs->spine_leaf
- || !node_lsp->tlvs->spine_leaf->has_tier
- || node_lsp->tlvs->spine_leaf->tier != 0) {
+ return lsp_for_vertex(f->spftree, &vertex);
+}
+
+static void fabricd_free_lsp_flooding_info(void *val)
+{
+ XFREE(MTYPE_FABRICD_FLOODING_INFO, val);
+}
+
+static void fabricd_lsp_reset_flooding_info(struct isis_lsp *lsp,
+ struct isis_circuit *circuit)
+{
+ lsp->flooding_time = time(NULL);
+
+ XFREE(MTYPE_FABRICD_FLOODING_INFO, lsp->flooding_interface);
+ for (enum isis_tx_type type = TX_LSP_NORMAL;
+ type <= TX_LSP_CIRCUIT_SCOPED; type++) {
+ if (lsp->flooding_neighbors[type]) {
+ list_delete_all_node(lsp->flooding_neighbors[type]);
continue;
}
- if (isis->debugs & DEBUG_FABRICD_FLOODING) {
- zlog_debug("Moving %s to DNR because it's T0",
- rawlspid_print(node_lsp->hdr.lsp_id));
- }
+ lsp->flooding_neighbors[type] = list_new();
+ lsp->flooding_neighbors[type]->del =
+ fabricd_free_lsp_flooding_info;
+ }
- move_to_dnr(lsp, n);
+ if (circuit) {
+ lsp->flooding_interface = XSTRDUP(MTYPE_FABRICD_FLOODING_INFO,
+ circuit->interface->name);
}
+ lsp->flooding_circuit_scoped = false;
+}
+
+void fabricd_lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit)
+{
+ struct fabricd *f = lsp->area->fabricd;
+ assert(f);
+
+ fabricd_lsp_reset_flooding_info(lsp, circuit);
+
+ void *cursor = NULL;
+ struct neighbor_entry *n;
+
+ /* Mark all elements in NL as present */
+ while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor))
+ n->present = true;
+
/* Mark all elements in NN as present */
hash_iterate(f->neighbors_neighbors, mark_neighbor_as_present, NULL);
- struct isis_vertex *originator = isis_find_vertex(&f->spftree->paths,
- lsp->hdr.lsp_id,
- VTYPE_NONPSEUDO_TE_IS);
+ struct isis_vertex *originator =
+ isis_find_vertex(&f->spftree->paths,
+ lsp->hdr.lsp_id,
+ VTYPE_NONPSEUDO_TE_IS);
/* Remove all IS from NL and NN in the shortest path
* to the IS that originated the LSP */
if (!n->present)
continue;
- struct isis_lsp *nlsp = lsp_for_vertex(f->spftree, n->vertex);
+ struct isis_lsp *nlsp = lsp_for_neighbor(f, n);
if (!nlsp || !nlsp->tlvs) {
- if (isis->debugs & DEBUG_FABRICD_FLOODING) {
- char buff[PREFIX2STR_BUFFER];
+ if (isis->debugs & DEBUG_FLOODING) {
zlog_debug("Moving %s to DNR as it has no LSP",
- vid2string(n->vertex, buff, sizeof(buff)));
+ print_sys_hostname(n->id));
}
- move_to_dnr(lsp, n);
+ move_to_queue(lsp, n, TX_LSP_CIRCUIT_SCOPED, circuit);
continue;
}
- if (isis->debugs & DEBUG_FABRICD_FLOODING) {
- char buff[PREFIX2STR_BUFFER];
+ if (isis->debugs & DEBUG_FLOODING) {
zlog_debug("Considering %s from NL...",
- vid2string(n->vertex, buff, sizeof(buff)));
+ print_sys_hostname(n->id));
}
/* For all neighbors of the NL IS check whether they are present
er->id);
if (nn) {
- if (isis->debugs & DEBUG_FABRICD_FLOODING) {
- char buff[PREFIX2STR_BUFFER];
+ if (isis->debugs & DEBUG_FLOODING) {
zlog_debug("Found neighbor %s in NN, removing it from NN and setting reflood.",
- vid2string(nn->vertex, buff, sizeof(buff)));
+ print_sys_hostname(nn->id));
}
nn->present = false;
}
}
- if (need_reflood)
- move_to_rf(lsp, n);
- else
- move_to_dnr(lsp, n);
+ move_to_queue(lsp, n, need_reflood ?
+ TX_LSP_NORMAL : TX_LSP_CIRCUIT_SCOPED,
+ circuit);
}
- if (isis->debugs & DEBUG_FABRICD_FLOODING) {
+ if (isis->debugs & DEBUG_FLOODING) {
zlog_debug("OpenFabric: Flooding algorithm complete.");
}
}
-void fabricd_trigger_csnp(struct isis_area *area)
+void fabricd_trigger_csnp(struct isis_area *area, bool circuit_scoped)
{
struct fabricd *f = area->fabricd;
if (!f)
return;
+ if (!circuit_scoped && !f->always_send_csnp)
+ return;
+
struct listnode *node;
struct isis_circuit *circuit;
thread_cancel(circuit->t_send_csnp[ISIS_LEVEL2 - 1]);
thread_add_timer_msec(master, send_l2_csnp, circuit,
- isis_jitter(500, CSNP_JITTER),
+ isis_jitter(f->csnp_delay, CSNP_JITTER),
&circuit->t_send_csnp[ISIS_LEVEL2 - 1]);
}
}
return NULL;
}
+
+void fabricd_lsp_free(struct isis_lsp *lsp)
+{
+ XFREE(MTYPE_FABRICD_FLOODING_INFO, lsp->flooding_interface);
+ for (enum isis_tx_type type = TX_LSP_NORMAL;
+ type <= TX_LSP_CIRCUIT_SCOPED; type++) {
+ if (!lsp->flooding_neighbors[type])
+ continue;
+
+ list_delete(&lsp->flooding_neighbors[type]);
+ }
+}
+
+void fabricd_update_lsp_no_flood(struct isis_lsp *lsp,
+ struct isis_circuit *circuit)
+{
+ if (!fabricd)
+ return;
+
+ fabricd_lsp_reset_flooding_info(lsp, circuit);
+ lsp->flooding_circuit_scoped = true;
+}
+
+void fabricd_configure_triggered_csnp(struct isis_area *area, int delay,
+ bool always_send_csnp)
+{
+ struct fabricd *f = area->fabricd;
+
+ if (!f)
+ return;
+
+ f->csnp_delay = delay;
+ f->always_send_csnp = always_send_csnp;
+}
+
+void fabricd_init(void)
+{
+ hook_register(isis_adj_state_change_hook,
+ fabricd_handle_adj_state_change);
+}