]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
8f399b0e CF |
2 | /* |
3 | * Test code for lib/command.c | |
4 | * | |
5 | * Copyright (C) 2013 by Open Source Routing. | |
6 | * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") | |
7 | * | |
8 | * This program reads in a list of commandlines from stdin | |
9 | * and calls all the public functions of lib/command.c for | |
10 | * both the given command lines and fuzzed versions thereof. | |
11 | * | |
12 | * The output is currently not validated but only logged. It can | |
13 | * be diffed to find regressions between versions. | |
8f399b0e CF |
14 | */ |
15 | ||
16 | #define REALLY_NEED_PLAIN_GETOPT 1 | |
17 | ||
18 | #include <zebra.h> | |
19 | ||
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | #include <unistd.h> | |
23 | ||
24 | #include "command.h" | |
25 | #include "memory.h" | |
26 | #include "vector.h" | |
27 | #include "prng.h" | |
28 | ||
29 | extern vector cmdvec; | |
30 | extern struct cmd_node vty_node; | |
31 | extern void test_init_cmd(void); /* provided in test-commands-defun.c */ | |
32 | ||
55c72803 | 33 | struct thread_master *master; /* dummy for libfrr*/ |
8f399b0e CF |
34 | |
35 | static vector test_cmds; | |
36 | static char test_buf[32768]; | |
37 | ||
d62a17ae | 38 | static struct cmd_node bgp_node = { |
f4b8291f | 39 | .name = "bgp", |
62b346ee | 40 | .node = BGP_NODE, |
24389580 | 41 | .parent_node = CONFIG_NODE, |
62b346ee | 42 | .prompt = "%s(config-router)# ", |
8f399b0e CF |
43 | }; |
44 | ||
d62a17ae | 45 | static struct cmd_node rip_node = { |
f4b8291f | 46 | .name = "rip", |
62b346ee | 47 | .node = RIP_NODE, |
24389580 | 48 | .parent_node = CONFIG_NODE, |
62b346ee | 49 | .prompt = "%s(config-router)# ", |
8f399b0e CF |
50 | }; |
51 | ||
d62a17ae | 52 | static struct cmd_node isis_node = { |
f4b8291f | 53 | .name = "isis", |
62b346ee | 54 | .node = ISIS_NODE, |
24389580 | 55 | .parent_node = CONFIG_NODE, |
62b346ee | 56 | .prompt = "%s(config-router)# ", |
8f399b0e CF |
57 | }; |
58 | ||
d62a17ae | 59 | static struct cmd_node interface_node = { |
f4b8291f | 60 | .name = "interface", |
62b346ee | 61 | .node = INTERFACE_NODE, |
24389580 | 62 | .parent_node = CONFIG_NODE, |
62b346ee | 63 | .prompt = "%s(config-if)# ", |
8f399b0e CF |
64 | }; |
65 | ||
62b346ee | 66 | static struct cmd_node rmap_node = { |
f4b8291f | 67 | .name = "routemap", |
62b346ee | 68 | .node = RMAP_NODE, |
24389580 | 69 | .parent_node = CONFIG_NODE, |
62b346ee DL |
70 | .prompt = "%s(config-route-map)# ", |
71 | }; | |
8f399b0e | 72 | |
62b346ee | 73 | static struct cmd_node zebra_node = { |
f4b8291f | 74 | .name = "zebra", |
62b346ee | 75 | .node = ZEBRA_NODE, |
24389580 | 76 | .parent_node = CONFIG_NODE, |
62b346ee DL |
77 | .prompt = "%s(config-router)# ", |
78 | }; | |
8f399b0e | 79 | |
62b346ee | 80 | static struct cmd_node bgp_vpnv4_node = { |
f4b8291f | 81 | .name = "bgp vpnv4", |
62b346ee | 82 | .node = BGP_VPNV4_NODE, |
24389580 | 83 | .parent_node = BGP_NODE, |
62b346ee DL |
84 | .prompt = "%s(config-router-af)# ", |
85 | }; | |
8f399b0e | 86 | |
62b346ee | 87 | static struct cmd_node bgp_ipv4_node = { |
f4b8291f | 88 | .name = "bgp ipv4 unicast", |
62b346ee | 89 | .node = BGP_IPV4_NODE, |
24389580 | 90 | .parent_node = BGP_NODE, |
62b346ee DL |
91 | .prompt = "%s(config-router-af)# ", |
92 | }; | |
8f399b0e | 93 | |
62b346ee | 94 | static struct cmd_node bgp_ipv4m_node = { |
f4b8291f | 95 | .name = "bgp ipv4 multicast", |
62b346ee | 96 | .node = BGP_IPV4M_NODE, |
24389580 | 97 | .parent_node = BGP_NODE, |
62b346ee DL |
98 | .prompt = "%s(config-router-af)# ", |
99 | }; | |
8f399b0e | 100 | |
62b346ee | 101 | static struct cmd_node bgp_ipv6_node = { |
f4b8291f | 102 | .name = "bgp ipv6", |
62b346ee | 103 | .node = BGP_IPV6_NODE, |
24389580 | 104 | .parent_node = BGP_NODE, |
62b346ee DL |
105 | .prompt = "%s(config-router-af)# ", |
106 | }; | |
8f399b0e | 107 | |
62b346ee | 108 | static struct cmd_node bgp_ipv6m_node = { |
f4b8291f | 109 | .name = "bgp ipv6 multicast", |
62b346ee | 110 | .node = BGP_IPV6M_NODE, |
24389580 | 111 | .parent_node = BGP_NODE, |
62b346ee DL |
112 | .prompt = "%s(config-router-af)# ", |
113 | }; | |
8f399b0e | 114 | |
62b346ee | 115 | static struct cmd_node ospf_node = { |
f4b8291f | 116 | .name = "ospf", |
62b346ee | 117 | .node = OSPF_NODE, |
24389580 | 118 | .parent_node = CONFIG_NODE, |
62b346ee DL |
119 | .prompt = "%s(config-router)# ", |
120 | }; | |
8f399b0e | 121 | |
62b346ee | 122 | static struct cmd_node ripng_node = { |
f4b8291f | 123 | .name = "ripng", |
62b346ee | 124 | .node = RIPNG_NODE, |
24389580 | 125 | .parent_node = CONFIG_NODE, |
62b346ee DL |
126 | .prompt = "%s(config-router)# ", |
127 | }; | |
8f399b0e | 128 | |
62b346ee | 129 | static struct cmd_node ospf6_node = { |
f4b8291f | 130 | .name = "ospf6", |
62b346ee | 131 | .node = OSPF6_NODE, |
24389580 | 132 | .parent_node = CONFIG_NODE, |
62b346ee DL |
133 | .prompt = "%s(config-ospf6)# ", |
134 | }; | |
8f399b0e | 135 | |
62b346ee | 136 | static struct cmd_node keychain_node = { |
f4b8291f | 137 | .name = "keychain", |
62b346ee | 138 | .node = KEYCHAIN_NODE, |
24389580 | 139 | .parent_node = CONFIG_NODE, |
62b346ee DL |
140 | .prompt = "%s(config-keychain)# ", |
141 | }; | |
8f399b0e | 142 | |
62b346ee | 143 | static struct cmd_node keychain_key_node = { |
f4b8291f | 144 | .name = "keychain key", |
62b346ee | 145 | .node = KEYCHAIN_KEY_NODE, |
24389580 | 146 | .parent_node = KEYCHAIN_NODE, |
62b346ee DL |
147 | .prompt = "%s(config-keychain-key)# ", |
148 | }; | |
8f399b0e | 149 | |
d62a17ae | 150 | static int test_callback(const struct cmd_element *cmd, struct vty *vty, |
151 | int argc, struct cmd_token *argv[]) | |
8f399b0e | 152 | { |
d62a17ae | 153 | int offset; |
154 | int rv; | |
155 | int i; | |
156 | ||
157 | offset = 0; | |
158 | rv = snprintf(test_buf, sizeof(test_buf), "'%s'", cmd->string); | |
159 | if (rv < 0) | |
160 | abort(); | |
161 | ||
162 | offset += rv; | |
163 | ||
164 | for (i = 0; i < argc; i++) { | |
165 | rv = snprintf(test_buf + offset, sizeof(test_buf) - offset, | |
166 | "%s'%s'", (i == 0) ? ": " : ", ", argv[i]->arg); | |
167 | if (rv < 0) | |
168 | abort(); | |
169 | offset += rv; | |
170 | } | |
171 | ||
172 | return CMD_SUCCESS; | |
8f399b0e CF |
173 | } |
174 | ||
d62a17ae | 175 | static void test_load(void) |
8f399b0e | 176 | { |
d62a17ae | 177 | char line[4096]; |
178 | ||
179 | test_cmds = vector_init(VECTOR_MIN_SIZE); | |
180 | ||
181 | while (fgets(line, sizeof(line), stdin) != NULL) { | |
182 | if (strlen(line)) | |
183 | line[strlen(line) - 1] = '\0'; | |
184 | if (line[0] == '#') | |
185 | continue; | |
fe011935 | 186 | vector_set(test_cmds, XSTRDUP(MTYPE_TMP, line)); |
d62a17ae | 187 | } |
8f399b0e CF |
188 | } |
189 | ||
d62a17ae | 190 | static void test_init(void) |
8f399b0e | 191 | { |
d62a17ae | 192 | unsigned int node; |
193 | unsigned int i; | |
194 | struct cmd_node *cnode; | |
195 | struct cmd_element *cmd; | |
196 | ||
197 | cmd_init(1); | |
390a8862 | 198 | nb_init(master, NULL, 0, false); |
d62a17ae | 199 | |
612c2c15 DL |
200 | install_node(&bgp_node); |
201 | install_node(&rip_node); | |
202 | install_node(&interface_node); | |
203 | install_node(&rmap_node); | |
204 | install_node(&zebra_node); | |
205 | install_node(&bgp_vpnv4_node); | |
206 | install_node(&bgp_ipv4_node); | |
207 | install_node(&bgp_ipv4m_node); | |
208 | install_node(&bgp_ipv6_node); | |
209 | install_node(&bgp_ipv6m_node); | |
210 | install_node(&ospf_node); | |
211 | install_node(&ripng_node); | |
212 | install_node(&ospf6_node); | |
213 | install_node(&keychain_node); | |
214 | install_node(&keychain_key_node); | |
215 | install_node(&isis_node); | |
216 | install_node(&vty_node); | |
d62a17ae | 217 | |
218 | test_init_cmd(); | |
219 | ||
220 | for (node = 0; node < vector_active(cmdvec); node++) | |
221 | if ((cnode = vector_slot(cmdvec, node)) != NULL) | |
222 | for (i = 0; i < vector_active(cnode->cmd_vector); i++) | |
223 | if ((cmd = vector_slot(cnode->cmd_vector, i)) | |
224 | != NULL) { | |
225 | cmd->daemon = 0; | |
226 | cmd->func = test_callback; | |
227 | } | |
228 | test_load(); | |
229 | vty_init_vtysh(); | |
8f399b0e CF |
230 | } |
231 | ||
d62a17ae | 232 | static void test_terminate(void) |
8f399b0e | 233 | { |
d62a17ae | 234 | unsigned int i; |
8f399b0e | 235 | |
d62a17ae | 236 | vty_terminate(); |
237 | for (i = 0; i < vector_active(test_cmds); i++) | |
fe011935 | 238 | XFREE(MTYPE_TMP, vector_slot(test_cmds, i)); |
d62a17ae | 239 | vector_free(test_cmds); |
240 | cmd_terminate(); | |
1c2facd1 RW |
241 | nb_terminate(); |
242 | yang_terminate(); | |
8f399b0e CF |
243 | } |
244 | ||
d62a17ae | 245 | static void test_run(struct prng *prng, struct vty *vty, const char *cmd, |
246 | unsigned int edit_dist, unsigned int node_index, | |
247 | int verbose) | |
8f399b0e | 248 | { |
d62a17ae | 249 | const char *test_str; |
250 | vector vline; | |
251 | int ret; | |
252 | unsigned int i; | |
253 | char **completions; | |
254 | unsigned int j; | |
255 | struct cmd_node *cnode; | |
256 | vector descriptions; | |
257 | int appended_null; | |
258 | int no_match; | |
259 | ||
260 | test_str = prng_fuzz( | |
261 | prng, cmd, | |
262 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:. /", | |
263 | edit_dist); | |
264 | vline = cmd_make_strvec(test_str); | |
265 | ||
266 | if (vline == NULL) | |
267 | return; | |
268 | ||
269 | appended_null = 0; | |
270 | for (i = 0; i < vector_active(cmdvec); i++) | |
271 | if ((cnode = vector_slot(cmdvec, i)) != NULL) { | |
272 | if (node_index != (unsigned int)-1 && i != node_index) | |
273 | continue; | |
274 | ||
275 | if (appended_null) { | |
276 | vector_unset(vline, vector_active(vline) - 1); | |
277 | appended_null = 0; | |
278 | } | |
279 | vty->node = cnode->node; | |
280 | test_buf[0] = '\0'; | |
281 | ret = cmd_execute_command(vline, vty, NULL, 0); | |
282 | no_match = (ret == CMD_ERR_NO_MATCH); | |
283 | if (verbose || !no_match) | |
284 | printf("execute relaxed '%s'@%d: rv==%d%s%s\n", | |
285 | test_str, cnode->node, ret, | |
286 | (test_buf[0] != '\0') ? ", " : "", | |
287 | test_buf); | |
288 | ||
289 | vty->node = cnode->node; | |
290 | test_buf[0] = '\0'; | |
291 | ret = cmd_execute_command_strict(vline, vty, NULL); | |
292 | if (verbose || !no_match) | |
293 | printf("execute strict '%s'@%d: rv==%d%s%s\n", | |
294 | test_str, cnode->node, ret, | |
295 | (test_buf[0] != '\0') ? ", " : "", | |
296 | test_buf); | |
297 | ||
fefa5e0f DL |
298 | if (isspace((unsigned char)test_str[ |
299 | strlen(test_str) - 1])) { | |
d62a17ae | 300 | vector_set(vline, NULL); |
301 | appended_null = 1; | |
302 | } | |
303 | ||
304 | vty->node = cnode->node; | |
305 | completions = cmd_complete_command(vline, vty, &ret); | |
306 | if (verbose || !no_match) | |
307 | printf("complete '%s'@%d: rv==%d\n", test_str, | |
308 | cnode->node, ret); | |
309 | if (completions != NULL) { | |
310 | for (j = 0; completions[j] != NULL; j++) { | |
311 | printf(" '%s'\n", completions[j]); | |
312 | XFREE(MTYPE_TMP, completions[j]); | |
313 | } | |
314 | XFREE(MTYPE_TMP, completions); | |
315 | } | |
316 | ||
317 | vty->node = cnode->node; | |
318 | descriptions = cmd_describe_command(vline, vty, &ret); | |
319 | if (verbose || !no_match) | |
320 | printf("describe '%s'@%d: rv==%d\n", test_str, | |
321 | cnode->node, ret); | |
322 | if (descriptions != NULL) { | |
323 | for (j = 0; j < vector_active(descriptions); | |
324 | j++) { | |
7fe96307 | 325 | struct cmd_token *ct = |
d62a17ae | 326 | vector_slot(descriptions, j); |
7fe96307 A |
327 | printf(" '%s' '%s'\n", ct->text, |
328 | ct->desc); | |
d62a17ae | 329 | } |
330 | vector_free(descriptions); | |
331 | } | |
332 | } | |
333 | cmd_free_strvec(vline); | |
8f399b0e CF |
334 | } |
335 | ||
d62a17ae | 336 | int main(int argc, char **argv) |
8f399b0e | 337 | { |
d62a17ae | 338 | int opt; |
339 | struct prng *prng; | |
340 | struct vty *vty; | |
341 | unsigned int edit_distance; | |
342 | unsigned int max_edit_distance; | |
343 | unsigned int node_index; | |
344 | int verbose; | |
345 | unsigned int test_cmd; | |
346 | unsigned int iteration; | |
347 | unsigned int num_iterations; | |
348 | ||
349 | max_edit_distance = 3; | |
350 | node_index = -1; | |
351 | verbose = 0; | |
352 | ||
353 | while ((opt = getopt(argc, argv, "e:n:v")) != -1) { | |
354 | switch (opt) { | |
355 | case 'e': | |
356 | max_edit_distance = atoi(optarg); | |
357 | break; | |
358 | case 'n': | |
359 | node_index = atoi(optarg); | |
360 | break; | |
361 | case 'v': | |
362 | verbose++; | |
363 | break; | |
364 | default: | |
365 | fprintf(stderr, | |
366 | "Usage: %s [-e <edit_dist>] [-n <node_idx>] [-v]\n", | |
367 | argv[0]); | |
368 | exit(1); | |
369 | break; | |
370 | } | |
371 | } | |
372 | ||
373 | test_init(); | |
374 | prng = prng_new(0); | |
375 | ||
376 | vty = vty_new(); | |
377 | vty->type = VTY_TERM; | |
378 | ||
379 | fprintf(stderr, "Progress:\n0/%u", vector_active(test_cmds)); | |
380 | for (test_cmd = 0; test_cmd < vector_active(test_cmds); test_cmd++) { | |
381 | for (edit_distance = 0; edit_distance <= max_edit_distance; | |
382 | edit_distance++) { | |
383 | num_iterations = 1 << edit_distance; | |
384 | num_iterations *= num_iterations * num_iterations; | |
385 | ||
386 | for (iteration = 0; iteration < num_iterations; | |
387 | iteration++) | |
388 | test_run(prng, vty, | |
389 | vector_slot(test_cmds, test_cmd), | |
390 | edit_distance, node_index, verbose); | |
391 | } | |
392 | fprintf(stderr, "\r%u/%u", test_cmd + 1, | |
393 | vector_active(test_cmds)); | |
394 | } | |
395 | fprintf(stderr, "\nDone.\n"); | |
396 | ||
397 | vty_close(vty); | |
398 | prng_free(prng); | |
399 | test_terminate(); | |
400 | return 0; | |
8f399b0e | 401 | } |