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 };
43 
44 #define F_DISPLAY_LSPDB 0x01
45 #define F_IPV4_ONLY 0x02
46 #define F_IPV6_ONLY 0x04
47 #define F_LEVEL1_ONLY 0x08
48 #define F_LEVEL2_ONLY 0x10
49 
50 static struct isis *isis;
51 
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)52 static void test_run_spf(struct vty *vty, const struct isis_topology *topology,
53 			 const struct isis_test_node *root,
54 			 struct isis_area *area, struct lspdb_head *lspdb,
55 			 int level, int tree, bool reverse)
56 {
57 	struct isis_spftree *spftree;
58 	enum spf_type spf_type;
59 
60 	/* Run SPF. */
61 	spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD;
62 	spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree,
63 				   spf_type, F_SPFTREE_NO_ADJACENCIES);
64 	isis_run_spf(spftree);
65 
66 	/* Print the SPT and the corresponding routing table. */
67 	isis_print_spftree(vty, spftree);
68 	isis_print_routes(vty, spftree);
69 
70 	/* Cleanup SPF tree. */
71 	isis_spftree_del(spftree);
72 }
73 
test_run(struct vty * vty,const struct isis_topology * topology,const struct isis_test_node * root,enum test_type test_type,uint8_t flags)74 static int test_run(struct vty *vty, const struct isis_topology *topology,
75 		    const struct isis_test_node *root, enum test_type test_type,
76 		    uint8_t flags)
77 {
78 	struct isis_area *area;
79 
80 	/* Init topology. */
81 	memcpy(isis->sysid, root->sysid, sizeof(isis->sysid));
82 	area = isis_area_create("1", NULL);
83 	area->is_type = IS_LEVEL_1_AND_2;
84 	area->srdb.enabled = true;
85 	if (test_topology_load(topology, area, area->lspdb) != 0) {
86 		vty_out(vty, "%% Failed to load topology\n");
87 		return CMD_WARNING;
88 	}
89 
90 	for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
91 		if (level == IS_LEVEL_1 && CHECK_FLAG(flags, F_LEVEL2_ONLY))
92 			continue;
93 		if (level == IS_LEVEL_2 && CHECK_FLAG(flags, F_LEVEL1_ONLY))
94 			continue;
95 		if ((root->level & level) == 0)
96 			continue;
97 
98 		/* Print the LDPDB. */
99 		if (CHECK_FLAG(flags, F_DISPLAY_LSPDB))
100 			show_isis_database_lspdb(vty, area, level - 1,
101 						 &area->lspdb[level - 1], NULL,
102 						 ISIS_UI_LEVEL_DETAIL);
103 
104 		for (int tree = SPFTREE_IPV4; tree <= SPFTREE_IPV6; tree++) {
105 			if (tree == SPFTREE_IPV4
106 			    && CHECK_FLAG(flags, F_IPV6_ONLY))
107 				continue;
108 			if (tree == SPFTREE_IPV6
109 			    && CHECK_FLAG(flags, F_IPV4_ONLY))
110 				continue;
111 
112 			switch (test_type) {
113 			case TEST_SPF:
114 				test_run_spf(vty, topology, root, area,
115 					     &area->lspdb[level - 1], level,
116 					     tree, false);
117 				break;
118 			case TEST_REVERSE_SPF:
119 				test_run_spf(vty, topology, root, area,
120 					     &area->lspdb[level - 1], level,
121 					     tree, true);
122 				break;
123 			}
124 		}
125 	}
126 
127 	/* Cleanup IS-IS area. */
128 	isis_area_destroy(area);
129 
130 	/* Cleanup hostnames. */
131 	dyn_cache_cleanup_all();
132 
133 	return CMD_SUCCESS;
134 }
135 
136 DEFUN(test_isis, test_isis_cmd,
137       "test isis topology (1-13) root HOSTNAME\
138          <\
139 	   spf\
140 	   |reverse-spf\
141 	 >\
142 	 [display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]",
143       "Test command\n"
144       "IS-IS routing protocol\n"
145       "Test topology\n"
146       "Test topology number\n"
147       "SPF root\n"
148       "SPF root hostname\n"
149       "Normal Shortest Path First\n"
150       "Reverse Shortest Path First\n"
151       "Display the LSPDB\n"
152       "Do IPv4 processing only\n"
153       "Do IPv6 processing only\n"
154       "Skip L2 LSPs\n"
155       "Skip L1 LSPs\n")
156 {
157 	uint16_t topology_number;
158 	const struct isis_topology *topology;
159 	const struct isis_test_node *root;
160 	enum test_type test_type;
161 	uint8_t flags = 0;
162 	int idx = 0;
163 
164 	/* Load topology. */
165 	argv_find(argv, argc, "topology", &idx);
166 	topology_number = atoi(argv[idx + 1]->arg);
167 	topology = test_topology_find(test_topologies, topology_number);
168 	if (!topology) {
169 		vty_out(vty, "%% Topology \"%s\" not found\n",
170 			argv[idx + 1]->arg);
171 		return CMD_WARNING;
172 	}
173 
174 	/* Find root node. */
175 	argv_find(argv, argc, "root", &idx);
176 	root = test_topology_find_node(topology, argv[idx + 1]->arg, 0);
177 	if (!root) {
178 		vty_out(vty, "%% Node \"%s\" not found\n", argv[idx + 1]->arg);
179 		return CMD_WARNING;
180 	}
181 
182 	/* Parse test information. */
183 	if (argv_find(argv, argc, "spf", &idx))
184 		test_type = TEST_SPF;
185 	else if (argv_find(argv, argc, "reverse-spf", &idx))
186 		test_type = TEST_REVERSE_SPF;
187 	else
188 		return CMD_WARNING;
189 
190 	/* Parse control flags. */
191 	if (argv_find(argv, argc, "display-lspdb", &idx))
192 		SET_FLAG(flags, F_DISPLAY_LSPDB);
193 	if (argv_find(argv, argc, "ipv4-only", &idx))
194 		SET_FLAG(flags, F_IPV4_ONLY);
195 	else if (argv_find(argv, argc, "ipv6-only", &idx))
196 		SET_FLAG(flags, F_IPV6_ONLY);
197 	if (argv_find(argv, argc, "level-1-only", &idx))
198 		SET_FLAG(flags, F_LEVEL1_ONLY);
199 	else if (argv_find(argv, argc, "level-2-only", &idx))
200 		SET_FLAG(flags, F_LEVEL2_ONLY);
201 
202 	return test_run(vty, topology, root, test_type, flags);
203 }
204 
vty_do_exit(int isexit)205 static void vty_do_exit(int isexit)
206 {
207 	printf("\nend.\n");
208 
209 	isis_finish(isis);
210 	cmd_terminate();
211 	vty_terminate();
212 	yang_terminate();
213 	thread_master_free(master);
214 
215 	log_memstats(stderr, "test-isis-spf");
216 	if (!isexit)
217 		exit(0);
218 }
219 
220 struct option longopts[] = {{"help", no_argument, NULL, 'h'},
221 			    {"debug", no_argument, NULL, 'd'},
222 			    {0}};
223 
224 /* Help information display. */
usage(char * progname,int status)225 static void usage(char *progname, int status)
226 {
227 	if (status != 0)
228 		fprintf(stderr, "Try `%s --help' for more information.\n",
229 			progname);
230 	else {
231 		printf("Usage : %s [OPTION...]\n\
232 isisd SPF test program.\n\n\
233 -u, --debug        Enable debugging\n\
234 -h, --help         Display this help and exit\n\
235 \n\
236 Report bugs to %s\n",
237 		       progname, FRR_BUG_ADDRESS);
238 	}
239 	exit(status);
240 }
241 
main(int argc,char ** argv)242 int main(int argc, char **argv)
243 {
244 	char *p;
245 	char *progname;
246 	struct thread thread;
247 	bool debug = false;
248 
249 	/* Set umask before anything for security */
250 	umask(0027);
251 
252 	/* get program name */
253 	progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
254 
255 	while (1) {
256 		int opt;
257 
258 		opt = getopt_long(argc, argv, "hd", longopts, 0);
259 
260 		if (opt == EOF)
261 			break;
262 
263 		switch (opt) {
264 		case 0:
265 			break;
266 		case 'd':
267 			debug = true;
268 			break;
269 		case 'h':
270 			usage(progname, 0);
271 			break;
272 		default:
273 			usage(progname, 1);
274 			break;
275 		}
276 	}
277 
278 	/* master init. */
279 	master = thread_master_create(NULL);
280 	isis_master_init(master);
281 
282 	/* Library inits. */
283 	cmd_init(1);
284 	cmd_hostname_set("test");
285 	vty_init(master, false);
286 	yang_init(true);
287 	if (debug)
288 		zlog_aux_init("NONE: ", LOG_DEBUG);
289 	else
290 		zlog_aux_init("NONE: ", ZLOG_DISABLED);
291 
292 	/* IS-IS inits. */
293 	yang_module_load("frr-isisd");
294 	isis = isis_new(VRF_DEFAULT_NAME);
295 	listnode_add(im->isis, isis);
296 	SET_FLAG(im->options, F_ISIS_UNIT_TEST);
297 	debug_spf_events |= DEBUG_SPF_EVENTS;
298 	debug_events |= DEBUG_EVENTS;
299 	debug_rte_events |= DEBUG_RTE_EVENTS;
300 
301 	/* Install test command. */
302 	install_element(VIEW_NODE, &test_isis_cmd);
303 
304 	/* Read input from .in file. */
305 	vty_stdio(vty_do_exit);
306 
307 	/* Fetch next active thread. */
308 	while (thread_fetch(master, &thread))
309 		thread_call(&thread);
310 
311 	/* Not reached. */
312 	exit(0);
313 }
314