]>
Commit | Line | Data |
---|---|---|
52a7c25e RW |
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, | |
75aa7aa1 | 41 | TEST_REVERSE_SPF, |
ca74d663 | 42 | TEST_TI_LFA, |
52a7c25e RW |
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, | |
75aa7aa1 | 56 | int level, int tree, bool reverse) |
52a7c25e RW |
57 | { |
58 | struct isis_spftree *spftree; | |
75aa7aa1 | 59 | enum spf_type spf_type; |
52a7c25e RW |
60 | |
61 | /* Run SPF. */ | |
75aa7aa1 | 62 | spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD; |
52a7c25e | 63 | spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree, |
75aa7aa1 | 64 | spf_type, F_SPFTREE_NO_ADJACENCIES); |
52a7c25e RW |
65 | isis_run_spf(spftree); |
66 | ||
67 | /* Print the SPT and the corresponding routing table. */ | |
68 | isis_print_spftree(vty, spftree); | |
d47d6089 | 69 | isis_print_routes(vty, spftree, false, false); |
52a7c25e RW |
70 | |
71 | /* Cleanup SPF tree. */ | |
72 | isis_spftree_del(spftree); | |
73 | } | |
74 | ||
ca74d663 RW |
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); | |
d47d6089 | 125 | isis_print_routes(vty, spftree_self, false, true); |
ca74d663 RW |
126 | |
127 | /* Cleanup everything. */ | |
128 | isis_spftree_del(spftree_self); | |
129 | isis_spftree_del(spftree_reverse); | |
130 | isis_spftree_del(spftree_pc); | |
131 | } | |
132 | ||
52a7c25e RW |
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, | |
ca74d663 RW |
135 | uint8_t flags, enum lfa_protection_type protection_type, |
136 | const char *fail_sysid_str, uint8_t fail_pseudonode_id) | |
52a7c25e RW |
137 | { |
138 | struct isis_area *area; | |
ca74d663 RW |
139 | struct lfa_protected_resource protected_resource = {}; |
140 | uint8_t fail_id[ISIS_SYS_ID_LEN] = {}; | |
52a7c25e RW |
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 | ||
ca74d663 RW |
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 | ||
52a7c25e RW |
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, | |
75aa7aa1 RW |
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); | |
52a7c25e | 204 | break; |
ca74d663 RW |
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; | |
52a7c25e RW |
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, | |
75aa7aa1 RW |
224 | "test isis topology (1-13) root HOSTNAME\ |
225 | <\ | |
226 | spf\ | |
227 | |reverse-spf\ | |
ca74d663 | 228 | |ti-lfa system-id WORD [pseudonode-id <1-255>] [node-protection]\ |
75aa7aa1 | 229 | >\ |
52a7c25e RW |
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" | |
75aa7aa1 | 238 | "Reverse Shortest Path First\n" |
ca74d663 RW |
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" | |
52a7c25e RW |
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; | |
75aa7aa1 | 254 | enum test_type test_type; |
ca74d663 RW |
255 | enum lfa_protection_type protection_type = 0; |
256 | const char *fail_sysid_str = NULL; | |
257 | uint8_t fail_pseudonode_id = 0; | |
52a7c25e RW |
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 | ||
75aa7aa1 RW |
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; | |
ca74d663 RW |
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 | |
75aa7aa1 RW |
296 | return CMD_WARNING; |
297 | ||
52a7c25e RW |
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 | ||
ca74d663 RW |
310 | return test_run(vty, topology, root, test_type, flags, protection_type, |
311 | fail_sysid_str, fail_pseudonode_id); | |
52a7c25e RW |
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"); | |
65251ce8 | 403 | isis = isis_new(VRF_DEFAULT_NAME); |
52a7c25e RW |
404 | listnode_add(im->isis, isis); |
405 | SET_FLAG(im->options, F_ISIS_UNIT_TEST); | |
406 | debug_spf_events |= DEBUG_SPF_EVENTS; | |
ca74d663 | 407 | debug_tilfa |= DEBUG_TILFA; |
52a7c25e RW |
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 | } |