summaryrefslogtreecommitdiffstats
path: root/tests/isisd/test_isis_spf.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/isisd/test_isis_spf.c')
-rw-r--r--tests/isisd/test_isis_spf.c570
1 files changed, 570 insertions, 0 deletions
diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c
new file mode 100644
index 0000000..971aba4
--- /dev/null
+++ b/tests/isisd/test_isis_spf.c
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * 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)
+ * any later version.
+ *
+ * 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
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "thread.h"
+#include "vty.h"
+#include "command.h"
+#include "log.h"
+#include "vrf.h"
+#include "yang.h"
+
+#include "isisd/isisd.h"
+#include "isisd/isis_dynhn.h"
+#include "isisd/isis_misc.h"
+#include "isisd/isis_route.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_spf_private.h"
+
+#include "test_common.h"
+
+enum test_type {
+ TEST_SPF = 1,
+ TEST_REVERSE_SPF,
+ TEST_LFA,
+ TEST_RLFA,
+ TEST_TI_LFA,
+};
+
+#define F_DISPLAY_LSPDB 0x01
+#define F_IPV4_ONLY 0x02
+#define F_IPV6_ONLY 0x04
+#define F_LEVEL1_ONLY 0x08
+#define F_LEVEL2_ONLY 0x10
+
+static void test_run_spf(struct vty *vty, const struct isis_topology *topology,
+ const struct isis_test_node *root,
+ struct isis_area *area, struct lspdb_head *lspdb,
+ int level, int tree, bool reverse)
+{
+ struct isis_spftree *spftree;
+ enum spf_type spf_type;
+
+ /* Run SPF. */
+ spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD;
+ spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree,
+ spf_type, F_SPFTREE_NO_ADJACENCIES);
+ isis_run_spf(spftree);
+
+ /* Print the SPT and the corresponding routing table. */
+ isis_print_spftree(vty, spftree);
+ isis_print_routes(vty, spftree, false, false);
+
+ /* Cleanup SPF tree. */
+ isis_spftree_del(spftree);
+}
+
+static void test_run_lfa(struct vty *vty, const struct isis_topology *topology,
+ const struct isis_test_node *root,
+ struct isis_area *area, struct lspdb_head *lspdb,
+ int level, int tree,
+ struct lfa_protected_resource *protected_resource)
+{
+ struct isis_spftree *spftree_self;
+ uint8_t flags;
+
+ /* Run forward SPF in the root node. */
+ flags = F_SPFTREE_NO_ADJACENCIES;
+ spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree,
+ SPF_TYPE_FORWARD, flags);
+ isis_run_spf(spftree_self);
+
+ /* Run forward SPF on all adjacent routers. */
+ isis_spf_run_neighbors(spftree_self);
+
+ /* Compute the LFA repair paths. */
+ isis_lfa_compute(area, NULL, spftree_self, protected_resource);
+
+ /* Print the SPT and the corresponding main/backup routing tables. */
+ isis_print_spftree(vty, spftree_self);
+ vty_out(vty, "Main:\n");
+ isis_print_routes(vty, spftree_self, false, false);
+ vty_out(vty, "Backup:\n");
+ isis_print_routes(vty, spftree_self, false, true);
+
+ /* Cleanup everything. */
+ isis_spftree_del(spftree_self);
+}
+
+static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology,
+ const struct isis_test_node *root,
+ struct isis_area *area, struct lspdb_head *lspdb,
+ int level, int tree,
+ struct lfa_protected_resource *protected_resource)
+{
+ struct isis_spftree *spftree_self;
+ struct isis_spftree *spftree_reverse;
+ struct isis_spftree *spftree_pc;
+ struct isis_spf_node *spf_node, *node;
+ struct rlfa *rlfa;
+ uint8_t flags;
+
+ /* Run forward SPF in the root node. */
+ flags = F_SPFTREE_NO_ADJACENCIES;
+ spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree,
+ SPF_TYPE_FORWARD, flags);
+ isis_run_spf(spftree_self);
+
+ /* Run reverse SPF in the root node. */
+ spftree_reverse = isis_spf_reverse_run(spftree_self);
+
+ /* Run forward SPF on all adjacent routers. */
+ isis_spf_run_neighbors(spftree_self);
+
+ /* Compute the local LFA repair paths. */
+ isis_lfa_compute(area, NULL, spftree_self, protected_resource);
+
+ /* Compute the remote LFA repair paths. */
+ spftree_pc = isis_rlfa_compute(area, spftree_self, spftree_reverse, 0,
+ protected_resource);
+
+ /* Print the extended P-space and Q-space. */
+ vty_out(vty, "P-space (self):\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ RB_FOREACH (spf_node, isis_spf_nodes, &spftree_self->adj_nodes) {
+ if (RB_EMPTY(isis_spf_nodes, &spf_node->lfa.p_space))
+ continue;
+ vty_out(vty, "P-space (%s):\n",
+ print_sys_hostname(spf_node->sysid));
+ RB_FOREACH (node, isis_spf_nodes, &spf_node->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ }
+ vty_out(vty, "Q-space:\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.q_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+
+ /* Print the post-convergence SPT. */
+ isis_print_spftree(vty, spftree_pc);
+
+ /*
+ * Activate the computed RLFAs (if any) using artificial LDP labels for
+ * the PQ nodes.
+ */
+ frr_each_safe (rlfa_tree, &spftree_self->lfa.remote.rlfas, rlfa) {
+ struct zapi_rlfa_response response = {};
+
+ response.pq_label = test_topology_node_ldp_label(
+ topology, rlfa->pq_address);
+ assert(response.pq_label != MPLS_INVALID_LABEL);
+ isis_rlfa_activate(spftree_self, rlfa, &response);
+ }
+
+ /* Print the SPT and the corresponding main/backup routing tables. */
+ isis_print_spftree(vty, spftree_self);
+ vty_out(vty, "Main:\n");
+ isis_print_routes(vty, spftree_self, false, false);
+ vty_out(vty, "Backup:\n");
+ isis_print_routes(vty, spftree_self, false, true);
+
+ /* Cleanup everything. */
+ isis_spftree_del(spftree_self);
+ isis_spftree_del(spftree_reverse);
+ isis_spftree_del(spftree_pc);
+}
+
+static void test_run_ti_lfa(struct vty *vty,
+ const struct isis_topology *topology,
+ const struct isis_test_node *root,
+ struct isis_area *area, struct lspdb_head *lspdb,
+ int level, int tree,
+ struct lfa_protected_resource *protected_resource)
+{
+ struct isis_spftree *spftree_self;
+ struct isis_spftree *spftree_reverse;
+ struct isis_spftree *spftree_pc;
+ struct isis_spf_node *spf_node, *node;
+ uint8_t flags;
+
+ /* Run forward SPF in the root node. */
+ flags = F_SPFTREE_NO_ADJACENCIES;
+ spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree,
+ SPF_TYPE_FORWARD, flags);
+ isis_run_spf(spftree_self);
+
+ /* Run reverse SPF in the root node. */
+ spftree_reverse = isis_spf_reverse_run(spftree_self);
+
+ /* Run forward SPF on all adjacent routers. */
+ isis_spf_run_neighbors(spftree_self);
+
+ /* Compute the TI-LFA repair paths. */
+ spftree_pc = isis_tilfa_compute(area, spftree_self, spftree_reverse,
+ protected_resource);
+
+ /* Print the extended P-space and Q-space. */
+ vty_out(vty, "P-space (self):\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ RB_FOREACH (spf_node, isis_spf_nodes, &spftree_self->adj_nodes) {
+ if (RB_EMPTY(isis_spf_nodes, &spf_node->lfa.p_space))
+ continue;
+ vty_out(vty, "P-space (%s):\n",
+ print_sys_hostname(spf_node->sysid));
+ RB_FOREACH (node, isis_spf_nodes, &spf_node->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ }
+ vty_out(vty, "Q-space:\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.q_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+
+ /*
+ * Print the post-convergence SPT and the corresponding routing table.
+ */
+ isis_print_spftree(vty, spftree_pc);
+ isis_print_routes(vty, spftree_self, false, true);
+
+ /* Cleanup everything. */
+ isis_spftree_del(spftree_self);
+ isis_spftree_del(spftree_reverse);
+ isis_spftree_del(spftree_pc);
+}
+
+static int test_run(struct vty *vty, const struct isis_topology *topology,
+ const struct isis_test_node *root, enum test_type test_type,
+ uint8_t flags, enum lfa_protection_type protection_type,
+ const char *fail_sysid_str, uint8_t fail_pseudonode_id)
+{
+ struct isis_area *area;
+ struct lfa_protected_resource protected_resource = {};
+ uint8_t fail_id[ISIS_SYS_ID_LEN] = {};
+
+ /* Init topology. */
+ area = isis_area_create("1", NULL);
+ memcpy(area->isis->sysid, root->sysid, sizeof(area->isis->sysid));
+ area->is_type = IS_LEVEL_1_AND_2;
+ area->srdb.enabled = true;
+ if (test_topology_load(topology, area, area->lspdb) != 0) {
+ vty_out(vty, "%% Failed to load topology\n");
+ return CMD_WARNING;
+ }
+
+ /* Parse failed link/node. */
+ if (fail_sysid_str) {
+ if (sysid2buff(fail_id, fail_sysid_str) == 0) {
+ struct isis_dynhn *dynhn;
+
+ dynhn = dynhn_find_by_name(area->isis, fail_sysid_str);
+ if (dynhn == NULL) {
+ vty_out(vty, "Invalid system id %s\n",
+ fail_sysid_str);
+ return CMD_WARNING;
+ }
+ memcpy(fail_id, dynhn->id, ISIS_SYS_ID_LEN);
+ }
+
+ protected_resource.type = protection_type;
+ memcpy(protected_resource.adjacency, fail_id, ISIS_SYS_ID_LEN);
+ LSP_PSEUDO_ID(protected_resource.adjacency) =
+ fail_pseudonode_id;
+ }
+
+ for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
+ if (level == IS_LEVEL_1 && CHECK_FLAG(flags, F_LEVEL2_ONLY))
+ continue;
+ if (level == IS_LEVEL_2 && CHECK_FLAG(flags, F_LEVEL1_ONLY))
+ continue;
+ if ((root->level & level) == 0)
+ continue;
+
+ /* Print the LDPDB. */
+ if (CHECK_FLAG(flags, F_DISPLAY_LSPDB))
+ show_isis_database_lspdb_vty(vty, area, level - 1,
+ &area->lspdb[level - 1], NULL,
+ ISIS_UI_LEVEL_DETAIL);
+
+ for (int tree = SPFTREE_IPV4; tree <= SPFTREE_IPV6; tree++) {
+ if (tree == SPFTREE_IPV4
+ && CHECK_FLAG(flags, F_IPV6_ONLY))
+ continue;
+ if (tree == SPFTREE_IPV6
+ && CHECK_FLAG(flags, F_IPV4_ONLY))
+ continue;
+
+ switch (test_type) {
+ case TEST_SPF:
+ test_run_spf(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, false);
+ break;
+ case TEST_REVERSE_SPF:
+ test_run_spf(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, true);
+ break;
+ case TEST_LFA:
+ test_run_lfa(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, &protected_resource);
+ break;
+ case TEST_RLFA:
+ test_run_rlfa(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, &protected_resource);
+ break;
+ case TEST_TI_LFA:
+ test_run_ti_lfa(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, &protected_resource);
+ break;
+ }
+ }
+ }
+
+ /* Cleanup IS-IS area. */
+ isis_area_destroy(area);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(test_isis, test_isis_cmd,
+ "test isis topology (1-14) root HOSTNAME\
+ <\
+ spf\
+ |reverse-spf\
+ |lfa system-id WORD [pseudonode-id <1-255>]\
+ |remote-lfa system-id WORD [pseudonode-id <1-255>]\
+ |ti-lfa system-id WORD [pseudonode-id <1-255>] [node-protection]\
+ >\
+ [display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]",
+ "Test command\n"
+ "IS-IS routing protocol\n"
+ "Test topology\n"
+ "Test topology number\n"
+ "SPF root\n"
+ "SPF root hostname\n"
+ "Normal Shortest Path First\n"
+ "Reverse Shortest Path First\n"
+ "Classic LFA\n"
+ "System ID\n"
+ "System ID\n"
+ "Pseudonode-ID\n"
+ "Pseudonode-ID\n"
+ "Remote LFA\n"
+ "System ID\n"
+ "System ID\n"
+ "Pseudonode-ID\n"
+ "Pseudonode-ID\n"
+ "Topology Independent LFA\n"
+ "System ID\n"
+ "System ID\n"
+ "Pseudonode-ID\n"
+ "Pseudonode-ID\n"
+ "Node protection\n"
+ "Display the LSPDB\n"
+ "Do IPv4 processing only\n"
+ "Do IPv6 processing only\n"
+ "Skip L2 LSPs\n"
+ "Skip L1 LSPs\n")
+{
+ uint16_t topology_number;
+ const struct isis_topology *topology;
+ const struct isis_test_node *root;
+ enum test_type test_type;
+ enum lfa_protection_type protection_type = 0;
+ const char *fail_sysid_str = NULL;
+ uint8_t fail_pseudonode_id = 0;
+ uint8_t flags = 0;
+ int idx = 0;
+
+ /* Load topology. */
+ argv_find(argv, argc, "topology", &idx);
+ topology_number = atoi(argv[idx + 1]->arg);
+ topology = test_topology_find(test_topologies, topology_number);
+ if (!topology) {
+ vty_out(vty, "%% Topology \"%s\" not found\n",
+ argv[idx + 1]->arg);
+ return CMD_WARNING;
+ }
+
+ /* Find root node. */
+ argv_find(argv, argc, "root", &idx);
+ root = test_topology_find_node(topology, argv[idx + 1]->arg, 0);
+ if (!root) {
+ vty_out(vty, "%% Node \"%s\" not found\n", argv[idx + 1]->arg);
+ return CMD_WARNING;
+ }
+
+ /* Parse test information. */
+ if (argv_find(argv, argc, "spf", &idx))
+ test_type = TEST_SPF;
+ else if (argv_find(argv, argc, "reverse-spf", &idx))
+ test_type = TEST_REVERSE_SPF;
+ else if (argv_find(argv, argc, "lfa", &idx)) {
+ test_type = TEST_LFA;
+
+ fail_sysid_str = argv[idx + 2]->arg;
+ if (argv_find(argv, argc, "pseudonode-id", &idx))
+ fail_pseudonode_id =
+ strtoul(argv[idx + 1]->arg, NULL, 10);
+ protection_type = LFA_LINK_PROTECTION;
+ } else if (argv_find(argv, argc, "remote-lfa", &idx)) {
+ test_type = TEST_RLFA;
+
+ fail_sysid_str = argv[idx + 2]->arg;
+ if (argv_find(argv, argc, "pseudonode-id", &idx))
+ fail_pseudonode_id =
+ strtoul(argv[idx + 1]->arg, NULL, 10);
+ protection_type = LFA_LINK_PROTECTION;
+ } else if (argv_find(argv, argc, "ti-lfa", &idx)) {
+ test_type = TEST_TI_LFA;
+
+ fail_sysid_str = argv[idx + 2]->arg;
+ if (argv_find(argv, argc, "pseudonode-id", &idx))
+ fail_pseudonode_id =
+ strtoul(argv[idx + 1]->arg, NULL, 10);
+ if (argv_find(argv, argc, "node-protection", &idx))
+ protection_type = LFA_NODE_PROTECTION;
+ else
+ protection_type = LFA_LINK_PROTECTION;
+ } else
+ return CMD_WARNING;
+
+ /* Parse control flags. */
+ if (argv_find(argv, argc, "display-lspdb", &idx))
+ SET_FLAG(flags, F_DISPLAY_LSPDB);
+ if (argv_find(argv, argc, "ipv4-only", &idx))
+ SET_FLAG(flags, F_IPV4_ONLY);
+ else if (argv_find(argv, argc, "ipv6-only", &idx))
+ SET_FLAG(flags, F_IPV6_ONLY);
+ if (argv_find(argv, argc, "level-1-only", &idx))
+ SET_FLAG(flags, F_LEVEL1_ONLY);
+ else if (argv_find(argv, argc, "level-2-only", &idx))
+ SET_FLAG(flags, F_LEVEL2_ONLY);
+
+ return test_run(vty, topology, root, test_type, flags, protection_type,
+ fail_sysid_str, fail_pseudonode_id);
+}
+
+static void vty_do_exit(int isexit)
+{
+ printf("\nend.\n");
+
+ cmd_terminate();
+ vty_terminate();
+ yang_terminate();
+ thread_master_free(master);
+
+ log_memstats(stderr, "test-isis-spf");
+ if (!isexit)
+ exit(0);
+}
+
+struct option longopts[] = {{"help", no_argument, NULL, 'h'},
+ {"debug", no_argument, NULL, 'd'},
+ {0}};
+
+/* Help information display. */
+static void usage(char *progname, int status)
+{
+ if (status != 0)
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ progname);
+ else {
+ printf("Usage : %s [OPTION...]\n\
+isisd SPF test program.\n\n\
+-u, --debug Enable debugging\n\
+-h, --help Display this help and exit\n\
+\n\
+Report bugs to %s\n",
+ progname, FRR_BUG_ADDRESS);
+ }
+ exit(status);
+}
+
+int main(int argc, char **argv)
+{
+ char *p;
+ char *progname;
+ struct thread thread;
+ bool debug = false;
+
+ /* Set umask before anything for security */
+ umask(0027);
+
+ /* get program name */
+ progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
+
+ while (1) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "hd", longopts, 0);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ case 'd':
+ debug = true;
+ break;
+ case 'h':
+ usage(progname, 0);
+ break;
+ default:
+ usage(progname, 1);
+ break;
+ }
+ }
+
+ /* master init. */
+ master = thread_master_create(NULL);
+ isis_master_init(master);
+
+ /* Library inits. */
+ cmd_init(1);
+ cmd_hostname_set("test");
+ vty_init(master, false);
+ yang_init(true, false);
+ if (debug)
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+ else
+ zlog_aux_init("NONE: ", ZLOG_DISABLED);
+
+ /* IS-IS inits. */
+ yang_module_load("frr-isisd");
+ SET_FLAG(im->options, F_ISIS_UNIT_TEST);
+ debug_spf_events |= DEBUG_SPF_EVENTS;
+ debug_lfa |= DEBUG_LFA;
+ debug_events |= DEBUG_EVENTS;
+ debug_rte_events |= DEBUG_RTE_EVENTS;
+
+ /* Install test command. */
+ install_element(VIEW_NODE, &test_isis_cmd);
+
+ /* Read input from .in file. */
+ vty_stdio(vty_do_exit);
+
+ /* Fetch next active thread. */
+ while (thread_fetch(master, &thread))
+ thread_call(&thread);
+
+ /* Not reached. */
+ exit(0);
+}