mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-04 00:59:13 +00:00
Merge pull request #1222 from opensourcerouting/isis-spf-improvements
isisd: spf improvements
This commit is contained in:
commit
1cec39e765
188
isisd/isis_spf.c
188
isisd/isis_spf.c
@ -36,6 +36,7 @@
|
||||
#include "table.h"
|
||||
#include "spf_backoff.h"
|
||||
#include "jhash.h"
|
||||
#include "skiplist.h"
|
||||
|
||||
#include "isis_constants.h"
|
||||
#include "isis_common.h"
|
||||
@ -88,14 +89,18 @@ struct isis_vertex {
|
||||
u_int16_t depth; /* The depth in the imaginary tree */
|
||||
struct list *Adj_N; /* {Adj(N)} next hop or neighbor list */
|
||||
struct list *parents; /* list of parents for ECMP */
|
||||
struct list *children; /* list of children used for tree dump */
|
||||
uint64_t insert_counter;
|
||||
};
|
||||
|
||||
/* Vertex Queue and associated functions */
|
||||
|
||||
struct isis_vertex_queue {
|
||||
struct list *list;
|
||||
union {
|
||||
struct skiplist *slist;
|
||||
struct list *list;
|
||||
} l;
|
||||
struct hash *hash;
|
||||
uint64_t insert_counter;
|
||||
};
|
||||
|
||||
static unsigned isis_vertex_queue_hash_key(void *vp)
|
||||
@ -121,9 +126,50 @@ static int isis_vertex_queue_hash_cmp(const void *a, const void *b)
|
||||
return memcmp(va->N.id, vb->N.id, ISIS_SYS_ID_LEN + 1) == 0;
|
||||
}
|
||||
|
||||
static void isis_vertex_queue_init(struct isis_vertex_queue *queue, const char *name)
|
||||
/*
|
||||
* Compares vertizes for sorting in the TENT list. Returns true
|
||||
* if candidate should be considered before current, false otherwise.
|
||||
*/
|
||||
static int isis_vertex_queue_tent_cmp(void *a, void *b)
|
||||
{
|
||||
queue->list = list_new();
|
||||
struct isis_vertex *va = a;
|
||||
struct isis_vertex *vb = b;
|
||||
|
||||
if (va->d_N < vb->d_N)
|
||||
return -1;
|
||||
|
||||
if (va->d_N > vb->d_N)
|
||||
return 1;
|
||||
|
||||
if (va->type < vb->type)
|
||||
return -1;
|
||||
|
||||
if (va->type > vb->type)
|
||||
return 1;
|
||||
|
||||
if (va->insert_counter < vb->insert_counter)
|
||||
return -1;
|
||||
|
||||
if (va->insert_counter > vb->insert_counter)
|
||||
return 1;
|
||||
|
||||
assert(!"Vertizes should be strictly ordered");
|
||||
}
|
||||
|
||||
static struct skiplist *isis_vertex_queue_skiplist(void)
|
||||
{
|
||||
return skiplist_new(0, isis_vertex_queue_tent_cmp, NULL);
|
||||
}
|
||||
|
||||
static void isis_vertex_queue_init(struct isis_vertex_queue *queue, const char *name, bool ordered)
|
||||
{
|
||||
if (ordered) {
|
||||
queue->insert_counter = 1;
|
||||
queue->l.slist = isis_vertex_queue_skiplist();
|
||||
} else {
|
||||
queue->insert_counter = 0;
|
||||
queue->l.list = list_new();
|
||||
}
|
||||
queue->hash = hash_create(isis_vertex_queue_hash_key,
|
||||
isis_vertex_queue_hash_cmp,
|
||||
name);
|
||||
@ -135,9 +181,18 @@ static void isis_vertex_queue_clear(struct isis_vertex_queue *queue)
|
||||
{
|
||||
hash_clean(queue->hash, NULL);
|
||||
|
||||
queue->list->del = (void (*)(void *))isis_vertex_del;
|
||||
list_delete_all_node(queue->list);
|
||||
queue->list->del = NULL;
|
||||
if (queue->insert_counter) {
|
||||
struct isis_vertex *vertex;
|
||||
while (0 == skiplist_first(queue->l.slist, NULL, (void**)&vertex)) {
|
||||
isis_vertex_del(vertex);
|
||||
skiplist_delete_first(queue->l.slist);
|
||||
}
|
||||
queue->insert_counter = 1;
|
||||
} else {
|
||||
queue->l.list->del = (void (*)(void *))isis_vertex_del;
|
||||
list_delete_all_node(queue->l.list);
|
||||
queue->l.list->del = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void isis_vertex_queue_free(struct isis_vertex_queue *queue)
|
||||
@ -147,19 +202,26 @@ static void isis_vertex_queue_free(struct isis_vertex_queue *queue)
|
||||
hash_free(queue->hash);
|
||||
queue->hash = NULL;
|
||||
|
||||
list_delete(queue->list);
|
||||
queue->list = NULL;
|
||||
if (queue->insert_counter) {
|
||||
skiplist_free(queue->l.slist);
|
||||
queue->l.slist = NULL;
|
||||
} else {
|
||||
list_delete(queue->l.list);
|
||||
queue->l.list = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int isis_vertex_queue_count(struct isis_vertex_queue *queue)
|
||||
{
|
||||
return listcount(queue->list);
|
||||
return hashcount(queue->hash);
|
||||
}
|
||||
|
||||
static void isis_vertex_queue_add(struct isis_vertex_queue *queue,
|
||||
struct isis_vertex *vertex)
|
||||
static void isis_vertex_queue_append(struct isis_vertex_queue *queue,
|
||||
struct isis_vertex *vertex)
|
||||
{
|
||||
listnode_add(queue->list, vertex);
|
||||
assert(!queue->insert_counter);
|
||||
|
||||
listnode_add(queue->l.list, vertex);
|
||||
|
||||
struct isis_vertex *inserted;
|
||||
|
||||
@ -167,17 +229,30 @@ static void isis_vertex_queue_add(struct isis_vertex_queue *queue,
|
||||
assert(inserted == vertex);
|
||||
}
|
||||
|
||||
static void isis_vertex_queue_insert(struct isis_vertex_queue *queue,
|
||||
struct isis_vertex *vertex)
|
||||
{
|
||||
assert(queue->insert_counter);
|
||||
vertex->insert_counter = queue->insert_counter++;
|
||||
assert(queue->insert_counter != (uint64_t)-1);
|
||||
|
||||
skiplist_insert(queue->l.slist, vertex, vertex);
|
||||
|
||||
struct isis_vertex *inserted;
|
||||
inserted = hash_get(queue->hash, vertex, hash_alloc_intern);
|
||||
assert(inserted == vertex);
|
||||
}
|
||||
|
||||
static struct isis_vertex *isis_vertex_queue_pop(struct isis_vertex_queue *queue)
|
||||
{
|
||||
struct listnode *node;
|
||||
assert(queue->insert_counter);
|
||||
|
||||
node = listhead(queue->list);
|
||||
if (!node)
|
||||
struct isis_vertex *rv;
|
||||
|
||||
if (skiplist_first(queue->l.slist, NULL, (void**)&rv))
|
||||
return NULL;
|
||||
|
||||
struct isis_vertex *rv = listgetdata(node);
|
||||
|
||||
list_delete_node(queue->list, node);
|
||||
skiplist_delete_first(queue->l.slist);
|
||||
hash_release(queue->hash, rv);
|
||||
|
||||
return rv;
|
||||
@ -186,12 +261,14 @@ static struct isis_vertex *isis_vertex_queue_pop(struct isis_vertex_queue *queue
|
||||
static void isis_vertex_queue_delete(struct isis_vertex_queue *queue,
|
||||
struct isis_vertex *vertex)
|
||||
{
|
||||
listnode_delete(queue->list, vertex);
|
||||
assert(queue->insert_counter);
|
||||
|
||||
skiplist_delete(queue->l.slist, vertex, vertex);
|
||||
hash_release(queue->hash, vertex);
|
||||
}
|
||||
|
||||
#define ALL_QUEUE_ELEMENTS_RO(queue, node, data) \
|
||||
ALL_LIST_ELEMENTS_RO((queue)->list, node, data)
|
||||
ALL_LIST_ELEMENTS_RO((queue)->l.list, node, data)
|
||||
|
||||
|
||||
/* End of vertex queue definitions */
|
||||
@ -353,7 +430,6 @@ static struct isis_vertex *isis_vertex_new(void *id, enum vertextype vtype)
|
||||
|
||||
vertex->Adj_N = list_new();
|
||||
vertex->parents = list_new();
|
||||
vertex->children = list_new();
|
||||
|
||||
return vertex;
|
||||
}
|
||||
@ -364,8 +440,6 @@ static void isis_vertex_del(struct isis_vertex *vertex)
|
||||
vertex->Adj_N = NULL;
|
||||
list_delete(vertex->parents);
|
||||
vertex->parents = NULL;
|
||||
list_delete(vertex->children);
|
||||
vertex->children = NULL;
|
||||
|
||||
memset(vertex, 0, sizeof(struct isis_vertex));
|
||||
XFREE(MTYPE_ISIS_VERTEX, vertex);
|
||||
@ -397,8 +471,8 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
isis_vertex_queue_init(&tree->tents, "IS-IS SPF tents");
|
||||
isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths");
|
||||
isis_vertex_queue_init(&tree->tents, "IS-IS SPF tents", true);
|
||||
isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths", false);
|
||||
tree->area = area;
|
||||
tree->last_run_timestamp = 0;
|
||||
tree->last_run_duration = 0;
|
||||
@ -422,8 +496,7 @@ static void isis_spftree_adj_del(struct isis_spftree *spftree,
|
||||
struct isis_vertex *v;
|
||||
if (!adj)
|
||||
return;
|
||||
for (ALL_QUEUE_ELEMENTS_RO(&spftree->tents, node, v))
|
||||
isis_vertex_adj_del(v, adj);
|
||||
assert(!isis_vertex_queue_count(&spftree->tents));
|
||||
for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, node, v))
|
||||
isis_vertex_adj_del(v, adj);
|
||||
return;
|
||||
@ -538,7 +611,7 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree,
|
||||
spftree->area->oldmetric
|
||||
? VTYPE_NONPSEUDO_IS
|
||||
: VTYPE_NONPSEUDO_TE_IS);
|
||||
isis_vertex_queue_add(&spftree->paths, vertex);
|
||||
isis_vertex_queue_append(&spftree->paths, vertex);
|
||||
|
||||
#ifdef EXTREME_DEBUG
|
||||
zlog_debug("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS",
|
||||
@ -559,45 +632,6 @@ static struct isis_vertex *isis_find_vertex(struct isis_vertex_queue *queue, voi
|
||||
return hash_lookup(queue->hash, &querier);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compares vertizes for sorting in the TENT list. Returns true
|
||||
* if candidate should be considered before current, false otherwise.
|
||||
*/
|
||||
static bool tent_cmp(struct isis_vertex *current, struct isis_vertex *candidate)
|
||||
{
|
||||
if (current->d_N > candidate->d_N)
|
||||
return true;
|
||||
|
||||
if (current->d_N == candidate->d_N && current->type > candidate->type)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void isis_vertex_queue_insert(struct isis_vertex_queue *queue,
|
||||
struct isis_vertex *vertex)
|
||||
{
|
||||
struct listnode *node;
|
||||
struct isis_vertex *v;
|
||||
|
||||
/* XXX: This cant use the standard ALL_LIST_ELEMENTS macro */
|
||||
for (node = listhead(queue->list); node; node = listnextnode(node)) {
|
||||
v = listgetdata(node);
|
||||
if (tent_cmp(v, vertex)) {
|
||||
listnode_add_before(queue->list, node, vertex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (node == NULL)
|
||||
listnode_add(queue->list, vertex);
|
||||
|
||||
struct isis_vertex *inserted;
|
||||
|
||||
inserted = hash_get(queue->hash, vertex, hash_alloc_intern);
|
||||
assert(inserted == vertex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a vertex to TENT sorted by cost and by vertextype on tie break situation
|
||||
*/
|
||||
@ -622,8 +656,6 @@ static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree,
|
||||
|
||||
if (parent) {
|
||||
listnode_add(vertex->parents, parent);
|
||||
if (listnode_lookup(parent->children, vertex) == NULL)
|
||||
listnode_add(parent->children, vertex);
|
||||
}
|
||||
|
||||
if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) {
|
||||
@ -665,9 +697,6 @@ static void isis_spf_add_local(struct isis_spftree *spftree,
|
||||
if (parent && (listnode_lookup(vertex->parents, parent)
|
||||
== NULL))
|
||||
listnode_add(vertex->parents, parent);
|
||||
if (parent && (listnode_lookup(parent->children, vertex)
|
||||
== NULL))
|
||||
listnode_add(parent->children, vertex);
|
||||
return;
|
||||
} else if (vertex->d_N < cost) {
|
||||
/* e) do nothing */
|
||||
@ -677,10 +706,6 @@ static void isis_spf_add_local(struct isis_spftree *spftree,
|
||||
struct listnode *pnode, *pnextnode;
|
||||
struct isis_vertex *pvertex;
|
||||
isis_vertex_queue_delete(&spftree->tents, vertex);
|
||||
assert(listcount(vertex->children) == 0);
|
||||
for (ALL_LIST_ELEMENTS(vertex->parents, pnode,
|
||||
pnextnode, pvertex))
|
||||
listnode_delete(pvertex->children, vertex);
|
||||
isis_vertex_del(vertex);
|
||||
}
|
||||
}
|
||||
@ -756,9 +781,6 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
|
||||
remove_excess_adjs(vertex->Adj_N);
|
||||
if (listnode_lookup(vertex->parents, parent) == NULL)
|
||||
listnode_add(vertex->parents, parent);
|
||||
if (listnode_lookup(parent->children, vertex) == NULL)
|
||||
listnode_add(parent->children, vertex);
|
||||
/* 3) */
|
||||
return;
|
||||
} else if (vertex->d_N < dist) {
|
||||
return;
|
||||
@ -767,10 +789,6 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
|
||||
struct listnode *pnode, *pnextnode;
|
||||
struct isis_vertex *pvertex;
|
||||
isis_vertex_queue_delete(&spftree->tents, vertex);
|
||||
assert(listcount(vertex->children) == 0);
|
||||
for (ALL_LIST_ELEMENTS(vertex->parents, pnode,
|
||||
pnextnode, pvertex))
|
||||
listnode_delete(pvertex->children, vertex);
|
||||
isis_vertex_del(vertex);
|
||||
}
|
||||
}
|
||||
@ -1198,7 +1216,7 @@ static void add_to_paths(struct isis_spftree *spftree,
|
||||
|
||||
if (isis_find_vertex(&spftree->paths, vertex->N.id, vertex->type))
|
||||
return;
|
||||
isis_vertex_queue_add(&spftree->paths, vertex);
|
||||
isis_vertex_queue_append(&spftree->paths, vertex);
|
||||
|
||||
#ifdef EXTREME_DEBUG
|
||||
zlog_debug("ISIS-Spf: added %s %s %s depth %d dist %d to PATHS",
|
||||
|
1
tests/.gitignore
vendored
1
tests/.gitignore
vendored
@ -26,6 +26,7 @@ __pycache__
|
||||
/bgpd/test_mpath
|
||||
/isisd/test_fuzz_isis_tlv
|
||||
/isisd/test_fuzz_isis_tlv_tests.h
|
||||
/isisd/test_isis_vertex_queue
|
||||
/lib/cli/test_cli
|
||||
/lib/cli/test_commands
|
||||
/lib/cli/test_commands_defun.c
|
||||
|
@ -29,7 +29,9 @@ if SOLARIS
|
||||
TESTS_ISISD =
|
||||
else
|
||||
TESTS_ISISD = \
|
||||
isisd/test_fuzz_isis_tlv
|
||||
isisd/test_fuzz_isis_tlv \
|
||||
isisd/test_isis_vertex_queue \
|
||||
# end
|
||||
endif
|
||||
else
|
||||
TESTS_ISISD =
|
||||
@ -135,6 +137,7 @@ bgpd_test_mp_attr_SOURCES = bgpd/test_mp_attr.c
|
||||
bgpd_test_mpath_SOURCES = bgpd/test_mpath.c
|
||||
isisd_test_fuzz_isis_tlv_SOURCES = isisd/test_fuzz_isis_tlv.c
|
||||
isisd_test_fuzz_isis_tlv_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/tests/isisd
|
||||
isisd_test_isis_vertex_queue_SOURCES = isisd/test_isis_vertex_queue.c
|
||||
|
||||
ospf6d_test_lsdb_SOURCES = ospf6d/test_lsdb.c lib/cli/common_cli.c
|
||||
|
||||
@ -168,6 +171,7 @@ bgpd_test_ecommunity_LDADD = $(BGP_TEST_LDADD)
|
||||
bgpd_test_mp_attr_LDADD = $(BGP_TEST_LDADD)
|
||||
bgpd_test_mpath_LDADD = $(BGP_TEST_LDADD)
|
||||
isisd_test_fuzz_isis_tlv_LDADD = $(ISISD_TEST_LDADD)
|
||||
isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD)
|
||||
ospf6d_test_lsdb_LDADD = $(OSPF6_TEST_LDADD)
|
||||
|
||||
EXTRA_DIST = \
|
||||
@ -181,6 +185,7 @@ EXTRA_DIST = \
|
||||
helpers/python/frrtest.py \
|
||||
isisd/test_fuzz_isis_tlv.py \
|
||||
isisd/test_fuzz_isis_tlv_tests.h.gz \
|
||||
isisd/test_isis_vertex_queue.py \
|
||||
lib/cli/test_commands.in \
|
||||
lib/cli/test_commands.py \
|
||||
lib/cli/test_commands.refout \
|
||||
|
108
tests/isisd/test_isis_vertex_queue.c
Normal file
108
tests/isisd/test_isis_vertex_queue.c
Normal file
@ -0,0 +1,108 @@
|
||||
#include <zebra.h>
|
||||
|
||||
#include "isisd/isis_spf.c"
|
||||
|
||||
struct thread_master *master;
|
||||
int isis_sock_init(struct isis_circuit *circuit);
|
||||
int isis_sock_init(struct isis_circuit *circuit)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct isis_vertex **vertices;
|
||||
static size_t vertex_count;
|
||||
|
||||
static void setup_test_vertices(void)
|
||||
{
|
||||
struct prefix p = {
|
||||
.family = AF_UNSPEC
|
||||
};
|
||||
uint8_t node_id[7];
|
||||
|
||||
vertices = XMALLOC(MTYPE_TMP, sizeof(*vertices) * 16);
|
||||
|
||||
p.family = AF_INET;
|
||||
p.prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.1.0", &p.u.prefix4);
|
||||
vertices[vertex_count] = isis_vertex_new(&p, VTYPE_IPREACH_TE);
|
||||
vertices[vertex_count]->d_N = 20;
|
||||
vertex_count++;
|
||||
|
||||
p.family = AF_INET;
|
||||
p.prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.2.0", &p.u.prefix4);
|
||||
vertices[vertex_count] = isis_vertex_new(&p, VTYPE_IPREACH_TE);
|
||||
vertices[vertex_count]->d_N = 20;
|
||||
vertex_count++;
|
||||
|
||||
memset(node_id, 0, sizeof(node_id));
|
||||
node_id[6] = 1;
|
||||
vertices[vertex_count] = isis_vertex_new(node_id, VTYPE_PSEUDO_TE_IS);
|
||||
vertices[vertex_count]->d_N = 15;
|
||||
vertex_count++;
|
||||
|
||||
memset(node_id, 0, sizeof(node_id));
|
||||
node_id[5] = 2;
|
||||
vertices[vertex_count] = isis_vertex_new(node_id, VTYPE_NONPSEUDO_TE_IS);
|
||||
vertices[vertex_count]->d_N = 15;
|
||||
vertex_count++;
|
||||
|
||||
p.family = AF_INET;
|
||||
p.prefixlen = 24;
|
||||
inet_pton(AF_INET, "192.168.3.0", &p.u.prefix4);
|
||||
vertices[vertex_count] = isis_vertex_new(&p, VTYPE_IPREACH_TE);
|
||||
vertices[vertex_count]->d_N = 20;
|
||||
vertex_count++;
|
||||
};
|
||||
|
||||
static void cleanup_test_vertices(void)
|
||||
{
|
||||
for (size_t i = 0; i < vertex_count; i++)
|
||||
isis_vertex_del(vertices[i]);
|
||||
XFREE(MTYPE_TMP, vertices);
|
||||
vertex_count = 0;
|
||||
}
|
||||
|
||||
static void test_ordered(void)
|
||||
{
|
||||
struct isis_vertex_queue q;
|
||||
|
||||
isis_vertex_queue_init(&q, NULL, true);
|
||||
for (size_t i = 0; i < vertex_count; i++)
|
||||
isis_vertex_queue_insert(&q, vertices[i]);
|
||||
|
||||
assert(isis_vertex_queue_count(&q) == vertex_count);
|
||||
|
||||
for (size_t i = 0; i < vertex_count; i++) {
|
||||
assert(isis_find_vertex(&q, vertices[i]->N.id, vertices[i]->type) == vertices[i]);
|
||||
}
|
||||
|
||||
assert(isis_vertex_queue_pop(&q) == vertices[2]);
|
||||
assert(isis_find_vertex(&q, vertices[2]->N.id, vertices[2]->type) == NULL);
|
||||
|
||||
assert(isis_vertex_queue_pop(&q) == vertices[3]);
|
||||
assert(isis_find_vertex(&q, vertices[3]->N.id, vertices[3]->type) == NULL);
|
||||
|
||||
assert(isis_vertex_queue_pop(&q) == vertices[0]);
|
||||
assert(isis_find_vertex(&q, vertices[0]->N.id, vertices[0]->type) == NULL);
|
||||
|
||||
assert(isis_vertex_queue_pop(&q) == vertices[1]);
|
||||
assert(isis_find_vertex(&q, vertices[1]->N.id, vertices[1]->type) == NULL);
|
||||
|
||||
assert(isis_vertex_queue_pop(&q) == vertices[4]);
|
||||
assert(isis_find_vertex(&q, vertices[4]->N.id, vertices[4]->type) == NULL);
|
||||
|
||||
assert(isis_vertex_queue_count(&q) == 0);
|
||||
assert(isis_vertex_queue_pop(&q) == NULL);
|
||||
|
||||
isis_vertex_queue_free(&q);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
setup_test_vertices();
|
||||
test_ordered();
|
||||
cleanup_test_vertices();
|
||||
|
||||
return 0;
|
||||
}
|
6
tests/isisd/test_isis_vertex_queue.py
Normal file
6
tests/isisd/test_isis_vertex_queue.py
Normal file
@ -0,0 +1,6 @@
|
||||
import frrtest
|
||||
|
||||
class TestIsisVertexQueue(frrtest.TestMultiOut):
|
||||
program = './test_isis_vertex_queue'
|
||||
|
||||
TestIsisVertexQueue.exit_cleanly()
|
Loading…
Reference in New Issue
Block a user