From 9d0662e009c0cf4532b88c9a1fb0f7c0dc174584 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 19 Jul 2016 21:14:27 +0000 Subject: [PATCH] lib: Break up functions, begin matcher Moved test hook out of command.c into vtysh.c, renamed graph modules, added matching code Signed-off-by: Quentin Young --- lib/Makefile.am | 4 +- lib/command.c | 6 - lib/{cmdtree.c => command_graph.c} | 2 +- lib/{cmdtree.h => command_graph.h} | 5 + lib/command_match.c | 174 +++++++++++++++++++++++++++++ lib/command_match.h | 69 ++++++++++++ lib/command_parse.y | 26 ++++- lib/grammar_sandbox.c | 43 +++++-- lib/grammar_sandbox.h | 2 + vtysh/vtysh.c | 4 + 10 files changed, 310 insertions(+), 25 deletions(-) rename lib/{cmdtree.c => command_graph.c} (99%) rename lib/{cmdtree.h => command_graph.h} (97%) create mode 100644 lib/command_match.c create mode 100644 lib/command_match.h create mode 100644 lib/grammar_sandbox.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 126303622f..2df5ed61f4 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -11,7 +11,7 @@ libzebra_la_LDFLAGS = -version-info 0:0:0 libzebra_la_SOURCES = \ network.c pid_output.c getopt.c getopt1.c daemon.c \ checksum.c vector.c linklist.c vty.c \ - cmdtree.c command_parse.y command_lex.l \ + command_graph.c command_parse.y command_lex.l command_match.c grammar_sandbox.c \ command.c \ sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \ filter.c routemap.c distribute.c stream.c str.c log.c plist.c \ @@ -28,7 +28,7 @@ libzebra_la_LIBADD = @LIB_REGEX@ @LIBCAP@ pkginclude_HEADERS = \ buffer.h checksum.h filter.h getopt.h hash.h \ if.h linklist.h log.h \ - cmdtree.h \ + command_graph.h command_match.h grammar_sandbox.h \ command.h \ memory.h network.h prefix.h routemap.h distribute.h sockunion.h \ str.h stream.h table.h thread.h vector.h version.h vty.h zebra.h \ diff --git a/lib/command.c b/lib/command.c index 4b571ba75d..490c2a0690 100644 --- a/lib/command.c +++ b/lib/command.c @@ -34,8 +34,6 @@ Boston, MA 02111-1307, USA. */ #include "workqueue.h" #include "vrf.h" -#include "grammar_sandbox.c" - /* Command vector which includes some level of command lists. Normally each daemon maintains each own cmdvec. */ vector cmdvec = NULL; @@ -4077,10 +4075,6 @@ cmd_init (int terminal) install_element (ENABLE_NODE, &show_version_cmd); install_element (ENABLE_NODE, &show_commandtree_cmd); - /**/ - grammar_sandbox_init(); - /**/ - if (terminal) { install_element (ENABLE_NODE, &config_terminal_length_cmd); diff --git a/lib/cmdtree.c b/lib/command_graph.c similarity index 99% rename from lib/cmdtree.c rename to lib/command_graph.c index 38632dca40..9b91eaf242 100644 --- a/lib/cmdtree.c +++ b/lib/command_graph.c @@ -7,7 +7,7 @@ */ #include -#include "cmdtree.h" +#include "command_graph.h" #include "memory.h" struct graph_node * diff --git a/lib/cmdtree.h b/lib/command_graph.h similarity index 97% rename from lib/cmdtree.h rename to lib/command_graph.h index 3d2366b008..8d23577d37 100644 --- a/lib/cmdtree.h +++ b/lib/command_graph.h @@ -1,3 +1,6 @@ +#ifndef COMMAND_GRAPH_H +#define COMMAND_GRAPH_H + #include "vty.h" #include "vector.h" @@ -78,3 +81,5 @@ new_node(enum graph_node_type); */ extern void walk_graph(struct graph_node *, int); + +#endif diff --git a/lib/command_match.c b/lib/command_match.c new file mode 100644 index 0000000000..bcc28bdc5b --- /dev/null +++ b/lib/command_match.c @@ -0,0 +1,174 @@ +#include +#include "memory.h" +#include "command_match.h" + +enum match_type +match_command (struct graph_node *start, enum filter_type filter, const char *input) +{ + // match input on DFA + return exact_match; +} + + +#define IPV4_ADDR_STR "0123456789." +#define IPV4_PREFIX_STR "0123456789./" + +enum match_type +cmd_ipv4_match (const char *str) +{ + struct sockaddr_in sin_dummy; + + if (str == NULL) + return partly_match; + + if (strspn (str, IPV4_ADDR_STR) != strlen (str)) + return no_match; + + if (inet_pton(AF_INET, str, &sin_dummy.sin_addr) != 1) + return no_match; + + return exact_match; +} + +enum match_type +cmd_ipv4_prefix_match (const char *str) +{ + struct sockaddr_in sin_dummy; + const char *delim = "/\0"; + char *dupe, *prefix, *mask, *context, *endptr; + int nmask = -1; + + if (str == NULL) + return partly_match; + + if (strspn (str, IPV4_PREFIX_STR) != strlen (str)) + return no_match; + + /* tokenize to address + mask */ + dupe = XMALLOC(MTYPE_TMP, strlen(str)+1); + strncpy(dupe, str, strlen(str)+1); + prefix = strtok_r(dupe, delim, &context); + mask = strtok_r(NULL, delim, &context); + + if (!mask) + return partly_match; + + /* validate prefix */ + if (inet_pton(AF_INET, prefix, &sin_dummy.sin_addr) != 1) + return no_match; + + /* validate mask */ + nmask = strtol (mask, &endptr, 10); + if (*endptr != '\0' || nmask < 0 || nmask > 32) + return no_match; + + XFREE(MTYPE_TMP, dupe); + + return exact_match; +} + +#define IPV6_ADDR_STR "0123456789abcdefABCDEF:." +#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./" + +#ifdef HAVE_IPV6 +enum match_type +cmd_ipv6_match (const char *str) +{ + struct sockaddr_in6 sin6_dummy; + int ret; + + if (str == NULL) + return partly_match; + + if (strspn (str, IPV6_ADDR_STR) != strlen (str)) + return no_match; + + ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); + + if (ret == 1) + return exact_match; + + return no_match; +} + +enum match_type +cmd_ipv6_prefix_match (const char *str) +{ + struct sockaddr_in6 sin6_dummy; + const char *delim = "/\0"; + char *dupe, *prefix, *mask, *context, *endptr; + int nmask = -1; + + if (str == NULL) + return partly_match; + + if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) + return no_match; + + /* tokenize to address + mask */ + dupe = XMALLOC(MTYPE_TMP, strlen(str)+1); + strncpy(dupe, str, strlen(str)+1); + prefix = strtok_r(dupe, delim, &context); + mask = strtok_r(NULL, delim, &context); + + if (!mask) + return partly_match; + + /* validate prefix */ + if (inet_pton(AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1) + return no_match; + + /* validate mask */ + nmask = strtol (mask, &endptr, 10); + if (*endptr != '\0' || nmask < 0 || nmask > 128) + return no_match; + + XFREE(MTYPE_TMP, dupe); + + return exact_match; +} +#endif + +enum match_type +cmd_range_match (struct graph_node *rangenode, const char *str) +{ + char *endptr = NULL; + signed long long val; + + if (str == NULL) + return 1; + + val = strtoll (str, &endptr, 10); + if (*endptr != '\0') + return 0; + val = llabs(val); + + if (val < rangenode->min || val > rangenode->max) + return no_match; + else + return exact_match; +} + +enum match_type +cmd_word_match(struct graph_node *wordnode, + enum filter_type filter, + const char *word) +{ + if (filter == FILTER_RELAXED) + if (!word || !strlen(word)) + return partly_match; + + if (!word) + return no_match; + + if (filter == FILTER_RELAXED && !strncmp(wordnode->text, word, strlen(word))) + { + if (!strcmp(wordnode->text, word)) + return exact_match; + return partly_match; + } + if (filter == FILTER_STRICT && !strcmp(wordnode->text, word)) + return exact_match; + + return no_match; +} diff --git a/lib/command_match.h b/lib/command_match.h new file mode 100644 index 0000000000..cddeb08af7 --- /dev/null +++ b/lib/command_match.h @@ -0,0 +1,69 @@ +#ifndef COMMAND_MATCH_H +#define COMMAND_MATCH_H + +#include "command_graph.h" + +/** + * Filter types. These tell the parser whether to allow + * partial matching on tokens. + */ +enum filter_type +{ + FILTER_RELAXED, + FILTER_STRICT +}; + +/** + * Command matcher result value. + */ +enum matcher_rv +{ + MATCHER_OK, + MATCHER_COMPLETE, + MATCHER_INCOMPLETE, + MATCHER_NO_MATCH, + MATCHER_AMBIGUOUS, + MATCHER_EXCEED_ARGC_MAX +}; + +/* Completion match types. */ +enum match_type +{ + no_match, + partly_match, + exact_match +}; +/** + * Defines which matcher_rv values constitute + * an error. Should be used against matcher_rv + * return values to do basic error checking. + */ +#define MATCHER_ERROR(matcher_rv) \ + ( (matcher_rv) == MATCHER_INCOMPLETE \ + || (matcher_rv) == MATCHER_NO_MATCH \ + || (matcher_rv) == MATCHER_AMBIGUOUS \ + || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \ + ) + +enum match_type +cmd_ipv4_match (const char *); + +enum match_type +cmd_ipv4_prefix_match (const char *); + +enum match_type +cmd_ipv6_match (const char *); + +enum match_type +cmd_ipv6_prefix_match (const char *); + +enum match_type +cmd_range_match (struct graph_node *, const char *str); + +enum match_type +cmd_word_match (struct graph_node *, enum filter_type, const char *); + +enum match_type +match_command (struct graph_node *, enum filter_type, const char *); + +#endif diff --git a/lib/command_parse.y b/lib/command_parse.y index cf2cd95bdd..80487af7cd 100644 --- a/lib/command_parse.y +++ b/lib/command_parse.y @@ -1,5 +1,15 @@ +/* + * Command format string parser. + * + * Turns a command definition into a DFA that together with the functions + * provided in command_match.c may be used to map command line input to a + * function. + * + * @author Quentin Young + */ + %{ -#include "cmdtree.h" +#include "command_graph.h" extern int yylex(void); extern void yyerror(const char *); @@ -8,16 +18,22 @@ extern void yyerror(const char *); #define YYDEBUG 1 %} %code provides { -extern struct graph_node *cmd_parse_format_new(const char *, const char *, struct graph_node *); -extern void set_buffer_string(const char *); +extern struct +graph_node *cmd_parse_format(const char *, + const char *, + struct graph_node *); +extern void +set_buffer_string(const char *); } +/* valid types for tokens */ %union{ int integer; char *string; struct graph_node *node; } +/* some helpful state variables */ %{ struct graph_node *startnode, // command root node *currnode, // current node @@ -66,7 +82,7 @@ sentence_root: WORD $$ = new_node(WORD_GN); $$->text = strdup(yylval.string); fprintf(stderr, ">>>>>>>> YYLVAL.STRING: %s\n", yylval.string); - fprintf(stderr, ">>>>>>>> TEXT: %s\n", $$->text); + fprintf(stderr, ">>>>>>>> TEXT: %s\n", $$->text); currnode = $$; currnode->is_root = 1; @@ -270,7 +286,7 @@ void yyerror(char const *message) { } struct graph_node * -cmd_parse_format_new(const char *string, const char *desc, struct graph_node *start) +cmd_parse_format(const char *string, const char *desc, struct graph_node *start) { fprintf(stderr, "parsing: %s\n", string); diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index 807ada9d98..1e6a76c69c 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -1,17 +1,14 @@ #include "command.h" +#include "command_graph.h" #include "command_parse.h" -#include "cmdtree.h" +#include "command_match.h" #define GRAMMAR_STR "CLI grammar sandbox\n" struct graph_node * nodegraph; -DEFUN (grammar_test, - grammar_test_cmd, - "grammar parse .COMMAND", - GRAMMAR_STR - "command to pass to new parser\n") -{ +/* +char* combine_vararg(char* argv, int argc) { size_t linesize = 0; for (int i = 0; i < argc; i++) linesize += strlen(argv[i]) + 1; @@ -24,8 +21,19 @@ DEFUN (grammar_test, strcat(cat, " "); } - //struct graph_node *result = new_node(NUL_GN); - cmd_parse_format_new((const char*) cat, "lol", nodegraph); + return cat; +} +*/ + +DEFUN (grammar_test, + grammar_test_cmd, + "grammar parse .COMMAND", + GRAMMAR_STR + "command to pass to new parser\n") +{ + + const char* command = argv_concat(argv, argc, 0); + cmd_parse_format(command, "lol", nodegraph); walk_graph(nodegraph, 0); return CMD_SUCCESS; @@ -37,8 +45,20 @@ DEFUN (grammar_test_show, GRAMMAR_STR "print current accumulated DFA\n") { - walk_graph(nodegraph, 0); - return CMD_SUCCESS; + walk_graph(nodegraph, 0); + return CMD_SUCCESS; +} + +DEFUN (grammar_test_match, + grammar_test_match_cmd, + "grammar match .COMMAND", + GRAMMAR_STR + "attempt to match input on DFA\n" + "command to match") +{ + const char* command = argv_concat(argv, argc, 0); + match_command(nodegraph, FILTER_STRICT, command); + return CMD_SUCCESS; } @@ -48,4 +68,5 @@ void grammar_sandbox_init() { nodegraph = new_node(NUL_GN); install_element (ENABLE_NODE, &grammar_test_cmd); install_element (ENABLE_NODE, &grammar_test_show_cmd); + install_element (ENABLE_NODE, &grammar_test_match_cmd); } diff --git a/lib/grammar_sandbox.h b/lib/grammar_sandbox.h new file mode 100644 index 0000000000..2b2c742461 --- /dev/null +++ b/lib/grammar_sandbox.h @@ -0,0 +1,2 @@ +void +grammar_sandbox_init(void); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index ecb6c5c6ac..9d4061cfcf 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -42,6 +42,8 @@ #include "bgpd/bgp_vty.h" #include "vrf.h" +#include "lib/grammar_sandbox.h" + /* Struct VTY. */ struct vty *vty; @@ -3076,4 +3078,6 @@ vtysh_init_vty (void) install_element (CONFIG_NODE, &vtysh_enable_password_text_cmd); install_element (CONFIG_NODE, &no_vtysh_enable_password_cmd); + /* grammar sandbox */ + grammar_sandbox_init(); } -- 2.39.5