]> git.proxmox.com Git - mirror_frr.git/blame - tests/isisd/test_isis_spf.c
Merge pull request #12837 from donaldsharp/unlikely_routemap
[mirror_frr.git] / tests / isisd / test_isis_spf.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
52a7c25e
RW
2/*
3 * Copyright (C) 2020 NetDEF, Inc.
4 * Renato Westphal
52a7c25e
RW
5 */
6
7#include <zebra.h>
8
9#include <lib/version.h>
10#include "getopt.h"
24a58196 11#include "frrevent.h"
52a7c25e
RW
12#include "vty.h"
13#include "command.h"
14#include "log.h"
15#include "vrf.h"
16#include "yang.h"
17
18#include "isisd/isisd.h"
19#include "isisd/isis_dynhn.h"
20#include "isisd/isis_misc.h"
c40de294 21#include "isisd/isis_route.h"
52a7c25e
RW
22#include "isisd/isis_spf.h"
23#include "isisd/isis_spf_private.h"
24
25#include "test_common.h"
26
27enum test_type {
28 TEST_SPF = 1,
75aa7aa1 29 TEST_REVERSE_SPF,
c8a4f331 30 TEST_LFA,
c40de294 31 TEST_RLFA,
ca74d663 32 TEST_TI_LFA,
52a7c25e
RW
33};
34
35#define F_DISPLAY_LSPDB 0x01
36#define F_IPV4_ONLY 0x02
37#define F_IPV6_ONLY 0x04
38#define F_LEVEL1_ONLY 0x08
39#define F_LEVEL2_ONLY 0x10
40
52a7c25e
RW
41static void test_run_spf(struct vty *vty, const struct isis_topology *topology,
42 const struct isis_test_node *root,
43 struct isis_area *area, struct lspdb_head *lspdb,
75aa7aa1 44 int level, int tree, bool reverse)
52a7c25e
RW
45{
46 struct isis_spftree *spftree;
75aa7aa1 47 enum spf_type spf_type;
52a7c25e
RW
48
49 /* Run SPF. */
75aa7aa1 50 spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD;
52a7c25e 51 spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree,
75aa7aa1 52 spf_type, F_SPFTREE_NO_ADJACENCIES);
52a7c25e
RW
53 isis_run_spf(spftree);
54
55 /* Print the SPT and the corresponding routing table. */
56 isis_print_spftree(vty, spftree);
d47d6089 57 isis_print_routes(vty, spftree, false, false);
52a7c25e
RW
58
59 /* Cleanup SPF tree. */
60 isis_spftree_del(spftree);
61}
62
c8a4f331
RW
63static void test_run_lfa(struct vty *vty, const struct isis_topology *topology,
64 const struct isis_test_node *root,
65 struct isis_area *area, struct lspdb_head *lspdb,
66 int level, int tree,
67 struct lfa_protected_resource *protected_resource)
68{
69 struct isis_spftree *spftree_self;
70 uint8_t flags;
71
72 /* Run forward SPF in the root node. */
73 flags = F_SPFTREE_NO_ADJACENCIES;
74 spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree,
75 SPF_TYPE_FORWARD, flags);
76 isis_run_spf(spftree_self);
77
78 /* Run forward SPF on all adjacent routers. */
79 isis_spf_run_neighbors(spftree_self);
80
81 /* Compute the LFA repair paths. */
82 isis_lfa_compute(area, NULL, spftree_self, protected_resource);
83
84 /* Print the SPT and the corresponding main/backup routing tables. */
85 isis_print_spftree(vty, spftree_self);
86 vty_out(vty, "Main:\n");
87 isis_print_routes(vty, spftree_self, false, false);
88 vty_out(vty, "Backup:\n");
89 isis_print_routes(vty, spftree_self, false, true);
90
91 /* Cleanup everything. */
92 isis_spftree_del(spftree_self);
93}
94
c40de294
RW
95static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology,
96 const struct isis_test_node *root,
97 struct isis_area *area, struct lspdb_head *lspdb,
98 int level, int tree,
99 struct lfa_protected_resource *protected_resource)
100{
101 struct isis_spftree *spftree_self;
102 struct isis_spftree *spftree_reverse;
103 struct isis_spftree *spftree_pc;
104 struct isis_spf_node *spf_node, *node;
105 struct rlfa *rlfa;
106 uint8_t flags;
107
108 /* Run forward SPF in the root node. */
109 flags = F_SPFTREE_NO_ADJACENCIES;
110 spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree,
111 SPF_TYPE_FORWARD, flags);
112 isis_run_spf(spftree_self);
113
114 /* Run reverse SPF in the root node. */
115 spftree_reverse = isis_spf_reverse_run(spftree_self);
116
117 /* Run forward SPF on all adjacent routers. */
118 isis_spf_run_neighbors(spftree_self);
119
120 /* Compute the local LFA repair paths. */
121 isis_lfa_compute(area, NULL, spftree_self, protected_resource);
122
123 /* Compute the remote LFA repair paths. */
124 spftree_pc = isis_rlfa_compute(area, spftree_self, spftree_reverse, 0,
125 protected_resource);
126
127 /* Print the extended P-space and Q-space. */
128 vty_out(vty, "P-space (self):\n");
129 RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.p_space)
130 vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
131 vty_out(vty, "\n");
132 RB_FOREACH (spf_node, isis_spf_nodes, &spftree_self->adj_nodes) {
133 if (RB_EMPTY(isis_spf_nodes, &spf_node->lfa.p_space))
134 continue;
135 vty_out(vty, "P-space (%s):\n",
136 print_sys_hostname(spf_node->sysid));
137 RB_FOREACH (node, isis_spf_nodes, &spf_node->lfa.p_space)
138 vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
139 vty_out(vty, "\n");
140 }
141 vty_out(vty, "Q-space:\n");
142 RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.q_space)
143 vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
144 vty_out(vty, "\n");
145
146 /* Print the post-convergence SPT. */
147 isis_print_spftree(vty, spftree_pc);
148
149 /*
150 * Activate the computed RLFAs (if any) using artificial LDP labels for
151 * the PQ nodes.
152 */
153 frr_each_safe (rlfa_tree, &spftree_self->lfa.remote.rlfas, rlfa) {
154 struct zapi_rlfa_response response = {};
155
156 response.pq_label = test_topology_node_ldp_label(
157 topology, rlfa->pq_address);
158 assert(response.pq_label != MPLS_INVALID_LABEL);
159 isis_rlfa_activate(spftree_self, rlfa, &response);
160 }
161
162 /* Print the SPT and the corresponding main/backup routing tables. */
163 isis_print_spftree(vty, spftree_self);
164 vty_out(vty, "Main:\n");
165 isis_print_routes(vty, spftree_self, false, false);
166 vty_out(vty, "Backup:\n");
167 isis_print_routes(vty, spftree_self, false, true);
168
169 /* Cleanup everything. */
170 isis_spftree_del(spftree_self);
171 isis_spftree_del(spftree_reverse);
172 isis_spftree_del(spftree_pc);
173}
174
ca74d663
RW
175static void test_run_ti_lfa(struct vty *vty,
176 const struct isis_topology *topology,
177 const struct isis_test_node *root,
178 struct isis_area *area, struct lspdb_head *lspdb,
179 int level, int tree,
180 struct lfa_protected_resource *protected_resource)
181{
182 struct isis_spftree *spftree_self;
183 struct isis_spftree *spftree_reverse;
184 struct isis_spftree *spftree_pc;
185 struct isis_spf_node *spf_node, *node;
186 uint8_t flags;
187
188 /* Run forward SPF in the root node. */
189 flags = F_SPFTREE_NO_ADJACENCIES;
190 spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree,
191 SPF_TYPE_FORWARD, flags);
192 isis_run_spf(spftree_self);
193
194 /* Run reverse SPF in the root node. */
195 spftree_reverse = isis_spf_reverse_run(spftree_self);
196
197 /* Run forward SPF on all adjacent routers. */
198 isis_spf_run_neighbors(spftree_self);
199
200 /* Compute the TI-LFA repair paths. */
201 spftree_pc = isis_tilfa_compute(area, spftree_self, spftree_reverse,
202 protected_resource);
203
204 /* Print the extended P-space and Q-space. */
205 vty_out(vty, "P-space (self):\n");
206 RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.p_space)
207 vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
208 vty_out(vty, "\n");
209 RB_FOREACH (spf_node, isis_spf_nodes, &spftree_self->adj_nodes) {
210 if (RB_EMPTY(isis_spf_nodes, &spf_node->lfa.p_space))
211 continue;
212 vty_out(vty, "P-space (%s):\n",
213 print_sys_hostname(spf_node->sysid));
214 RB_FOREACH (node, isis_spf_nodes, &spf_node->lfa.p_space)
215 vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
216 vty_out(vty, "\n");
217 }
218 vty_out(vty, "Q-space:\n");
219 RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.q_space)
220 vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
221 vty_out(vty, "\n");
222
c8a4f331
RW
223 /*
224 * Print the post-convergence SPT and the corresponding routing table.
225 */
ca74d663 226 isis_print_spftree(vty, spftree_pc);
d47d6089 227 isis_print_routes(vty, spftree_self, false, true);
ca74d663
RW
228
229 /* Cleanup everything. */
230 isis_spftree_del(spftree_self);
231 isis_spftree_del(spftree_reverse);
232 isis_spftree_del(spftree_pc);
233}
234
52a7c25e
RW
235static int test_run(struct vty *vty, const struct isis_topology *topology,
236 const struct isis_test_node *root, enum test_type test_type,
ca74d663
RW
237 uint8_t flags, enum lfa_protection_type protection_type,
238 const char *fail_sysid_str, uint8_t fail_pseudonode_id)
52a7c25e
RW
239{
240 struct isis_area *area;
ca74d663
RW
241 struct lfa_protected_resource protected_resource = {};
242 uint8_t fail_id[ISIS_SYS_ID_LEN] = {};
52a7c25e
RW
243
244 /* Init topology. */
52a7c25e 245 area = isis_area_create("1", NULL);
5cfffcdd 246 memcpy(area->isis->sysid, root->sysid, sizeof(area->isis->sysid));
52a7c25e
RW
247 area->is_type = IS_LEVEL_1_AND_2;
248 area->srdb.enabled = true;
249 if (test_topology_load(topology, area, area->lspdb) != 0) {
250 vty_out(vty, "%% Failed to load topology\n");
251 return CMD_WARNING;
252 }
253
ca74d663
RW
254 /* Parse failed link/node. */
255 if (fail_sysid_str) {
256 if (sysid2buff(fail_id, fail_sysid_str) == 0) {
257 struct isis_dynhn *dynhn;
258
240f48b3 259 dynhn = dynhn_find_by_name(area->isis, fail_sysid_str);
ca74d663
RW
260 if (dynhn == NULL) {
261 vty_out(vty, "Invalid system id %s\n",
262 fail_sysid_str);
263 return CMD_WARNING;
264 }
265 memcpy(fail_id, dynhn->id, ISIS_SYS_ID_LEN);
266 }
267
268 protected_resource.type = protection_type;
269 memcpy(protected_resource.adjacency, fail_id, ISIS_SYS_ID_LEN);
270 LSP_PSEUDO_ID(protected_resource.adjacency) =
271 fail_pseudonode_id;
272 }
273
52a7c25e
RW
274 for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
275 if (level == IS_LEVEL_1 && CHECK_FLAG(flags, F_LEVEL2_ONLY))
276 continue;
277 if (level == IS_LEVEL_2 && CHECK_FLAG(flags, F_LEVEL1_ONLY))
278 continue;
279 if ((root->level & level) == 0)
280 continue;
281
282 /* Print the LDPDB. */
283 if (CHECK_FLAG(flags, F_DISPLAY_LSPDB))
432f1432 284 show_isis_database_lspdb_vty(vty, area, level - 1,
52a7c25e
RW
285 &area->lspdb[level - 1], NULL,
286 ISIS_UI_LEVEL_DETAIL);
287
288 for (int tree = SPFTREE_IPV4; tree <= SPFTREE_IPV6; tree++) {
289 if (tree == SPFTREE_IPV4
290 && CHECK_FLAG(flags, F_IPV6_ONLY))
291 continue;
292 if (tree == SPFTREE_IPV6
293 && CHECK_FLAG(flags, F_IPV4_ONLY))
294 continue;
295
296 switch (test_type) {
297 case TEST_SPF:
298 test_run_spf(vty, topology, root, area,
299 &area->lspdb[level - 1], level,
75aa7aa1
RW
300 tree, false);
301 break;
302 case TEST_REVERSE_SPF:
303 test_run_spf(vty, topology, root, area,
304 &area->lspdb[level - 1], level,
305 tree, true);
52a7c25e 306 break;
c8a4f331
RW
307 case TEST_LFA:
308 test_run_lfa(vty, topology, root, area,
309 &area->lspdb[level - 1], level,
310 tree, &protected_resource);
311 break;
c40de294
RW
312 case TEST_RLFA:
313 test_run_rlfa(vty, topology, root, area,
314 &area->lspdb[level - 1], level,
315 tree, &protected_resource);
316 break;
ca74d663
RW
317 case TEST_TI_LFA:
318 test_run_ti_lfa(vty, topology, root, area,
319 &area->lspdb[level - 1], level,
320 tree, &protected_resource);
321 break;
52a7c25e
RW
322 }
323 }
324 }
325
326 /* Cleanup IS-IS area. */
327 isis_area_destroy(area);
328
52a7c25e
RW
329 return CMD_SUCCESS;
330}
331
332DEFUN(test_isis, test_isis_cmd,
c8a4f331 333 "test isis topology (1-14) root HOSTNAME\
75aa7aa1
RW
334 <\
335 spf\
336 |reverse-spf\
c8a4f331 337 |lfa system-id WORD [pseudonode-id <1-255>]\
c40de294 338 |remote-lfa system-id WORD [pseudonode-id <1-255>]\
ca74d663 339 |ti-lfa system-id WORD [pseudonode-id <1-255>] [node-protection]\
75aa7aa1 340 >\
52a7c25e
RW
341 [display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]",
342 "Test command\n"
343 "IS-IS routing protocol\n"
344 "Test topology\n"
345 "Test topology number\n"
346 "SPF root\n"
347 "SPF root hostname\n"
348 "Normal Shortest Path First\n"
75aa7aa1 349 "Reverse Shortest Path First\n"
c8a4f331
RW
350 "Classic LFA\n"
351 "System ID\n"
352 "System ID\n"
353 "Pseudonode-ID\n"
354 "Pseudonode-ID\n"
c40de294
RW
355 "Remote LFA\n"
356 "System ID\n"
357 "System ID\n"
358 "Pseudonode-ID\n"
359 "Pseudonode-ID\n"
ca74d663
RW
360 "Topology Independent LFA\n"
361 "System ID\n"
362 "System ID\n"
363 "Pseudonode-ID\n"
364 "Pseudonode-ID\n"
365 "Node protection\n"
52a7c25e
RW
366 "Display the LSPDB\n"
367 "Do IPv4 processing only\n"
368 "Do IPv6 processing only\n"
369 "Skip L2 LSPs\n"
370 "Skip L1 LSPs\n")
371{
372 uint16_t topology_number;
373 const struct isis_topology *topology;
374 const struct isis_test_node *root;
75aa7aa1 375 enum test_type test_type;
ca74d663
RW
376 enum lfa_protection_type protection_type = 0;
377 const char *fail_sysid_str = NULL;
378 uint8_t fail_pseudonode_id = 0;
52a7c25e
RW
379 uint8_t flags = 0;
380 int idx = 0;
381
382 /* Load topology. */
383 argv_find(argv, argc, "topology", &idx);
384 topology_number = atoi(argv[idx + 1]->arg);
385 topology = test_topology_find(test_topologies, topology_number);
386 if (!topology) {
387 vty_out(vty, "%% Topology \"%s\" not found\n",
388 argv[idx + 1]->arg);
389 return CMD_WARNING;
390 }
391
392 /* Find root node. */
393 argv_find(argv, argc, "root", &idx);
394 root = test_topology_find_node(topology, argv[idx + 1]->arg, 0);
395 if (!root) {
396 vty_out(vty, "%% Node \"%s\" not found\n", argv[idx + 1]->arg);
397 return CMD_WARNING;
398 }
399
75aa7aa1
RW
400 /* Parse test information. */
401 if (argv_find(argv, argc, "spf", &idx))
402 test_type = TEST_SPF;
403 else if (argv_find(argv, argc, "reverse-spf", &idx))
404 test_type = TEST_REVERSE_SPF;
c8a4f331
RW
405 else if (argv_find(argv, argc, "lfa", &idx)) {
406 test_type = TEST_LFA;
407
c40de294
RW
408 fail_sysid_str = argv[idx + 2]->arg;
409 if (argv_find(argv, argc, "pseudonode-id", &idx))
410 fail_pseudonode_id =
411 strtoul(argv[idx + 1]->arg, NULL, 10);
412 protection_type = LFA_LINK_PROTECTION;
413 } else if (argv_find(argv, argc, "remote-lfa", &idx)) {
414 test_type = TEST_RLFA;
415
c8a4f331
RW
416 fail_sysid_str = argv[idx + 2]->arg;
417 if (argv_find(argv, argc, "pseudonode-id", &idx))
418 fail_pseudonode_id =
419 strtoul(argv[idx + 1]->arg, NULL, 10);
420 protection_type = LFA_LINK_PROTECTION;
421 } else if (argv_find(argv, argc, "ti-lfa", &idx)) {
ca74d663
RW
422 test_type = TEST_TI_LFA;
423
424 fail_sysid_str = argv[idx + 2]->arg;
425 if (argv_find(argv, argc, "pseudonode-id", &idx))
426 fail_pseudonode_id =
427 strtoul(argv[idx + 1]->arg, NULL, 10);
428 if (argv_find(argv, argc, "node-protection", &idx))
429 protection_type = LFA_NODE_PROTECTION;
430 else
431 protection_type = LFA_LINK_PROTECTION;
432 } else
75aa7aa1
RW
433 return CMD_WARNING;
434
52a7c25e
RW
435 /* Parse control flags. */
436 if (argv_find(argv, argc, "display-lspdb", &idx))
437 SET_FLAG(flags, F_DISPLAY_LSPDB);
438 if (argv_find(argv, argc, "ipv4-only", &idx))
439 SET_FLAG(flags, F_IPV4_ONLY);
440 else if (argv_find(argv, argc, "ipv6-only", &idx))
441 SET_FLAG(flags, F_IPV6_ONLY);
442 if (argv_find(argv, argc, "level-1-only", &idx))
443 SET_FLAG(flags, F_LEVEL1_ONLY);
444 else if (argv_find(argv, argc, "level-2-only", &idx))
445 SET_FLAG(flags, F_LEVEL2_ONLY);
446
ca74d663
RW
447 return test_run(vty, topology, root, test_type, flags, protection_type,
448 fail_sysid_str, fail_pseudonode_id);
52a7c25e
RW
449}
450
451static void vty_do_exit(int isexit)
452{
453 printf("\nend.\n");
454
52a7c25e
RW
455 cmd_terminate();
456 vty_terminate();
457 yang_terminate();
ce50d11c 458 event_master_free(master);
52a7c25e
RW
459
460 log_memstats(stderr, "test-isis-spf");
461 if (!isexit)
462 exit(0);
463}
464
465struct option longopts[] = {{"help", no_argument, NULL, 'h'},
466 {"debug", no_argument, NULL, 'd'},
467 {0}};
468
469/* Help information display. */
470static void usage(char *progname, int status)
471{
472 if (status != 0)
473 fprintf(stderr, "Try `%s --help' for more information.\n",
474 progname);
475 else {
476 printf("Usage : %s [OPTION...]\n\
477isisd SPF test program.\n\n\
478-u, --debug Enable debugging\n\
479-h, --help Display this help and exit\n\
480\n\
481Report bugs to %s\n",
482 progname, FRR_BUG_ADDRESS);
483 }
484 exit(status);
485}
486
487int main(int argc, char **argv)
488{
489 char *p;
490 char *progname;
e6685141 491 struct event thread;
52a7c25e
RW
492 bool debug = false;
493
494 /* Set umask before anything for security */
495 umask(0027);
496
497 /* get program name */
498 progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
499
500 while (1) {
501 int opt;
502
503 opt = getopt_long(argc, argv, "hd", longopts, 0);
504
505 if (opt == EOF)
506 break;
507
508 switch (opt) {
509 case 0:
510 break;
511 case 'd':
512 debug = true;
513 break;
514 case 'h':
515 usage(progname, 0);
516 break;
517 default:
518 usage(progname, 1);
519 break;
520 }
521 }
522
523 /* master init. */
ce50d11c 524 master = event_master_create(NULL);
52a7c25e
RW
525 isis_master_init(master);
526
527 /* Library inits. */
528 cmd_init(1);
529 cmd_hostname_set("test");
530 vty_init(master, false);
3bb513c3 531 yang_init(true, false);
52a7c25e
RW
532 if (debug)
533 zlog_aux_init("NONE: ", LOG_DEBUG);
534 else
535 zlog_aux_init("NONE: ", ZLOG_DISABLED);
536
537 /* IS-IS inits. */
538 yang_module_load("frr-isisd");
52a7c25e
RW
539 SET_FLAG(im->options, F_ISIS_UNIT_TEST);
540 debug_spf_events |= DEBUG_SPF_EVENTS;
c8a4f331 541 debug_lfa |= DEBUG_LFA;
52a7c25e
RW
542 debug_events |= DEBUG_EVENTS;
543 debug_rte_events |= DEBUG_RTE_EVENTS;
544
545 /* Install test command. */
546 install_element(VIEW_NODE, &test_isis_cmd);
547
548 /* Read input from .in file. */
549 vty_stdio(vty_do_exit);
550
551 /* Fetch next active thread. */
de2754be
DS
552 while (event_fetch(master, &thread))
553 event_call(&thread);
52a7c25e
RW
554
555 /* Not reached. */
556 exit(0);
557}