2 * Copyright (C) 2020 NetDEF, Inc.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include <lib/version.h>
31 #include "isisd/isisd.h"
32 #include "isisd/isis_dynhn.h"
33 #include "isisd/isis_misc.h"
34 #include "isisd/isis_route.h"
35 #include "isisd/isis_spf.h"
36 #include "isisd/isis_spf_private.h"
38 #include "test_common.h"
48 #define F_DISPLAY_LSPDB 0x01
49 #define F_IPV4_ONLY 0x02
50 #define F_IPV6_ONLY 0x04
51 #define F_LEVEL1_ONLY 0x08
52 #define F_LEVEL2_ONLY 0x10
54 static void test_run_spf(struct vty
*vty
, const struct isis_topology
*topology
,
55 const struct isis_test_node
*root
,
56 struct isis_area
*area
, struct lspdb_head
*lspdb
,
57 int level
, int tree
, bool reverse
)
59 struct isis_spftree
*spftree
;
60 enum spf_type spf_type
;
63 spf_type
= reverse
? SPF_TYPE_REVERSE
: SPF_TYPE_FORWARD
;
64 spftree
= isis_spftree_new(area
, lspdb
, root
->sysid
, level
, tree
,
65 spf_type
, F_SPFTREE_NO_ADJACENCIES
);
66 isis_run_spf(spftree
);
68 /* Print the SPT and the corresponding routing table. */
69 isis_print_spftree(vty
, spftree
);
70 isis_print_routes(vty
, spftree
, false, false);
72 /* Cleanup SPF tree. */
73 isis_spftree_del(spftree
);
76 static void test_run_lfa(struct vty
*vty
, const struct isis_topology
*topology
,
77 const struct isis_test_node
*root
,
78 struct isis_area
*area
, struct lspdb_head
*lspdb
,
80 struct lfa_protected_resource
*protected_resource
)
82 struct isis_spftree
*spftree_self
;
85 /* Run forward SPF in the root node. */
86 flags
= F_SPFTREE_NO_ADJACENCIES
;
87 spftree_self
= isis_spftree_new(area
, lspdb
, root
->sysid
, level
, tree
,
88 SPF_TYPE_FORWARD
, flags
);
89 isis_run_spf(spftree_self
);
91 /* Run forward SPF on all adjacent routers. */
92 isis_spf_run_neighbors(spftree_self
);
94 /* Compute the LFA repair paths. */
95 isis_lfa_compute(area
, NULL
, spftree_self
, protected_resource
);
97 /* Print the SPT and the corresponding main/backup routing tables. */
98 isis_print_spftree(vty
, spftree_self
);
99 vty_out(vty
, "Main:\n");
100 isis_print_routes(vty
, spftree_self
, false, false);
101 vty_out(vty
, "Backup:\n");
102 isis_print_routes(vty
, spftree_self
, false, true);
104 /* Cleanup everything. */
105 isis_spftree_del(spftree_self
);
108 static void test_run_rlfa(struct vty
*vty
, const struct isis_topology
*topology
,
109 const struct isis_test_node
*root
,
110 struct isis_area
*area
, struct lspdb_head
*lspdb
,
112 struct lfa_protected_resource
*protected_resource
)
114 struct isis_spftree
*spftree_self
;
115 struct isis_spftree
*spftree_reverse
;
116 struct isis_spftree
*spftree_pc
;
117 struct isis_spf_node
*spf_node
, *node
;
121 /* Run forward SPF in the root node. */
122 flags
= F_SPFTREE_NO_ADJACENCIES
;
123 spftree_self
= isis_spftree_new(area
, lspdb
, root
->sysid
, level
, tree
,
124 SPF_TYPE_FORWARD
, flags
);
125 isis_run_spf(spftree_self
);
127 /* Run reverse SPF in the root node. */
128 spftree_reverse
= isis_spf_reverse_run(spftree_self
);
130 /* Run forward SPF on all adjacent routers. */
131 isis_spf_run_neighbors(spftree_self
);
133 /* Compute the local LFA repair paths. */
134 isis_lfa_compute(area
, NULL
, spftree_self
, protected_resource
);
136 /* Compute the remote LFA repair paths. */
137 spftree_pc
= isis_rlfa_compute(area
, spftree_self
, spftree_reverse
, 0,
140 /* Print the extended P-space and Q-space. */
141 vty_out(vty
, "P-space (self):\n");
142 RB_FOREACH (node
, isis_spf_nodes
, &spftree_pc
->lfa
.p_space
)
143 vty_out(vty
, " %s\n", print_sys_hostname(node
->sysid
));
145 RB_FOREACH (spf_node
, isis_spf_nodes
, &spftree_self
->adj_nodes
) {
146 if (RB_EMPTY(isis_spf_nodes
, &spf_node
->lfa
.p_space
))
148 vty_out(vty
, "P-space (%s):\n",
149 print_sys_hostname(spf_node
->sysid
));
150 RB_FOREACH (node
, isis_spf_nodes
, &spf_node
->lfa
.p_space
)
151 vty_out(vty
, " %s\n", print_sys_hostname(node
->sysid
));
154 vty_out(vty
, "Q-space:\n");
155 RB_FOREACH (node
, isis_spf_nodes
, &spftree_pc
->lfa
.q_space
)
156 vty_out(vty
, " %s\n", print_sys_hostname(node
->sysid
));
159 /* Print the post-convergence SPT. */
160 isis_print_spftree(vty
, spftree_pc
);
163 * Activate the computed RLFAs (if any) using artificial LDP labels for
166 frr_each_safe (rlfa_tree
, &spftree_self
->lfa
.remote
.rlfas
, rlfa
) {
167 struct zapi_rlfa_response response
= {};
169 response
.pq_label
= test_topology_node_ldp_label(
170 topology
, rlfa
->pq_address
);
171 assert(response
.pq_label
!= MPLS_INVALID_LABEL
);
172 isis_rlfa_activate(spftree_self
, rlfa
, &response
);
175 /* Print the SPT and the corresponding main/backup routing tables. */
176 isis_print_spftree(vty
, spftree_self
);
177 vty_out(vty
, "Main:\n");
178 isis_print_routes(vty
, spftree_self
, false, false);
179 vty_out(vty
, "Backup:\n");
180 isis_print_routes(vty
, spftree_self
, false, true);
182 /* Cleanup everything. */
183 isis_spftree_del(spftree_self
);
184 isis_spftree_del(spftree_reverse
);
185 isis_spftree_del(spftree_pc
);
188 static void test_run_ti_lfa(struct vty
*vty
,
189 const struct isis_topology
*topology
,
190 const struct isis_test_node
*root
,
191 struct isis_area
*area
, struct lspdb_head
*lspdb
,
193 struct lfa_protected_resource
*protected_resource
)
195 struct isis_spftree
*spftree_self
;
196 struct isis_spftree
*spftree_reverse
;
197 struct isis_spftree
*spftree_pc
;
198 struct isis_spf_node
*spf_node
, *node
;
201 /* Run forward SPF in the root node. */
202 flags
= F_SPFTREE_NO_ADJACENCIES
;
203 spftree_self
= isis_spftree_new(area
, lspdb
, root
->sysid
, level
, tree
,
204 SPF_TYPE_FORWARD
, flags
);
205 isis_run_spf(spftree_self
);
207 /* Run reverse SPF in the root node. */
208 spftree_reverse
= isis_spf_reverse_run(spftree_self
);
210 /* Run forward SPF on all adjacent routers. */
211 isis_spf_run_neighbors(spftree_self
);
213 /* Compute the TI-LFA repair paths. */
214 spftree_pc
= isis_tilfa_compute(area
, spftree_self
, spftree_reverse
,
217 /* Print the extended P-space and Q-space. */
218 vty_out(vty
, "P-space (self):\n");
219 RB_FOREACH (node
, isis_spf_nodes
, &spftree_pc
->lfa
.p_space
)
220 vty_out(vty
, " %s\n", print_sys_hostname(node
->sysid
));
222 RB_FOREACH (spf_node
, isis_spf_nodes
, &spftree_self
->adj_nodes
) {
223 if (RB_EMPTY(isis_spf_nodes
, &spf_node
->lfa
.p_space
))
225 vty_out(vty
, "P-space (%s):\n",
226 print_sys_hostname(spf_node
->sysid
));
227 RB_FOREACH (node
, isis_spf_nodes
, &spf_node
->lfa
.p_space
)
228 vty_out(vty
, " %s\n", print_sys_hostname(node
->sysid
));
231 vty_out(vty
, "Q-space:\n");
232 RB_FOREACH (node
, isis_spf_nodes
, &spftree_pc
->lfa
.q_space
)
233 vty_out(vty
, " %s\n", print_sys_hostname(node
->sysid
));
237 * Print the post-convergence SPT and the corresponding routing table.
239 isis_print_spftree(vty
, spftree_pc
);
240 isis_print_routes(vty
, spftree_self
, false, true);
242 /* Cleanup everything. */
243 isis_spftree_del(spftree_self
);
244 isis_spftree_del(spftree_reverse
);
245 isis_spftree_del(spftree_pc
);
248 static int test_run(struct vty
*vty
, const struct isis_topology
*topology
,
249 const struct isis_test_node
*root
, enum test_type test_type
,
250 uint8_t flags
, enum lfa_protection_type protection_type
,
251 const char *fail_sysid_str
, uint8_t fail_pseudonode_id
)
253 struct isis_area
*area
;
254 struct lfa_protected_resource protected_resource
= {};
255 uint8_t fail_id
[ISIS_SYS_ID_LEN
] = {};
258 area
= isis_area_create("1", NULL
);
259 memcpy(area
->isis
->sysid
, root
->sysid
, sizeof(area
->isis
->sysid
));
260 area
->is_type
= IS_LEVEL_1_AND_2
;
261 area
->srdb
.enabled
= true;
262 if (test_topology_load(topology
, area
, area
->lspdb
) != 0) {
263 vty_out(vty
, "%% Failed to load topology\n");
267 /* Parse failed link/node. */
268 if (fail_sysid_str
) {
269 if (sysid2buff(fail_id
, fail_sysid_str
) == 0) {
270 struct isis_dynhn
*dynhn
;
272 dynhn
= dynhn_find_by_name(area
->isis
, fail_sysid_str
);
274 vty_out(vty
, "Invalid system id %s\n",
278 memcpy(fail_id
, dynhn
->id
, ISIS_SYS_ID_LEN
);
281 protected_resource
.type
= protection_type
;
282 memcpy(protected_resource
.adjacency
, fail_id
, ISIS_SYS_ID_LEN
);
283 LSP_PSEUDO_ID(protected_resource
.adjacency
) =
287 for (int level
= IS_LEVEL_1
; level
<= IS_LEVEL_2
; level
++) {
288 if (level
== IS_LEVEL_1
&& CHECK_FLAG(flags
, F_LEVEL2_ONLY
))
290 if (level
== IS_LEVEL_2
&& CHECK_FLAG(flags
, F_LEVEL1_ONLY
))
292 if ((root
->level
& level
) == 0)
295 /* Print the LDPDB. */
296 if (CHECK_FLAG(flags
, F_DISPLAY_LSPDB
))
297 show_isis_database_lspdb_vty(vty
, area
, level
- 1,
298 &area
->lspdb
[level
- 1], NULL
,
299 ISIS_UI_LEVEL_DETAIL
);
301 for (int tree
= SPFTREE_IPV4
; tree
<= SPFTREE_IPV6
; tree
++) {
302 if (tree
== SPFTREE_IPV4
303 && CHECK_FLAG(flags
, F_IPV6_ONLY
))
305 if (tree
== SPFTREE_IPV6
306 && CHECK_FLAG(flags
, F_IPV4_ONLY
))
311 test_run_spf(vty
, topology
, root
, area
,
312 &area
->lspdb
[level
- 1], level
,
315 case TEST_REVERSE_SPF
:
316 test_run_spf(vty
, topology
, root
, area
,
317 &area
->lspdb
[level
- 1], level
,
321 test_run_lfa(vty
, topology
, root
, area
,
322 &area
->lspdb
[level
- 1], level
,
323 tree
, &protected_resource
);
326 test_run_rlfa(vty
, topology
, root
, area
,
327 &area
->lspdb
[level
- 1], level
,
328 tree
, &protected_resource
);
331 test_run_ti_lfa(vty
, topology
, root
, area
,
332 &area
->lspdb
[level
- 1], level
,
333 tree
, &protected_resource
);
339 /* Cleanup IS-IS area. */
340 isis_area_destroy(area
);
345 DEFUN(test_isis
, test_isis_cmd
,
346 "test isis topology (1-14) root HOSTNAME\
350 |lfa system-id WORD [pseudonode-id <1-255>]\
351 |remote-lfa system-id WORD [pseudonode-id <1-255>]\
352 |ti-lfa system-id WORD [pseudonode-id <1-255>] [node-protection]\
354 [display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]",
356 "IS-IS routing protocol\n"
358 "Test topology number\n"
360 "SPF root hostname\n"
361 "Normal Shortest Path First\n"
362 "Reverse Shortest Path First\n"
373 "Topology Independent LFA\n"
379 "Display the LSPDB\n"
380 "Do IPv4 processing only\n"
381 "Do IPv6 processing only\n"
385 uint16_t topology_number
;
386 const struct isis_topology
*topology
;
387 const struct isis_test_node
*root
;
388 enum test_type test_type
;
389 enum lfa_protection_type protection_type
= 0;
390 const char *fail_sysid_str
= NULL
;
391 uint8_t fail_pseudonode_id
= 0;
396 argv_find(argv
, argc
, "topology", &idx
);
397 topology_number
= atoi(argv
[idx
+ 1]->arg
);
398 topology
= test_topology_find(test_topologies
, topology_number
);
400 vty_out(vty
, "%% Topology \"%s\" not found\n",
405 /* Find root node. */
406 argv_find(argv
, argc
, "root", &idx
);
407 root
= test_topology_find_node(topology
, argv
[idx
+ 1]->arg
, 0);
409 vty_out(vty
, "%% Node \"%s\" not found\n", argv
[idx
+ 1]->arg
);
413 /* Parse test information. */
414 if (argv_find(argv
, argc
, "spf", &idx
))
415 test_type
= TEST_SPF
;
416 else if (argv_find(argv
, argc
, "reverse-spf", &idx
))
417 test_type
= TEST_REVERSE_SPF
;
418 else if (argv_find(argv
, argc
, "lfa", &idx
)) {
419 test_type
= TEST_LFA
;
421 fail_sysid_str
= argv
[idx
+ 2]->arg
;
422 if (argv_find(argv
, argc
, "pseudonode-id", &idx
))
424 strtoul(argv
[idx
+ 1]->arg
, NULL
, 10);
425 protection_type
= LFA_LINK_PROTECTION
;
426 } else if (argv_find(argv
, argc
, "remote-lfa", &idx
)) {
427 test_type
= TEST_RLFA
;
429 fail_sysid_str
= argv
[idx
+ 2]->arg
;
430 if (argv_find(argv
, argc
, "pseudonode-id", &idx
))
432 strtoul(argv
[idx
+ 1]->arg
, NULL
, 10);
433 protection_type
= LFA_LINK_PROTECTION
;
434 } else if (argv_find(argv
, argc
, "ti-lfa", &idx
)) {
435 test_type
= TEST_TI_LFA
;
437 fail_sysid_str
= argv
[idx
+ 2]->arg
;
438 if (argv_find(argv
, argc
, "pseudonode-id", &idx
))
440 strtoul(argv
[idx
+ 1]->arg
, NULL
, 10);
441 if (argv_find(argv
, argc
, "node-protection", &idx
))
442 protection_type
= LFA_NODE_PROTECTION
;
444 protection_type
= LFA_LINK_PROTECTION
;
448 /* Parse control flags. */
449 if (argv_find(argv
, argc
, "display-lspdb", &idx
))
450 SET_FLAG(flags
, F_DISPLAY_LSPDB
);
451 if (argv_find(argv
, argc
, "ipv4-only", &idx
))
452 SET_FLAG(flags
, F_IPV4_ONLY
);
453 else if (argv_find(argv
, argc
, "ipv6-only", &idx
))
454 SET_FLAG(flags
, F_IPV6_ONLY
);
455 if (argv_find(argv
, argc
, "level-1-only", &idx
))
456 SET_FLAG(flags
, F_LEVEL1_ONLY
);
457 else if (argv_find(argv
, argc
, "level-2-only", &idx
))
458 SET_FLAG(flags
, F_LEVEL2_ONLY
);
460 return test_run(vty
, topology
, root
, test_type
, flags
, protection_type
,
461 fail_sysid_str
, fail_pseudonode_id
);
464 static void vty_do_exit(int isexit
)
471 thread_master_free(master
);
473 log_memstats(stderr
, "test-isis-spf");
478 struct option longopts
[] = {{"help", no_argument
, NULL
, 'h'},
479 {"debug", no_argument
, NULL
, 'd'},
482 /* Help information display. */
483 static void usage(char *progname
, int status
)
486 fprintf(stderr
, "Try `%s --help' for more information.\n",
489 printf("Usage : %s [OPTION...]\n\
490 isisd SPF test program.\n\n\
491 -u, --debug Enable debugging\n\
492 -h, --help Display this help and exit\n\
494 Report bugs to %s\n",
495 progname
, FRR_BUG_ADDRESS
);
500 int main(int argc
, char **argv
)
504 struct thread thread
;
507 /* Set umask before anything for security */
510 /* get program name */
511 progname
= ((p
= strrchr(argv
[0], '/')) ? ++p
: argv
[0]);
516 opt
= getopt_long(argc
, argv
, "hd", longopts
, 0);
537 master
= thread_master_create(NULL
);
538 isis_master_init(master
);
542 cmd_hostname_set("test");
543 vty_init(master
, false);
544 yang_init(true, false);
546 zlog_aux_init("NONE: ", LOG_DEBUG
);
548 zlog_aux_init("NONE: ", ZLOG_DISABLED
);
551 yang_module_load("frr-isisd");
552 SET_FLAG(im
->options
, F_ISIS_UNIT_TEST
);
553 debug_spf_events
|= DEBUG_SPF_EVENTS
;
554 debug_lfa
|= DEBUG_LFA
;
555 debug_events
|= DEBUG_EVENTS
;
556 debug_rte_events
|= DEBUG_RTE_EVENTS
;
558 /* Install test command. */
559 install_element(VIEW_NODE
, &test_isis_cmd
);
561 /* Read input from .in file. */
562 vty_stdio(vty_do_exit
);
564 /* Fetch next active thread. */
565 while (thread_fetch(master
, &thread
))
566 thread_call(&thread
);