]> git.proxmox.com Git - mirror_frr.git/blob - tests/isisd/test_isis_spf.c
Merge pull request #7276 from donaldsharp/speedup_isis_topotests
[mirror_frr.git] / tests / isisd / test_isis_spf.c
1 /*
2 * Copyright (C) 2020 NetDEF, Inc.
3 * Renato Westphal
4 *
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)
8 * any later version.
9 *
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
13 * more details.
14 *
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
18 */
19
20 #include <zebra.h>
21
22 #include <lib/version.h>
23 #include "getopt.h"
24 #include "thread.h"
25 #include "vty.h"
26 #include "command.h"
27 #include "log.h"
28 #include "vrf.h"
29 #include "yang.h"
30
31 #include "isisd/isisd.h"
32 #include "isisd/isis_dynhn.h"
33 #include "isisd/isis_misc.h"
34 #include "isisd/isis_spf.h"
35 #include "isisd/isis_spf_private.h"
36
37 #include "test_common.h"
38
39 enum test_type {
40 TEST_SPF = 1,
41 TEST_REVERSE_SPF,
42 TEST_TI_LFA,
43 };
44
45 #define F_DISPLAY_LSPDB 0x01
46 #define F_IPV4_ONLY 0x02
47 #define F_IPV6_ONLY 0x04
48 #define F_LEVEL1_ONLY 0x08
49 #define F_LEVEL2_ONLY 0x10
50
51 static struct isis *isis;
52
53 static void test_run_spf(struct vty *vty, const struct isis_topology *topology,
54 const struct isis_test_node *root,
55 struct isis_area *area, struct lspdb_head *lspdb,
56 int level, int tree, bool reverse)
57 {
58 struct isis_spftree *spftree;
59 enum spf_type spf_type;
60
61 /* Run SPF. */
62 spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD;
63 spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree,
64 spf_type, F_SPFTREE_NO_ADJACENCIES);
65 isis_run_spf(spftree);
66
67 /* Print the SPT and the corresponding routing table. */
68 isis_print_spftree(vty, spftree);
69 isis_print_routes(vty, spftree, false);
70
71 /* Cleanup SPF tree. */
72 isis_spftree_del(spftree);
73 }
74
75 static void test_run_ti_lfa(struct vty *vty,
76 const struct isis_topology *topology,
77 const struct isis_test_node *root,
78 struct isis_area *area, struct lspdb_head *lspdb,
79 int level, int tree,
80 struct lfa_protected_resource *protected_resource)
81 {
82 struct isis_spftree *spftree_self;
83 struct isis_spftree *spftree_reverse;
84 struct isis_spftree *spftree_pc;
85 struct isis_spf_node *spf_node, *node;
86 uint8_t flags;
87
88 /* Run forward SPF in the root node. */
89 flags = F_SPFTREE_NO_ADJACENCIES;
90 spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree,
91 SPF_TYPE_FORWARD, flags);
92 isis_run_spf(spftree_self);
93
94 /* Run reverse SPF in the root node. */
95 spftree_reverse = isis_spf_reverse_run(spftree_self);
96
97 /* Run forward SPF on all adjacent routers. */
98 isis_spf_run_neighbors(spftree_self);
99
100 /* Compute the TI-LFA repair paths. */
101 spftree_pc = isis_tilfa_compute(area, spftree_self, spftree_reverse,
102 protected_resource);
103
104 /* Print the extended P-space and Q-space. */
105 vty_out(vty, "P-space (self):\n");
106 RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.p_space)
107 vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
108 vty_out(vty, "\n");
109 RB_FOREACH (spf_node, isis_spf_nodes, &spftree_self->adj_nodes) {
110 if (RB_EMPTY(isis_spf_nodes, &spf_node->lfa.p_space))
111 continue;
112 vty_out(vty, "P-space (%s):\n",
113 print_sys_hostname(spf_node->sysid));
114 RB_FOREACH (node, isis_spf_nodes, &spf_node->lfa.p_space)
115 vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
116 vty_out(vty, "\n");
117 }
118 vty_out(vty, "Q-space:\n");
119 RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.q_space)
120 vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
121 vty_out(vty, "\n");
122
123 /* Print the post-convergence SPT and the correspoding routing table. */
124 isis_print_spftree(vty, spftree_pc);
125 isis_print_routes(vty, spftree_self, true);
126
127 /* Cleanup everything. */
128 isis_spftree_del(spftree_self);
129 isis_spftree_del(spftree_reverse);
130 isis_spftree_del(spftree_pc);
131 }
132
133 static int test_run(struct vty *vty, const struct isis_topology *topology,
134 const struct isis_test_node *root, enum test_type test_type,
135 uint8_t flags, enum lfa_protection_type protection_type,
136 const char *fail_sysid_str, uint8_t fail_pseudonode_id)
137 {
138 struct isis_area *area;
139 struct lfa_protected_resource protected_resource = {};
140 uint8_t fail_id[ISIS_SYS_ID_LEN] = {};
141
142 /* Init topology. */
143 memcpy(isis->sysid, root->sysid, sizeof(isis->sysid));
144 area = isis_area_create("1", NULL);
145 area->is_type = IS_LEVEL_1_AND_2;
146 area->srdb.enabled = true;
147 if (test_topology_load(topology, area, area->lspdb) != 0) {
148 vty_out(vty, "%% Failed to load topology\n");
149 return CMD_WARNING;
150 }
151
152 /* Parse failed link/node. */
153 if (fail_sysid_str) {
154 if (sysid2buff(fail_id, fail_sysid_str) == 0) {
155 struct isis_dynhn *dynhn;
156
157 dynhn = dynhn_find_by_name(fail_sysid_str);
158 if (dynhn == NULL) {
159 vty_out(vty, "Invalid system id %s\n",
160 fail_sysid_str);
161 return CMD_WARNING;
162 }
163 memcpy(fail_id, dynhn->id, ISIS_SYS_ID_LEN);
164 }
165
166 protected_resource.type = protection_type;
167 memcpy(protected_resource.adjacency, fail_id, ISIS_SYS_ID_LEN);
168 LSP_PSEUDO_ID(protected_resource.adjacency) =
169 fail_pseudonode_id;
170 }
171
172 for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
173 if (level == IS_LEVEL_1 && CHECK_FLAG(flags, F_LEVEL2_ONLY))
174 continue;
175 if (level == IS_LEVEL_2 && CHECK_FLAG(flags, F_LEVEL1_ONLY))
176 continue;
177 if ((root->level & level) == 0)
178 continue;
179
180 /* Print the LDPDB. */
181 if (CHECK_FLAG(flags, F_DISPLAY_LSPDB))
182 show_isis_database_lspdb(vty, area, level - 1,
183 &area->lspdb[level - 1], NULL,
184 ISIS_UI_LEVEL_DETAIL);
185
186 for (int tree = SPFTREE_IPV4; tree <= SPFTREE_IPV6; tree++) {
187 if (tree == SPFTREE_IPV4
188 && CHECK_FLAG(flags, F_IPV6_ONLY))
189 continue;
190 if (tree == SPFTREE_IPV6
191 && CHECK_FLAG(flags, F_IPV4_ONLY))
192 continue;
193
194 switch (test_type) {
195 case TEST_SPF:
196 test_run_spf(vty, topology, root, area,
197 &area->lspdb[level - 1], level,
198 tree, false);
199 break;
200 case TEST_REVERSE_SPF:
201 test_run_spf(vty, topology, root, area,
202 &area->lspdb[level - 1], level,
203 tree, true);
204 break;
205 case TEST_TI_LFA:
206 test_run_ti_lfa(vty, topology, root, area,
207 &area->lspdb[level - 1], level,
208 tree, &protected_resource);
209 break;
210 }
211 }
212 }
213
214 /* Cleanup IS-IS area. */
215 isis_area_destroy(area);
216
217 /* Cleanup hostnames. */
218 dyn_cache_cleanup_all();
219
220 return CMD_SUCCESS;
221 }
222
223 DEFUN(test_isis, test_isis_cmd,
224 "test isis topology (1-13) root HOSTNAME\
225 <\
226 spf\
227 |reverse-spf\
228 |ti-lfa system-id WORD [pseudonode-id <1-255>] [node-protection]\
229 >\
230 [display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]",
231 "Test command\n"
232 "IS-IS routing protocol\n"
233 "Test topology\n"
234 "Test topology number\n"
235 "SPF root\n"
236 "SPF root hostname\n"
237 "Normal Shortest Path First\n"
238 "Reverse Shortest Path First\n"
239 "Topology Independent LFA\n"
240 "System ID\n"
241 "System ID\n"
242 "Pseudonode-ID\n"
243 "Pseudonode-ID\n"
244 "Node protection\n"
245 "Display the LSPDB\n"
246 "Do IPv4 processing only\n"
247 "Do IPv6 processing only\n"
248 "Skip L2 LSPs\n"
249 "Skip L1 LSPs\n")
250 {
251 uint16_t topology_number;
252 const struct isis_topology *topology;
253 const struct isis_test_node *root;
254 enum test_type test_type;
255 enum lfa_protection_type protection_type = 0;
256 const char *fail_sysid_str = NULL;
257 uint8_t fail_pseudonode_id = 0;
258 uint8_t flags = 0;
259 int idx = 0;
260
261 /* Load topology. */
262 argv_find(argv, argc, "topology", &idx);
263 topology_number = atoi(argv[idx + 1]->arg);
264 topology = test_topology_find(test_topologies, topology_number);
265 if (!topology) {
266 vty_out(vty, "%% Topology \"%s\" not found\n",
267 argv[idx + 1]->arg);
268 return CMD_WARNING;
269 }
270
271 /* Find root node. */
272 argv_find(argv, argc, "root", &idx);
273 root = test_topology_find_node(topology, argv[idx + 1]->arg, 0);
274 if (!root) {
275 vty_out(vty, "%% Node \"%s\" not found\n", argv[idx + 1]->arg);
276 return CMD_WARNING;
277 }
278
279 /* Parse test information. */
280 if (argv_find(argv, argc, "spf", &idx))
281 test_type = TEST_SPF;
282 else if (argv_find(argv, argc, "reverse-spf", &idx))
283 test_type = TEST_REVERSE_SPF;
284 else if (argv_find(argv, argc, "ti-lfa", &idx)) {
285 test_type = TEST_TI_LFA;
286
287 fail_sysid_str = argv[idx + 2]->arg;
288 if (argv_find(argv, argc, "pseudonode-id", &idx))
289 fail_pseudonode_id =
290 strtoul(argv[idx + 1]->arg, NULL, 10);
291 if (argv_find(argv, argc, "node-protection", &idx))
292 protection_type = LFA_NODE_PROTECTION;
293 else
294 protection_type = LFA_LINK_PROTECTION;
295 } else
296 return CMD_WARNING;
297
298 /* Parse control flags. */
299 if (argv_find(argv, argc, "display-lspdb", &idx))
300 SET_FLAG(flags, F_DISPLAY_LSPDB);
301 if (argv_find(argv, argc, "ipv4-only", &idx))
302 SET_FLAG(flags, F_IPV4_ONLY);
303 else if (argv_find(argv, argc, "ipv6-only", &idx))
304 SET_FLAG(flags, F_IPV6_ONLY);
305 if (argv_find(argv, argc, "level-1-only", &idx))
306 SET_FLAG(flags, F_LEVEL1_ONLY);
307 else if (argv_find(argv, argc, "level-2-only", &idx))
308 SET_FLAG(flags, F_LEVEL2_ONLY);
309
310 return test_run(vty, topology, root, test_type, flags, protection_type,
311 fail_sysid_str, fail_pseudonode_id);
312 }
313
314 static void vty_do_exit(int isexit)
315 {
316 printf("\nend.\n");
317
318 isis_finish(isis);
319 cmd_terminate();
320 vty_terminate();
321 yang_terminate();
322 thread_master_free(master);
323
324 log_memstats(stderr, "test-isis-spf");
325 if (!isexit)
326 exit(0);
327 }
328
329 struct option longopts[] = {{"help", no_argument, NULL, 'h'},
330 {"debug", no_argument, NULL, 'd'},
331 {0}};
332
333 /* Help information display. */
334 static void usage(char *progname, int status)
335 {
336 if (status != 0)
337 fprintf(stderr, "Try `%s --help' for more information.\n",
338 progname);
339 else {
340 printf("Usage : %s [OPTION...]\n\
341 isisd SPF test program.\n\n\
342 -u, --debug Enable debugging\n\
343 -h, --help Display this help and exit\n\
344 \n\
345 Report bugs to %s\n",
346 progname, FRR_BUG_ADDRESS);
347 }
348 exit(status);
349 }
350
351 int main(int argc, char **argv)
352 {
353 char *p;
354 char *progname;
355 struct thread thread;
356 bool debug = false;
357
358 /* Set umask before anything for security */
359 umask(0027);
360
361 /* get program name */
362 progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
363
364 while (1) {
365 int opt;
366
367 opt = getopt_long(argc, argv, "hd", longopts, 0);
368
369 if (opt == EOF)
370 break;
371
372 switch (opt) {
373 case 0:
374 break;
375 case 'd':
376 debug = true;
377 break;
378 case 'h':
379 usage(progname, 0);
380 break;
381 default:
382 usage(progname, 1);
383 break;
384 }
385 }
386
387 /* master init. */
388 master = thread_master_create(NULL);
389 isis_master_init(master);
390
391 /* Library inits. */
392 cmd_init(1);
393 cmd_hostname_set("test");
394 vty_init(master, false);
395 yang_init(true);
396 if (debug)
397 zlog_aux_init("NONE: ", LOG_DEBUG);
398 else
399 zlog_aux_init("NONE: ", ZLOG_DISABLED);
400
401 /* IS-IS inits. */
402 yang_module_load("frr-isisd");
403 isis = isis_new(VRF_DEFAULT_NAME);
404 listnode_add(im->isis, isis);
405 SET_FLAG(im->options, F_ISIS_UNIT_TEST);
406 debug_spf_events |= DEBUG_SPF_EVENTS;
407 debug_tilfa |= DEBUG_TILFA;
408 debug_events |= DEBUG_EVENTS;
409 debug_rte_events |= DEBUG_RTE_EVENTS;
410
411 /* Install test command. */
412 install_element(VIEW_NODE, &test_isis_cmd);
413
414 /* Read input from .in file. */
415 vty_stdio(vty_do_exit);
416
417 /* Fetch next active thread. */
418 while (thread_fetch(master, &thread))
419 thread_call(&thread);
420
421 /* Not reached. */
422 exit(0);
423 }