diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/.gitignore | 6 | ||||
| -rw-r--r-- | lib/Makefile.am | 45 | ||||
| -rw-r--r-- | lib/bfd.c | 8 | ||||
| -rw-r--r-- | lib/bfd.h | 5 | ||||
| -rw-r--r-- | lib/command.c | 3718 | ||||
| -rw-r--r-- | lib/command.h | 453 | ||||
| -rw-r--r-- | lib/command_lex.l | 85 | ||||
| -rw-r--r-- | lib/command_match.c | 1027 | ||||
| -rw-r--r-- | lib/command_match.h | 113 | ||||
| -rw-r--r-- | lib/command_parse.y | 435 | ||||
| -rw-r--r-- | lib/csv.c | 24 | ||||
| -rw-r--r-- | lib/distribute.c | 663 | ||||
| -rw-r--r-- | lib/filter.c | 478 | ||||
| -rw-r--r-- | lib/filter.h | 3 | ||||
| -rw-r--r-- | lib/grammar_sandbox.c | 661 | ||||
| -rw-r--r-- | lib/grammar_sandbox_main.c | 64 | ||||
| -rw-r--r-- | lib/graph.c | 148 | ||||
| -rw-r--r-- | lib/graph.h | 101 | ||||
| -rw-r--r-- | lib/if.c | 111 | ||||
| -rw-r--r-- | lib/if.h | 27 | ||||
| -rw-r--r-- | lib/if_rmap.c | 46 | ||||
| -rw-r--r-- | lib/imsg.c | 3 | ||||
| -rw-r--r-- | lib/json.c | 20 | ||||
| -rw-r--r-- | lib/json.h | 8 | ||||
| -rw-r--r-- | lib/keychain.c | 223 | ||||
| -rw-r--r-- | lib/libfrr.c | 392 | ||||
| -rw-r--r-- | lib/libfrr.h | 100 | ||||
| -rw-r--r-- | lib/log.c | 190 | ||||
| -rw-r--r-- | lib/log.h | 78 | ||||
| -rw-r--r-- | lib/log_int.h | 57 | ||||
| -rw-r--r-- | lib/memory.c | 6 | ||||
| -rw-r--r-- | lib/monotime.h | 77 | ||||
| -rw-r--r-- | lib/mpls.h | 45 | ||||
| -rw-r--r-- | lib/network.c | 9 | ||||
| -rw-r--r-- | lib/ns.c | 16 | ||||
| -rw-r--r-- | lib/plist.c | 580 | ||||
| -rw-r--r-- | lib/plist.h | 5 | ||||
| -rw-r--r-- | lib/prefix.c | 170 | ||||
| -rw-r--r-- | lib/prefix.h | 80 | ||||
| -rw-r--r-- | lib/ptm_lib.c | 2 | ||||
| -rw-r--r-- | lib/qobj.c | 5 | ||||
| -rw-r--r-- | lib/qobj.h | 2 | ||||
| -rwxr-xr-x | lib/route_types.pl | 2 | ||||
| -rw-r--r-- | lib/route_types.txt | 4 | ||||
| -rw-r--r-- | lib/routemap.c | 1417 | ||||
| -rw-r--r-- | lib/routemap.h | 170 | ||||
| -rw-r--r-- | lib/skiplist.c | 3 | ||||
| -rw-r--r-- | lib/smux.c | 71 | ||||
| -rw-r--r-- | lib/sockopt.c | 31 | ||||
| -rw-r--r-- | lib/sockopt.h | 11 | ||||
| -rw-r--r-- | lib/sockunion.c | 80 | ||||
| -rw-r--r-- | lib/sockunion.h | 4 | ||||
| -rw-r--r-- | lib/spf_backoff.c | 341 | ||||
| -rw-r--r-- | lib/spf_backoff.h | 66 | ||||
| -rw-r--r-- | lib/srcdest_table.c | 312 | ||||
| -rw-r--r-- | lib/srcdest_table.h | 101 | ||||
| -rw-r--r-- | lib/table.c | 36 | ||||
| -rw-r--r-- | lib/table.h | 6 | ||||
| -rw-r--r-- | lib/thread.c | 292 | ||||
| -rw-r--r-- | lib/thread.h | 25 | ||||
| -rw-r--r-- | lib/vector.c | 32 | ||||
| -rw-r--r-- | lib/vector.h | 5 | ||||
| -rw-r--r-- | lib/vrf.c | 38 | ||||
| -rw-r--r-- | lib/vrf.h | 8 | ||||
| -rw-r--r-- | lib/vty.c | 1268 | ||||
| -rw-r--r-- | lib/vty.h | 37 | ||||
| -rw-r--r-- | lib/wheel.c | 164 | ||||
| -rw-r--r-- | lib/wheel.h | 118 | ||||
| -rw-r--r-- | lib/workqueue.c | 6 | ||||
| -rw-r--r-- | lib/workqueue.h | 4 | ||||
| -rw-r--r-- | lib/zclient.c | 28 | ||||
| -rw-r--r-- | lib/zclient.h | 62 | ||||
| -rw-r--r-- | lib/zebra.h | 160 |
73 files changed, 9415 insertions, 5776 deletions
diff --git a/lib/.gitignore b/lib/.gitignore index fe75137bca..10b8704ab2 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -15,4 +15,10 @@ gitversion.h.tmp *~ *.loT route_types.h +memtypes.h +command_lex.c +command_lex.h +command_parse.c +command_parse.h refix +grammar_sandbox diff --git a/lib/Makefile.am b/lib/Makefile.am index f393836cc0..1a8c7af42b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,34 +1,47 @@ ## Process this file with automake to produce Makefile.in. -AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ - -DVTY_DEPRECATE_INDEX +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib AM_CFLAGS = $(WERROR) DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" +AM_YFLAGS = -d -Dapi.prefix=@BISON_OPENBRACE@cmd_yy@BISON_CLOSEBRACE@ @BISON_VERBOSE@ + +command_lex.h: command_lex.c + @if test ! -f $@; then rm -f command_lex.c; else :; fi + @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) command_lex.c; else :; fi +command_parse.lo: command_lex.h lib_LTLIBRARIES = libfrr.la libfrr_la_LDFLAGS = -version-info 0:0:0 libfrr_la_SOURCES = \ network.c pid_output.c getopt.c getopt1.c \ - checksum.c vector.c linklist.c vty.c command.c \ + checksum.c vector.c linklist.c vty.c \ + graph.c command_parse.y command_lex.l command_match.c \ + command.c \ sockunion.c prefix.c thread.c if.c buffer.c table.c hash.c \ filter.c routemap.c distribute.c stream.c log.c plist.c \ zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c privs.c \ sigevent.c pqueue.c jhash.c workqueue.c nexthop.c json.c \ ptm_lib.c csv.c bfd.c vrf.c systemd.c ns.c memory.c memory_vty.c \ imsg-buffer.c imsg.c skiplist.c \ - qobj.c \ + qobj.c wheel.c \ event_counter.c \ + grammar_sandbox.c \ + srcdest_table.c \ + spf_backoff.c \ + libfrr.c \ strlcpy.c \ strlcat.c -BUILT_SOURCES = route_types.h gitversion.h +BUILT_SOURCES = route_types.h gitversion.h command_parse.h command_lex.h libfrr_la_LIBADD = @LIBCAP@ pkginclude_HEADERS = \ - buffer.h checksum.h command.h filter.h getopt.h hash.h \ + buffer.h checksum.h filter.h getopt.h hash.h \ if.h linklist.h log.h \ + graph.h command_match.h \ + command.h \ memory.h network.h prefix.h routemap.h distribute.h sockunion.h \ stream.h table.h thread.h vector.h version.h vty.h zebra.h \ plist.h zclient.h sockopt.h smux.h md5.h if_rmap.h keychain.h \ @@ -36,14 +49,26 @@ pkginclude_HEADERS = \ workqueue.h route_types.h libospf.h nexthop.h json.h \ ptm_lib.h csv.h bfd.h vrf.h ns.h systemd.h bitfield.h \ fifo.h memory_vty.h mpls.h imsg.h openbsd-queue.h openbsd-tree.h \ - skiplist.h qobj.h \ - event_counter.h + skiplist.h qobj.h wheel.h \ + event_counter.h \ + monotime.h \ + spf_backoff.h \ + srcdest_table.h \ + libfrr.h \ + # end noinst_HEADERS = \ - plist_int.h + plist_int.h \ + log_int.h + +noinst_PROGRAMS = grammar_sandbox + +grammar_sandbox_SOURCES = grammar_sandbox_main.c +grammar_sandbox_LDADD = libfrr.la EXTRA_DIST = \ queue.h \ + command_lex.h \ route_types.pl route_types.txt \ gitversion.pl @@ -64,7 +89,7 @@ GITH=gitversion.h gitversion.h.tmp: $(srcdir)/../.git @PERL@ $(srcdir)/gitversion.pl $(srcdir) > ${GITH}.tmp gitversion.h: gitversion.h.tmp - { test -f ${GITH} && diff -s -q ${GITH}.tmp ${GITH}; } || cp -v ${GITH}.tmp ${GITH} + { test -f ${GITH} && diff -s -q ${GITH}.tmp ${GITH}; } || cp -v ${GITH}.tmp ${GITH} else .PHONY: gitversion.h @@ -179,11 +179,9 @@ bfd_peer_sendmsg (struct zclient *zclient, struct bfd_info *bfd_info, case AF_INET: stream_put_in_addr (s, (struct in_addr *)dst_ip); break; -#ifdef HAVE_IPV6 case AF_INET6: stream_put(s, dst_ip, 16); break; -#endif default: break; } @@ -207,11 +205,9 @@ bfd_peer_sendmsg (struct zclient *zclient, struct bfd_info *bfd_info, case AF_INET: stream_put_in_addr (s, (struct in_addr *) src_ip); break; - #ifdef HAVE_IPV6 case AF_INET6: stream_put(s, src_ip, 16); break; - #endif default: break; } @@ -221,13 +217,11 @@ bfd_peer_sendmsg (struct zclient *zclient, struct bfd_info *bfd_info, else { stream_putc(s, 0); -#ifdef HAVE_IPV6 if ((family == AF_INET6) && (src_ip)) { stream_putw(s, family); stream_put(s, src_ip, 16); } -#endif if (if_name) { len = strlen(if_name); @@ -368,7 +362,7 @@ bfd_last_update (time_t last_update, char *buf, size_t len) } /* Get current time. */ - quagga_gettime(QUAGGA_CLK_MONOTONIC, &tv); + monotime(&tv); curr = tv.tv_sec; diff = curr - last_update; tm = gmtime (&diff); @@ -26,11 +26,6 @@ #include "lib/json.h" -#define BFD_CMD_DETECT_MULT_RANGE "<2-255> " -#define BFD_CMD_MIN_RX_RANGE "<50-60000> " -#define BFD_CMD_MIN_TX_RANGE "<50-60000>" -#define BFD_CMD_TYPE "(multihop|singlehop)" - #define BFD_DEF_MIN_RX 300 #define BFD_MIN_MIN_RX 50 #define BFD_MAX_MIN_RX 60000 diff --git a/lib/command.c b/lib/command.c index 6176640bf6..bfff581d9b 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1,86 +1,58 @@ /* - Command interpreter routine for virtual terminal [aka TeletYpe] - Copyright (C) 1997, 98, 99 Kunihiro Ishiguro - Copyright (C) 2013 by Open Source Routing. - Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") - -This file is part of GNU Zebra. - -GNU Zebra is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published -by the Free Software Foundation; either version 2, or (at your -option) any later version. - -GNU Zebra is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Zebra; see the file COPYING. If not, write to the -Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ + * CLI backend interface. + * + * -- + * Copyright (C) 2016 Cumulus Networks, Inc. + * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro + * Copyright (C) 2013 by Open Source Routing. + * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC") + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ #include <zebra.h> #include "memory.h" #include "log.h" +#include "log_int.h" #include <lib/version.h> #include "thread.h" #include "vector.h" +#include "linklist.h" #include "vty.h" #include "command.h" #include "workqueue.h" #include "vrf.h" +#include "command_match.h" #include "qobj.h" DEFINE_MTYPE( LIB, HOST, "Host config") DEFINE_MTYPE( LIB, STRVEC, "String vector") -DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc") +DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command Tokens") +DEFINE_MTYPE_STATIC(LIB, CMD_DESC, "Command Token Text") +DEFINE_MTYPE_STATIC(LIB, CMD_TEXT, "Command Token Help") +DEFINE_MTYPE( LIB, CMD_ARG, "Command Argument") /* Command vector which includes some level of command lists. Normally each daemon maintains each own cmdvec. */ vector cmdvec = NULL; -struct cmd_token token_cr; -char *command_cr = NULL; - -/** - * 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 -}; - -/** - * 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 \ - ) - /* Host information structure. */ struct host host; @@ -123,7 +95,7 @@ static const struct facility_map { int facility; const char *name; size_t match; -} syslog_facilities[] = +} syslog_facilities[] = { { LOG_KERN, "kern", 1 }, { LOG_USER, "user", 2 }, @@ -175,7 +147,7 @@ static int level_match(const char *s) { int level ; - + for ( level = 0 ; zlog_priority [level] != NULL ; level ++ ) if (!strncmp (s, zlog_priority[level], 2)) return level; @@ -195,7 +167,7 @@ print_version (const char *progname) /* Utility function to concatenate argv argument into a single string with inserting ' ' character between each argument. */ char * -argv_concat (const char **argv, int argc, int shift) +argv_concat (struct cmd_token **argv, int argc, int shift) { int i; size_t len; @@ -204,14 +176,14 @@ argv_concat (const char **argv, int argc, int shift) len = 0; for (i = shift; i < argc; i++) - len += strlen(argv[i])+1; + len += strlen(argv[i]->arg)+1; if (!len) return NULL; p = str = XMALLOC(MTYPE_TMP, len); for (i = shift; i < argc; i++) { size_t arglen; - memcpy(p, argv[i], (arglen = strlen(argv[i]))); + memcpy(p, argv[i]->arg, (arglen = strlen(argv[i]->arg))); p += arglen; *p++ = ' '; } @@ -219,6 +191,26 @@ argv_concat (const char **argv, int argc, int shift) return str; } +/** + * Convenience function for accessing argv data. + * + * @param argc + * @param argv + * @param text definition snippet of the desired token + * @param index the starting index, and where to store the + * index of the found token if it exists + * @return 1 if found, 0 otherwise + */ +int +argv_find (struct cmd_token **argv, int argc, const char *text, int *index) +{ + int found = 0; + for (int i = *index; i < argc && found == 0; i++) + if ((found = strmatch (text, argv[i]->text))) + *index = i; + return found; +} + static unsigned int cmd_hash_key (void *p) { @@ -233,65 +225,57 @@ cmd_hash_cmp (const void *a, const void *b) /* Install top node of command vector. */ void -install_node (struct cmd_node *node, - int (*func) (struct vty *)) +install_node (struct cmd_node *node, + int (*func) (struct vty *)) { vector_set_index (cmdvec, node->node, node); node->func = func; + node->cmdgraph = graph_new (); node->cmd_vector = vector_init (VECTOR_MIN_SIZE); + // add start node + struct cmd_token *token = new_cmd_token (START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + graph_new_node (node->cmdgraph, token, (void (*)(void *)) &del_cmd_token); node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp); } -/* Breaking up string into each command piece. I assume given - character is separated by a space character. Return value is a - vector which includes char ** data element. */ +/** + * Tokenizes a string, storing tokens in a vector. + * Whitespace is ignored. + * + * Delimiter string = " \n\r\t". + * + * @param string to tokenize + * @return tokenized string + */ vector cmd_make_strvec (const char *string) { - const char *cp, *start; - char *token; - int strlen; - vector strvec; - - if (string == NULL) - return NULL; - - cp = string; + if (!string) return NULL; - /* Skip white spaces. */ - while (isspace ((int) *cp) && *cp != '\0') - cp++; + char *copy, *copystart; + copystart = copy = XSTRDUP (MTYPE_TMP, string); - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; + // skip leading whitespace + while (isspace ((int) *copy) && *copy != '\0') copy++; - if (*cp == '!' || *cp == '#') + // if the entire string was whitespace or a comment, return + if (*copy == '\0' || *copy == '!' || *copy == '#') + { + XFREE (MTYPE_TMP, copystart); return NULL; + } - /* Prepare return vector. */ - strvec = vector_init (VECTOR_MIN_SIZE); + vector strvec = vector_init (VECTOR_MIN_SIZE); + const char *delim = " \n\r\t", *tok = NULL; + while (copy) + { + tok = strsep (©, delim); + if (*tok != '\0') + vector_set (strvec, XSTRDUP (MTYPE_STRVEC, tok)); + } - /* Copy each command piece and set into vector. */ - while (1) - { - start = cp; - while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && - *cp != '\0') - cp++; - strlen = cp - start; - token = XMALLOC (MTYPE_STRVEC, strlen + 1); - memcpy (token, start, strlen); - *(token + strlen) = '\0'; - vector_set (strvec, token); - - while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') && - *cp != '\0') - cp++; - - if (*cp == '\0') - return strvec; - } + XFREE (MTYPE_TMP, copystart); + return strvec; } /* Free allocated string vector. */ @@ -311,397 +295,280 @@ cmd_free_strvec (vector v) vector_free (v); } -/** - * State structure for command format parser. Tracks - * parse tree position and miscellaneous state variables. - * Used when building a command vector from format strings. - */ -struct format_parser_state -{ - vector topvect; /* Top level vector */ - vector intvect; /* Intermediate level vector, used when there's - a multiple in a keyword. */ - vector curvect; /* current vector where read tokens should be - appended. */ - - const char *string; /* pointer to command string, not modified */ - const char *cp; /* pointer in command string, moved along while - parsing */ - const char *dp; /* pointer in description string, moved along while - parsing */ - - int in_keyword; /* flag to remember if we are in a keyword group */ - int in_multiple; /* flag to remember if we are in a multiple group */ - int just_read_word; /* flag to remember if the last thing we read was a - real word and not some abstract token */ -}; - -static void -format_parser_error(struct format_parser_state *state, const char *message) +/* Return prompt character of specified node. */ +const char * +cmd_prompt (enum node_type node) { - int offset = state->cp - state->string + 1; + struct cmd_node *cnode; - fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string); - fprintf(stderr, " %*c\n", offset, '^'); - fprintf(stderr, "%s at offset %d.\n", message, offset); - fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n"); - exit(1); + cnode = vector_slot (cmdvec, node); + return cnode->prompt; } -/** - * Reads out one section of a help string from state->dp. - * Leading whitespace is trimmed and the string is read until - * a newline is reached. - * - * @param[out] state format parser state - * @return the help string token read - */ -static char * -format_parser_desc_str(struct format_parser_state *state) +static bool +cmd_nodes_link (struct graph_node *from, struct graph_node *to) { - const char *cp, *start; - char *token; - int strlen; - - cp = state->dp; - - if (cp == NULL) - return NULL; - - /* Skip white spaces. */ - while (isspace ((int) *cp) && *cp != '\0') - cp++; - - /* Return if there is only white spaces */ - if (*cp == '\0') - return NULL; - - start = cp; - - while (!(*cp == '\r' || *cp == '\n') && *cp != '\0') - cp++; - - strlen = cp - start; - token = XMALLOC (MTYPE_CMD_TOKENS, strlen + 1); - memcpy (token, start, strlen); - *(token + strlen) = '\0'; - - state->dp = cp; - - return token; + for (size_t i = 0; i < vector_active (from->to); i++) + if (vector_slot (from->to, i) == to) + return true; + return false; } -/** - * Transitions format parser state into keyword parsing mode. - * A cmd_token struct, `token`, representing this keyword token is initialized - * and appended to state->curvect. token->keyword is initialized as a vector of - * vector, a new vector is initialized and added to token->keyword, and - * state->curvect is set to point at this vector. When control returns to the - * caller newly parsed tokens will be added to this vector. - * - * In short: - * state->curvect[HEAD] = new cmd_token - * state->curvect[HEAD]->keyword[0] = new vector - * state->curvect = state->curvect[HEAD]->keyword[0] - * - * @param[out] state state struct to transition +static bool cmd_nodes_equal (struct graph_node *ga, struct graph_node *gb); + +/* returns a single node to be excluded as "next" from iteration + * - for JOIN_TKN, never continue back to the FORK_TKN + * - in all other cases, don't try the node itself (in case of "...") */ -static void -format_parser_begin_keyword(struct format_parser_state *state) +static inline struct graph_node * +cmd_loopstop(struct graph_node *gn) { - struct cmd_token *token; - vector keyword_vect; + struct cmd_token *tok = gn->data; + if (tok->type == JOIN_TKN) + return tok->forkjoin; + else + return gn; +} - if (state->in_keyword - || state->in_multiple) - format_parser_error(state, "Unexpected '{'"); +static bool +cmd_subgraph_equal (struct graph_node *ga, struct graph_node *gb, + struct graph_node *a_join) +{ + size_t i, j; + struct graph_node *a_fork, *b_fork; + a_fork = cmd_loopstop (ga); + b_fork = cmd_loopstop (gb); - state->cp++; - state->in_keyword = 1; + if (vector_active (ga->to) != vector_active (gb->to)) + return false; + for (i = 0; i < vector_active (ga->to); i++) + { + struct graph_node *cga = vector_slot (ga->to, i); - token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); - token->type = TOKEN_KEYWORD; - token->keyword = vector_init(VECTOR_MIN_SIZE); + for (j = 0; j < vector_active (gb->to); j++) + { + struct graph_node *cgb = vector_slot (gb->to, i); - keyword_vect = vector_init(VECTOR_MIN_SIZE); - vector_set(token->keyword, keyword_vect); + if (cga == a_fork && cgb != b_fork) + continue; + if (cga == a_fork && cgb == b_fork) + break; - vector_set(state->curvect, token); - state->curvect = keyword_vect; + if (cmd_nodes_equal (cga, cgb)) + { + if (cga == a_join) + break; + if (cmd_subgraph_equal (cga, cgb, a_join)) + break; + } + } + if (j == vector_active (gb->to)) + return false; + } + return true; } -/** - * Transitions format parser state into multiple parsing mode. - * A cmd_token struct, `token`, representing this multiple token is initialized - * and appended to state->curvect. token->multiple is initialized as a vector - * of cmd_token and state->curvect is set to point at token->multiple. If - * state->curvect != state->topvect (i.e. this multiple token is nested inside - * another composite token) then a pointer to state->curvect is saved in - * state->intvect. - * - * In short: - * state->curvect[HEAD] = new cmd_token - * state->curvect[HEAD]->multiple = new vector - * state->intvect = state->curvect IFF nested token - * state->curvect = state->curvect[HEAD]->multiple - * - * @param[out] state state struct to transition - */ -static void -format_parser_begin_multiple(struct format_parser_state *state) +/* deep compare -- for FORK_TKN, the entire subgraph is compared. + * this is what's needed since we're not currently trying to partially + * merge subgraphs */ +static bool +cmd_nodes_equal (struct graph_node *ga, struct graph_node *gb) { - struct cmd_token *token; + struct cmd_token *a = ga->data, *b = gb->data; - if (state->in_keyword == 1) - format_parser_error(state, "Keyword starting with '('"); + if (a->type != b->type || a->allowrepeat != b->allowrepeat) + return false; + if (a->type < SPECIAL_TKN && strcmp (a->text, b->text)) + return false; + /* one a ..., the other not. */ + if (cmd_nodes_link (ga, ga) != cmd_nodes_link (gb, gb)) + return false; - if (state->in_multiple) - format_parser_error(state, "Nested group"); - - state->cp++; - state->in_multiple = 1; - state->just_read_word = 0; + switch (a->type) + { + case RANGE_TKN: + return a->min == b->min && a->max == b->max; - token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); - token->type = TOKEN_MULTIPLE; - token->multiple = vector_init(VECTOR_MIN_SIZE); + case FORK_TKN: + /* one is keywords, the other just option or selector ... */ + if (cmd_nodes_link (a->forkjoin, ga) != cmd_nodes_link (b->forkjoin, gb)) + return false; + if (cmd_nodes_link (ga, a->forkjoin) != cmd_nodes_link (gb, b->forkjoin)) + return false; + return cmd_subgraph_equal (ga, gb, a->forkjoin); - vector_set(state->curvect, token); - if (state->curvect != state->topvect) - state->intvect = state->curvect; - state->curvect = token->multiple; + default: + return true; + } } -/** - * Transition format parser state out of keyword parsing mode. - * This function is called upon encountering '}'. - * state->curvect is reassigned to the top level vector (as - * keywords cannot be nested) and state flags are set appropriately. - * - * @param[out] state state struct to transition - */ static void -format_parser_end_keyword(struct format_parser_state *state) +cmd_fork_bump_attr (struct graph_node *gn, struct graph_node *join, + u_char attr) { - if (state->in_multiple - || !state->in_keyword) - format_parser_error(state, "Unexpected '}'"); + size_t i; + struct cmd_token *tok = gn->data; + struct graph_node *stop = cmd_loopstop (gn); - if (state->in_keyword == 1) - format_parser_error(state, "Empty keyword group"); - - state->cp++; - state->in_keyword = 0; - state->curvect = state->topvect; + tok->attr = attr; + for (i = 0; i < vector_active (gn->to); i++) + { + struct graph_node *next = vector_slot (gn->to, i); + if (next == stop || next == join) + continue; + cmd_fork_bump_attr (next, join, attr); + } } -/** - * Transition format parser state out of multiple parsing mode. - * This function is called upon encountering ')'. - * state->curvect is reassigned to its parent vector (state->intvect - * if the multiple token being exited was nested inside another token, - * state->topvect otherwise) and state flags are set appropriately. +/* move an entire subtree from the temporary graph resulting from + * parse() into the permanent graph for the command node. * - * @param[out] state state struct to transition + * this touches rather deeply into the graph code unfortunately. */ static void -format_parser_end_multiple(struct format_parser_state *state) +cmd_reparent_tree (struct graph *fromgraph, struct graph *tograph, + struct graph_node *node) { - char *dummy; - - if (!state->in_multiple) - format_parser_error(state, "Unexpected ')'"); + struct graph_node *stop = cmd_loopstop (node); + size_t i; - if (vector_active(state->curvect) == 0) - format_parser_error(state, "Empty multiple section"); + for (i = 0; i < vector_active (fromgraph->nodes); i++) + if (vector_slot (fromgraph->nodes, i) == node) + { + /* agressive iteration punching through subgraphs - may hit some + * nodes twice. reparent only if found on old graph */ + vector_unset (fromgraph->nodes, i); + vector_set (tograph->nodes, node); + break; + } - if (!state->just_read_word) + for (i = 0; i < vector_active (node->to); i++) { - /* There are constructions like - * 'show ip ospf database ... (self-originate|)' - * in use. - * The old parser reads a description string for the - * word '' between |) which will never match. - * Simulate this behvaior by dropping the next desc - * string in such a case. */ - - dummy = format_parser_desc_str(state); - XFREE(MTYPE_CMD_TOKENS, dummy); + struct graph_node *next = vector_slot (node->to, i); + if (next != stop) + cmd_reparent_tree (fromgraph, tograph, next); } - - state->cp++; - state->in_multiple = 0; - - if (state->intvect) - state->curvect = state->intvect; - else - state->curvect = state->topvect; } -/** - * Format parser handler for pipe '|' character. - * This character separates subtokens in multiple and keyword type tokens. - * If the current token is a multiple keyword, the position pointer is - * simply moved past the pipe and state flags are set appropriately. - * If the current token is a keyword token, the position pointer is moved - * past the pipe. Then the cmd_token struct for the keyword is fetched and - * a new vector of cmd_token is appended to its vector of vector. Finally - * state->curvect is set to point at this new vector. - * - * In short: - * state->curvect = state->topvect[HEAD]->keyword[HEAD] = new vector - * - * @param[out] state state struct to transition - */ static void -format_parser_handle_pipe(struct format_parser_state *state) +cmd_free_recur (struct graph *graph, struct graph_node *node, + struct graph_node *stop) { - struct cmd_token *keyword_token; - vector keyword_vect; + struct graph_node *next, *nstop; - if (state->in_multiple) - { - state->just_read_word = 0; - state->cp++; - } - else if (state->in_keyword) + for (size_t i = vector_active (node->to); i; i--) { - state->in_keyword = 1; - state->cp++; - - keyword_token = vector_slot(state->topvect, - vector_active(state->topvect) - 1); - keyword_vect = vector_init(VECTOR_MIN_SIZE); - vector_set(keyword_token->keyword, keyword_vect); - state->curvect = keyword_vect; - } - else - { - format_parser_error(state, "Unexpected '|'"); + next = vector_slot (node->to, i - 1); + if (next == stop) + continue; + nstop = cmd_loopstop (next); + if (nstop != next) + cmd_free_recur (graph, next, nstop); + cmd_free_recur (graph, nstop, stop); } + graph_delete_node (graph, node); } -/** - * Format parser handler for terminal tokens. - * Parses the token, appends it to state->curvect, and sets - * state flags appropriately. - * - * @param[out] state state struct for current format parser state - */ static void -format_parser_read_word(struct format_parser_state *state) +cmd_free_node (struct graph *graph, struct graph_node *node) { - const char *start; - int len; - char *cmd; - struct cmd_token *token; - - start = state->cp; - - while (state->cp[0] != '\0' - && !strchr("\r\n(){}|", state->cp[0]) - && !isspace((int)state->cp[0])) - state->cp++; - - len = state->cp - start; - cmd = XMALLOC(MTYPE_CMD_TOKENS, len + 1); - memcpy(cmd, start, len); - cmd[len] = '\0'; - - token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); - token->type = TOKEN_TERMINAL; - if (strcmp (cmd, "A.B.C.D") == 0) - token->terminal = TERMINAL_IPV4; - else if (strcmp (cmd, "A.B.C.D/M") == 0) - token->terminal = TERMINAL_IPV4_PREFIX; - else if (strcmp (cmd, "X:X::X:X") == 0) - token->terminal = TERMINAL_IPV6; - else if (strcmp (cmd, "X:X::X:X/M") == 0) - token->terminal = TERMINAL_IPV6_PREFIX; - else if (cmd[0] == '[') - token->terminal = TERMINAL_OPTION; - else if (cmd[0] == '.') - token->terminal = TERMINAL_VARARG; - else if (cmd[0] == '<') - token->terminal = TERMINAL_RANGE; - else if (cmd[0] >= 'A' && cmd[0] <= 'Z') - token->terminal = TERMINAL_VARIABLE; - else - token->terminal = TERMINAL_LITERAL; - - token->cmd = cmd; - token->desc = format_parser_desc_str(state); - vector_set(state->curvect, token); - - if (state->in_keyword == 1) - state->in_keyword = 2; - - state->just_read_word = 1; + struct cmd_token *tok = node->data; + if (tok->type == JOIN_TKN) + cmd_free_recur (graph, tok->forkjoin, node); + graph_delete_node (graph, node); } -/** - * Parse a given command format string and build a tree of tokens from - * it that is suitable to be used by the command subsystem. - * - * @param string Command format string. - * @param descstr Description string. - * @return A vector of struct cmd_token representing the given command, - * or NULL on error. +/* recursive graph merge. call with + * old ~= new + * (which holds true for old == START_TKN, new == START_TKN) */ -static vector -cmd_parse_format(const char *string, const char *descstr) +static void +cmd_merge_nodes (struct graph *oldgraph, struct graph *newgraph, + struct graph_node *old, struct graph_node *new, + int direction) { - struct format_parser_state state; + struct cmd_token *tok; + struct graph_node *old_skip, *new_skip; + old_skip = cmd_loopstop (old); + new_skip = cmd_loopstop (new); - if (string == NULL) - return NULL; + assert (direction == 1 || direction == -1); - memset(&state, 0, sizeof(state)); - state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE); - state.cp = state.string = string; - state.dp = descstr; + tok = old->data; + tok->refcnt += direction; - while (1) + size_t j, i; + for (j = 0; j < vector_active (new->to); j++) { - while (isspace((int)state.cp[0]) && state.cp[0] != '\0') - state.cp++; + struct graph_node *cnew = vector_slot (new->to, j); + if (cnew == new_skip) + continue; - switch (state.cp[0]) + for (i = 0; i < vector_active (old->to); i++) { - case '\0': - if (state.in_keyword - || state.in_multiple) - format_parser_error(&state, "Unclosed group/keyword"); - return state.topvect; - case '{': - format_parser_begin_keyword(&state); - break; - case '(': - format_parser_begin_multiple(&state); - break; - case '}': - format_parser_end_keyword(&state); - break; - case ')': - format_parser_end_multiple(&state); - break; - case '|': - format_parser_handle_pipe(&state); - break; - default: - format_parser_read_word(&state); + struct graph_node *cold = vector_slot (old->to, i); + if (cold == old_skip) + continue; + + if (cmd_nodes_equal (cold, cnew)) + { + struct cmd_token *told = cold->data, *tnew = cnew->data; + + if (told->type == END_TKN) + { + if (direction < 0) + { + graph_delete_node (oldgraph, vector_slot (cold->to, 0)); + graph_delete_node (oldgraph, cold); + } + else + /* force no-match handling to install END_TKN */ + i = vector_active (old->to); + break; + } + + /* the entire fork compared as equal, we continue after it. */ + if (told->type == FORK_TKN) + { + if (tnew->attr < told->attr && direction > 0) + cmd_fork_bump_attr (cold, told->forkjoin, tnew->attr); + /* XXX: no reverse bump on uninstall */ + told = (cold = told->forkjoin)->data; + tnew = (cnew = tnew->forkjoin)->data; + } + if (tnew->attr < told->attr) + told->attr = tnew->attr; + + cmd_merge_nodes (oldgraph, newgraph, cold, cnew, direction); + break; + } + } + /* nothing found => add new to old */ + if (i == vector_active (old->to) && direction > 0) + { + assert (vector_count (cnew->from) == + cmd_nodes_link (cnew, cnew) ? 2 : 1); + graph_remove_edge (new, cnew); + + cmd_reparent_tree (newgraph, oldgraph, cnew); + + graph_add_edge (old, cnew); } } + + if (!tok->refcnt) + cmd_free_node (oldgraph, old); } -/* Return prompt character of specified node. */ -const char * -cmd_prompt (enum node_type node) +void +cmd_merge_graphs (struct graph *old, struct graph *new, int direction) { - struct cmd_node *cnode; + assert (vector_active (old->nodes) >= 1); + assert (vector_active (new->nodes) >= 1); - cnode = vector_slot (cmdvec, node); - return cnode->prompt; + cmd_merge_nodes (old, new, + vector_slot (old->nodes, 0), vector_slot (new->nodes, 0), + direction); } /* Install a command into a node. */ @@ -709,7 +576,7 @@ void install_element (enum node_type ntype, struct cmd_element *cmd) { struct cmd_node *cnode; - + /* cmd_init hasn't been called */ if (!cmdvec) { @@ -717,41 +584,92 @@ install_element (enum node_type ntype, struct cmd_element *cmd) __func__); return; } - + cnode = vector_slot (cmdvec, ntype); - if (cnode == NULL) + if (cnode == NULL) { fprintf (stderr, "Command node %d doesn't exist, please check it\n", - ntype); - exit (1); + ntype); + exit (EXIT_FAILURE); } - + if (hash_lookup (cnode->cmd_hash, cmd) != NULL) { - fprintf (stderr, + fprintf (stderr, "Multiple command installs to node %d of command:\n%s\n", ntype, cmd->string); return; } - + assert (hash_get (cnode->cmd_hash, cmd, hash_alloc_intern)); - + + struct graph *graph = graph_new(); + struct cmd_token *token = new_cmd_token (START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token); + + command_parse_format (graph, cmd); + cmd_merge_graphs (cnode->cmdgraph, graph, +1); + graph_delete_graph (graph); + vector_set (cnode->cmd_vector, cmd); - if (cmd->tokens == NULL) - cmd->tokens = cmd_parse_format(cmd->string, cmd->doc); if (ntype == VIEW_NODE) install_element (ENABLE_NODE, cmd); } +void +uninstall_element (enum node_type ntype, struct cmd_element *cmd) +{ + struct cmd_node *cnode; + + /* cmd_init hasn't been called */ + if (!cmdvec) + { + fprintf (stderr, "%s called before cmd_init, breakage likely\n", + __func__); + return; + } + + cnode = vector_slot (cmdvec, ntype); + + if (cnode == NULL) + { + fprintf (stderr, "Command node %d doesn't exist, please check it\n", + ntype); + exit (EXIT_FAILURE); + } + + if (hash_release (cnode->cmd_hash, cmd) == NULL) + { + fprintf (stderr, + "Trying to uninstall non-installed command (node %d):\n%s\n", + ntype, cmd->string); + return; + } + + vector_unset_value (cnode->cmd_vector, cmd); + + struct graph *graph = graph_new(); + struct cmd_token *token = new_cmd_token (START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token); + + command_parse_format (graph, cmd); + cmd_merge_graphs (cnode->cmdgraph, graph, -1); + graph_delete_graph (graph); + + if (ntype == VIEW_NODE) + uninstall_element (ENABLE_NODE, cmd); +} + + static const unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; static void to64(char *s, long v, int n) { - while (--n >= 0) + while (--n >= 0) { *s++ = itoa64[v&0x3f]; v >>= 6; @@ -766,7 +684,7 @@ zencrypt (const char *passwd) char *crypt (const char *, const char *); gettimeofday(&tv,0); - + to64(&salt[0], random(), 3); to64(&salt[3], tv.tv_usec, 3); salt[5] = '\0'; @@ -784,9 +702,9 @@ config_write_host (struct vty *vty) if (host.encrypt) { if (host.password_encrypt) - vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE); + vty_out (vty, "password 8 %s%s", host.password_encrypt, VTY_NEWLINE); if (host.enable_encrypt) - vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE); + vty_out (vty, "enable password 8 %s%s", host.enable_encrypt, VTY_NEWLINE); } else { @@ -799,17 +717,17 @@ config_write_host (struct vty *vty) if (zlog_default->default_lvl != LOG_DEBUG) { vty_out (vty, "! N.B. The 'log trap' command is deprecated.%s", - VTY_NEWLINE); + VTY_NEWLINE); vty_out (vty, "log trap %s%s", - zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); + zlog_priority[zlog_default->default_lvl], VTY_NEWLINE); } if (host.logfile && (zlog_default->maxlvl[ZLOG_DEST_FILE] != ZLOG_DISABLED)) { vty_out (vty, "log file %s", host.logfile); if (zlog_default->maxlvl[ZLOG_DEST_FILE] != zlog_default->default_lvl) - vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]); + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_FILE]]); vty_out (vty, "%s", VTY_NEWLINE); } @@ -817,8 +735,8 @@ config_write_host (struct vty *vty) { vty_out (vty, "log stdout"); if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != zlog_default->default_lvl) - vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]); + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_STDOUT]]); vty_out (vty, "%s", VTY_NEWLINE); } @@ -826,27 +744,27 @@ config_write_host (struct vty *vty) vty_out(vty,"no log monitor%s",VTY_NEWLINE); else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] != zlog_default->default_lvl) vty_out(vty,"log monitor %s%s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE); + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_MONITOR]],VTY_NEWLINE); if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) { vty_out (vty, "log syslog"); if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != zlog_default->default_lvl) - vty_out (vty, " %s", - zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]); + vty_out (vty, " %s", + zlog_priority[zlog_default->maxlvl[ZLOG_DEST_SYSLOG]]); vty_out (vty, "%s", VTY_NEWLINE); } if (zlog_default->facility != LOG_DAEMON) vty_out (vty, "log facility %s%s", - facility_name(zlog_default->facility), VTY_NEWLINE); + facility_name(zlog_default->facility), VTY_NEWLINE); if (zlog_default->record_priority == 1) vty_out (vty, "log record-priority%s", VTY_NEWLINE); if (zlog_default->timestamp_precision > 0) vty_out (vty, "log timestamp precision %d%s", - zlog_default->timestamp_precision, VTY_NEWLINE); + zlog_default->timestamp_precision, VTY_NEWLINE); if (host.advanced) vty_out (vty, "service advanced-vty%s", VTY_NEWLINE); @@ -856,7 +774,7 @@ config_write_host (struct vty *vty) if (host.lines >= 0) vty_out (vty, "service terminal-length %d%s", host.lines, - VTY_NEWLINE); + VTY_NEWLINE); if (host.motdfile) vty_out (vty, "banner motd file %s%s", host.motdfile, VTY_NEWLINE); @@ -866,1382 +784,132 @@ config_write_host (struct vty *vty) return 1; } -/* Utility function for getting command vector. */ -static vector -cmd_node_vector (vector v, enum node_type ntype) +/* Utility function for getting command graph. */ +static struct graph * +cmd_node_graph (vector v, enum node_type ntype) { struct cmd_node *cnode = vector_slot (v, ntype); - return cnode->cmd_vector; -} - -/* Completion match types. */ -enum match_type -{ - no_match, - extend_match, - ipv4_prefix_match, - ipv4_match, - ipv6_prefix_match, - ipv6_match, - range_match, - vararg_match, - partly_match, - exact_match -}; - -static enum match_type -cmd_ipv4_match (const char *str) -{ - const char *sp; - int dots = 0, nums = 0; - char buf[4]; - - if (str == NULL) - return partly_match; - - for (;;) - { - memset (buf, 0, sizeof (buf)); - sp = str; - while (*str != '\0') - { - if (*str == '.') - { - if (dots >= 3) - return no_match; - - if (*(str + 1) == '.') - return no_match; - - if (*(str + 1) == '\0') - return partly_match; - - dots++; - break; - } - if (!isdigit ((int) *str)) - return no_match; - - str++; - } - - if (str - sp > 3) - return no_match; - - strncpy (buf, sp, str - sp); - if (atoi (buf) > 255) - return no_match; - - nums++; - - if (*str == '\0') - break; - - str++; - } - - if (nums < 4) - return partly_match; - - return exact_match; -} - -static enum match_type -cmd_ipv4_prefix_match (const char *str) -{ - const char *sp; - int dots = 0; - char buf[4]; - - if (str == NULL) - return partly_match; - - for (;;) - { - memset (buf, 0, sizeof (buf)); - sp = str; - while (*str != '\0' && *str != '/') - { - if (*str == '.') - { - if (dots == 3) - return no_match; - - if (*(str + 1) == '.' || *(str + 1) == '/') - return no_match; - - if (*(str + 1) == '\0') - return partly_match; - - dots++; - break; - } - - if (!isdigit ((int) *str)) - return no_match; - - str++; - } - - if (str - sp > 3) - return no_match; - - strncpy (buf, sp, str - sp); - if (atoi (buf) > 255) - return no_match; - - if (dots == 3) - { - if (*str == '/') - { - if (*(str + 1) == '\0') - return partly_match; - - str++; - break; - } - else if (*str == '\0') - return partly_match; - } - - if (*str == '\0') - return partly_match; - - str++; - } - - sp = str; - while (*str != '\0') - { - if (!isdigit ((int) *str)) - return no_match; - - str++; - } - - if (atoi (sp) > 32) - return no_match; - - return exact_match; -} - -#define IPV6_ADDR_STR "0123456789abcdefABCDEF:." -#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./" - -#ifdef HAVE_IPV6 - -static 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; - - /* use inet_pton that has a better support, - * for example inet_pton can support the automatic addresses: - * ::1.2.3.4 - */ - ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); - - if (ret == 1) - return exact_match; - - return no_match; -} - -static 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; - enum match_type ret; - - 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); - - ret = exact_match; - if (!mask) - ret = partly_match; - else - { - /* validate prefix */ - if (inet_pton(AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1) - ret = no_match; - else - { - /* validate mask */ - nmask = strtol (mask, &endptr, 10); - if (*endptr != '\0' || nmask < 0 || nmask > 128) - ret = no_match; - } - } - - XFREE(MTYPE_TMP, dupe); - - return ret; + return cnode->cmdgraph; } -#endif /* HAVE_IPV6 */ - -#define DECIMAL_STRLEN_MAX 20 - static int -cmd_range_match (const char *range, const char *str) -{ - char *p; - char buf[DECIMAL_STRLEN_MAX + 1]; - char *endptr = NULL; - signed long long min, max, val; - - if (str == NULL) +cmd_try_do_shortcut (enum node_type node, char* first_word) { + if ( first_word != NULL && + node != AUTH_NODE && + node != VIEW_NODE && + node != AUTH_ENABLE_NODE && + node != ENABLE_NODE && + 0 == strcmp( "do", first_word ) ) return 1; - - val = strtoll (str, &endptr, 10); - if (*endptr != '\0') - return 0; - val = llabs(val); - - range++; - p = strchr (range, '-'); - if (p == NULL) - return 0; - if (p - range > DECIMAL_STRLEN_MAX) - return 0; - strncpy (buf, range, p - range); - buf[p - range] = '\0'; - min = strtoll (buf, &endptr, 10); - if (*endptr != '\0') - return 0; - - range = p + 1; - p = strchr (range, '>'); - if (p == NULL) - return 0; - if (p - range > DECIMAL_STRLEN_MAX) - return 0; - strncpy (buf, range, p - range); - buf[p - range] = '\0'; - max = strtoll (buf, &endptr, 10); - if (*endptr != '\0') - return 0; - - if (val < min || val > max) - return 0; - - return 1; -} - -static enum match_type -cmd_word_match(struct cmd_token *token, - enum filter_type filter, - const char *word) -{ - const char *str; - enum match_type match_type; - - str = token->cmd; - - if (filter == FILTER_RELAXED) - if (!word || !strlen(word)) - return partly_match; - - if (!word) - return no_match; - - switch (token->terminal) - { - case TERMINAL_VARARG: - return vararg_match; - - case TERMINAL_RANGE: - if (cmd_range_match(str, word)) - return range_match; - break; - - case TERMINAL_IPV6: - match_type = cmd_ipv6_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv6_match; - break; - - case TERMINAL_IPV6_PREFIX: - match_type = cmd_ipv6_prefix_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv6_prefix_match; - break; - - case TERMINAL_IPV4: - match_type = cmd_ipv4_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv4_match; - break; - - case TERMINAL_IPV4_PREFIX: - match_type = cmd_ipv4_prefix_match(word); - if ((filter == FILTER_RELAXED && match_type != no_match) - || (filter == FILTER_STRICT && match_type == exact_match)) - return ipv4_prefix_match; - break; - - case TERMINAL_OPTION: - case TERMINAL_VARIABLE: - return extend_match; - - case TERMINAL_LITERAL: - if (filter == FILTER_RELAXED && !strncmp(str, word, strlen(word))) - { - if (!strcmp(str, word)) - return exact_match; - return partly_match; - } - if (filter == FILTER_STRICT && !strcmp(str, word)) - return exact_match; - break; - - default: - assert (0); - } - - return no_match; -} - -struct cmd_matcher -{ - struct cmd_element *cmd; /* The command element the matcher is using */ - enum filter_type filter; /* Whether to use strict or relaxed matching */ - vector vline; /* The tokenized commandline which is to be matched */ - unsigned int index; /* The index up to which matching should be done */ - - /* If set, construct a list of matches at the position given by index */ - enum match_type *match_type; - vector *match; - - unsigned int word_index; /* iterating over vline */ -}; - -static int -push_argument(int *argc, const char **argv, const char *arg) -{ - if (!arg || !strlen(arg)) - arg = NULL; - - if (!argc || !argv) - return 0; - - if (*argc >= CMD_ARGC_MAX) - return -1; - - argv[(*argc)++] = arg; return 0; } -static void -cmd_matcher_record_match(struct cmd_matcher *matcher, - enum match_type match_type, - struct cmd_token *token) -{ - if (matcher->word_index != matcher->index) - return; - - if (matcher->match) - { - if (!*matcher->match) - *matcher->match = vector_init(VECTOR_MIN_SIZE); - vector_set(*matcher->match, token); - } - - if (matcher->match_type) - { - if (match_type > *matcher->match_type) - *matcher->match_type = match_type; - } -} - -static int -cmd_matcher_words_left(struct cmd_matcher *matcher) -{ - return matcher->word_index < vector_active(matcher->vline); -} - -static const char* -cmd_matcher_get_word(struct cmd_matcher *matcher) -{ - assert(cmd_matcher_words_left(matcher)); - - return vector_slot(matcher->vline, matcher->word_index); -} - -static enum matcher_rv -cmd_matcher_match_terminal(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv) -{ - const char *word; - enum match_type word_match; - - assert(token->type == TOKEN_TERMINAL); - - if (!cmd_matcher_words_left(matcher)) - { - if (token->terminal == TERMINAL_OPTION) - return MATCHER_OK; /* missing optional args are NOT pushed as NULL */ - else - return MATCHER_INCOMPLETE; - } - - word = cmd_matcher_get_word(matcher); - word_match = cmd_word_match(token, matcher->filter, word); - if (word_match == no_match) - return MATCHER_NO_MATCH; - - /* We have to record the input word as argument if it matched - * against a variable. */ - if (TERMINAL_RECORD (token->terminal)) - { - if (push_argument(argc, argv, word)) - return MATCHER_EXCEED_ARGC_MAX; - } - - cmd_matcher_record_match(matcher, word_match, token); - - matcher->word_index++; - - /* A vararg token should consume all left over words as arguments */ - if (token->terminal == TERMINAL_VARARG) - while (cmd_matcher_words_left(matcher)) - { - word = cmd_matcher_get_word(matcher); - if (word && strlen(word)) - push_argument(argc, argv, word); - matcher->word_index++; - } - - return MATCHER_OK; -} - -static enum matcher_rv -cmd_matcher_match_multiple(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv) -{ - enum match_type multiple_match; - unsigned int multiple_index; - const char *word; - const char *arg = NULL; - struct cmd_token *word_token; - enum match_type word_match; - - assert(token->type == TOKEN_MULTIPLE); - - multiple_match = no_match; - - if (!cmd_matcher_words_left(matcher)) - return MATCHER_INCOMPLETE; - - word = cmd_matcher_get_word(matcher); - for (multiple_index = 0; - multiple_index < vector_active(token->multiple); - multiple_index++) - { - word_token = vector_slot(token->multiple, multiple_index); - - word_match = cmd_word_match(word_token, matcher->filter, word); - if (word_match == no_match) - continue; - - cmd_matcher_record_match(matcher, word_match, word_token); - - if (word_match > multiple_match) - { - multiple_match = word_match; - arg = word; - } - /* To mimic the behavior of the old command implementation, we - * tolerate any ambiguities here :/ */ - } - - matcher->word_index++; - - if (multiple_match == no_match) - return MATCHER_NO_MATCH; - - if (push_argument(argc, argv, arg)) - return MATCHER_EXCEED_ARGC_MAX; - - return MATCHER_OK; -} - -static enum matcher_rv -cmd_matcher_read_keywords(struct cmd_matcher *matcher, - struct cmd_token *token, - vector args_vector) -{ - unsigned int i; - unsigned long keyword_mask; - unsigned int keyword_found; - enum match_type keyword_match; - enum match_type word_match; - vector keyword_vector; - struct cmd_token *word_token; - const char *word; - int keyword_argc; - const char **keyword_argv; - enum matcher_rv rv = MATCHER_OK; - - keyword_mask = 0; - while (1) - { - if (!cmd_matcher_words_left(matcher)) - return MATCHER_OK; - - word = cmd_matcher_get_word(matcher); - - keyword_found = -1; - keyword_match = no_match; - for (i = 0; i < vector_active(token->keyword); i++) - { - if (keyword_mask & (1 << i)) - continue; - - keyword_vector = vector_slot(token->keyword, i); - word_token = vector_slot(keyword_vector, 0); - - word_match = cmd_word_match(word_token, matcher->filter, word); - if (word_match == no_match) - continue; - - cmd_matcher_record_match(matcher, word_match, word_token); - - if (word_match > keyword_match) - { - keyword_match = word_match; - keyword_found = i; - } - else if (word_match == keyword_match) - { - if (matcher->word_index != matcher->index || args_vector) - return MATCHER_AMBIGUOUS; - } - } - - if (keyword_found == (unsigned int)-1) - return MATCHER_NO_MATCH; - - matcher->word_index++; - - if (matcher->word_index > matcher->index) - return MATCHER_OK; - - keyword_mask |= (1 << keyword_found); - - if (args_vector) - { - keyword_argc = 0; - keyword_argv = XMALLOC(MTYPE_TMP, (CMD_ARGC_MAX + 1) * sizeof(char*)); - /* We use -1 as a marker for unused fields as NULL might be a valid value */ - for (i = 0; i < CMD_ARGC_MAX + 1; i++) - keyword_argv[i] = (void*)-1; - vector_set_index(args_vector, keyword_found, keyword_argv); - } - else - { - keyword_argv = NULL; - } - - keyword_vector = vector_slot(token->keyword, keyword_found); - /* the keyword itself is at 0. We are only interested in the arguments, - * so start counting at 1. */ - for (i = 1; i < vector_active(keyword_vector); i++) - { - word_token = vector_slot(keyword_vector, i); - - switch (word_token->type) - { - case TOKEN_TERMINAL: - rv = cmd_matcher_match_terminal(matcher, word_token, - &keyword_argc, keyword_argv); - break; - case TOKEN_MULTIPLE: - rv = cmd_matcher_match_multiple(matcher, word_token, - &keyword_argc, keyword_argv); - break; - case TOKEN_KEYWORD: - assert(!"Keywords should never be nested."); - break; - } - - if (MATCHER_ERROR(rv)) - return rv; - - if (matcher->word_index > matcher->index) - return MATCHER_OK; - } - } - /* not reached */ -} - -static enum matcher_rv -cmd_matcher_build_keyword_args(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv, - vector keyword_args_vector) -{ - unsigned int i, j; - const char **keyword_args; - vector keyword_vector; - struct cmd_token *word_token; - const char *arg; - enum matcher_rv rv; - - rv = MATCHER_OK; - - if (keyword_args_vector == NULL) - return rv; - - for (i = 0; i < vector_active(token->keyword); i++) - { - keyword_vector = vector_slot(token->keyword, i); - keyword_args = vector_lookup(keyword_args_vector, i); - - if (vector_active(keyword_vector) == 1) - { - /* this is a keyword without arguments */ - if (keyword_args) - { - word_token = vector_slot(keyword_vector, 0); - arg = word_token->cmd; - XFREE (MTYPE_TMP, keyword_args); - } - else - { - arg = NULL; - } - - if (push_argument(argc, argv, arg)) - rv = MATCHER_EXCEED_ARGC_MAX; - } - else - { - /* this is a keyword with arguments */ - if (keyword_args) - { - /* the keyword was present, so just fill in the arguments */ - for (j = 0; keyword_args[j] != (void*)-1; j++) - if (push_argument(argc, argv, keyword_args[j])) - rv = MATCHER_EXCEED_ARGC_MAX; - XFREE(MTYPE_TMP, keyword_args); - } - else - { - /* the keyword was not present, insert NULL for the arguments - * the keyword would have taken. */ - for (j = 1; j < vector_active(keyword_vector); j++) - { - word_token = vector_slot(keyword_vector, j); - if ((word_token->type == TOKEN_TERMINAL - && TERMINAL_RECORD (word_token->terminal)) - || word_token->type == TOKEN_MULTIPLE) - { - if (push_argument(argc, argv, NULL)) - rv = MATCHER_EXCEED_ARGC_MAX; - } - } - } - } - } - vector_free(keyword_args_vector); - return rv; -} - -static enum matcher_rv -cmd_matcher_match_keyword(struct cmd_matcher *matcher, - struct cmd_token *token, - int *argc, const char **argv) -{ - vector keyword_args_vector; - enum matcher_rv reader_rv; - enum matcher_rv builder_rv; - - assert(token->type == TOKEN_KEYWORD); - - if (argc && argv) - keyword_args_vector = vector_init(VECTOR_MIN_SIZE); - else - keyword_args_vector = NULL; - - reader_rv = cmd_matcher_read_keywords(matcher, token, keyword_args_vector); - builder_rv = cmd_matcher_build_keyword_args(matcher, token, argc, - argv, keyword_args_vector); - /* keyword_args_vector is consumed by cmd_matcher_build_keyword_args */ - - if (!MATCHER_ERROR(reader_rv) && MATCHER_ERROR(builder_rv)) - return builder_rv; - - return reader_rv; -} - -static void -cmd_matcher_init(struct cmd_matcher *matcher, - struct cmd_element *cmd, - enum filter_type filter, - vector vline, - unsigned int index, - enum match_type *match_type, - vector *match) -{ - memset(matcher, 0, sizeof(*matcher)); - - matcher->cmd = cmd; - matcher->filter = filter; - matcher->vline = vline; - matcher->index = index; - - matcher->match_type = match_type; - if (matcher->match_type) - *matcher->match_type = no_match; - matcher->match = match; - - matcher->word_index = 0; -} - -static enum matcher_rv -cmd_element_match(struct cmd_element *cmd_element, - enum filter_type filter, - vector vline, - unsigned int index, - enum match_type *match_type, - vector *match, - int *argc, - const char **argv) -{ - struct cmd_matcher matcher; - unsigned int token_index; - enum matcher_rv rv = MATCHER_OK; - - cmd_matcher_init(&matcher, cmd_element, filter, - vline, index, match_type, match); - - if (argc != NULL) - *argc = 0; - - for (token_index = 0; - token_index < vector_active(cmd_element->tokens); - token_index++) - { - struct cmd_token *token = vector_slot(cmd_element->tokens, token_index); - - switch (token->type) - { - case TOKEN_TERMINAL: - rv = cmd_matcher_match_terminal(&matcher, token, argc, argv); - break; - case TOKEN_MULTIPLE: - rv = cmd_matcher_match_multiple(&matcher, token, argc, argv); - break; - case TOKEN_KEYWORD: - rv = cmd_matcher_match_keyword(&matcher, token, argc, argv); - } - - if (MATCHER_ERROR(rv)) - return rv; - - if (matcher.word_index > index) - return MATCHER_OK; - } - - /* return MATCHER_COMPLETE if all words were consumed */ - if (matcher.word_index >= vector_active(vline)) - return MATCHER_COMPLETE; - - /* return MATCHER_COMPLETE also if only an empty word is left. */ - if (matcher.word_index == vector_active(vline) - 1 - && (!vector_slot(vline, matcher.word_index) - || !strlen((char*)vector_slot(vline, matcher.word_index)))) - return MATCHER_COMPLETE; - - return MATCHER_NO_MATCH; /* command is too long to match */ -} - -/** - * Filter a given vector of commands against a given commandline and - * calculate possible completions. - * - * @param commands A vector of struct cmd_element*. Commands that don't - * match against the given command line will be overwritten - * with NULL in that vector. - * @param filter Either FILTER_RELAXED or FILTER_STRICT. This basically - * determines how incomplete commands are handled, compare with - * cmd_word_match for details. - * @param vline A vector of char* containing the tokenized commandline. - * @param index Only match up to the given token of the commandline. - * @param match_type Record the type of the best match here. - * @param matches Record the matches here. For each cmd_element in the commands - * vector, a match vector will be created in the matches vector. - * That vector will contain all struct command_token* of the - * cmd_element which matched against the given vline at the given - * index. - * @return A code specifying if an error occured. If all went right, it's - * CMD_SUCCESS. - */ -static int -cmd_vector_filter(vector commands, - enum filter_type filter, - vector vline, - unsigned int index, - enum match_type *match_type, - vector *matches) -{ - unsigned int i; - struct cmd_element *cmd_element; - enum match_type best_match; - enum match_type element_match; - enum matcher_rv matcher_rv; - - best_match = no_match; - *matches = vector_init(VECTOR_MIN_SIZE); - - for (i = 0; i < vector_active (commands); i++) - if ((cmd_element = vector_slot (commands, i)) != NULL) - { - vector_set_index(*matches, i, NULL); - matcher_rv = cmd_element_match(cmd_element, filter, - vline, index, - &element_match, - (vector*)&vector_slot(*matches, i), - NULL, NULL); - if (MATCHER_ERROR(matcher_rv)) - { - vector_slot(commands, i) = NULL; - if (matcher_rv == MATCHER_AMBIGUOUS) - return CMD_ERR_AMBIGUOUS; - if (matcher_rv == MATCHER_EXCEED_ARGC_MAX) - return CMD_ERR_EXEED_ARGC_MAX; - } - else if (element_match > best_match) - { - best_match = element_match; - } - } - *match_type = best_match; - return CMD_SUCCESS; -} - /** - * Check whether a given commandline is complete if used for a specific - * cmd_element. - * - * @param cmd_element A cmd_element against which the commandline should be - * checked. - * @param vline The tokenized commandline. - * @return 1 if the given commandline is complete, 0 otherwise. + * Compare function for cmd_token. + * Used with qsort to sort command completions. */ static int -cmd_is_complete(struct cmd_element *cmd_element, - vector vline) +compare_completions (const void *fst, const void *snd) { - enum matcher_rv rv; - - rv = cmd_element_match(cmd_element, - FILTER_RELAXED, - vline, -1, - NULL, NULL, - NULL, NULL); - return (rv == MATCHER_COMPLETE); + struct cmd_token *first = *(struct cmd_token **) fst, + *secnd = *(struct cmd_token **) snd; + return strcmp (first->text, secnd->text); } /** - * Parse a given commandline and construct a list of arguments for the - * given command_element. + * Takes a list of completions returned by command_complete, + * dedeuplicates them based on both text and description, + * sorts them, and returns them as a vector. * - * @param cmd_element The cmd_element for which we want to construct arguments. - * @param vline The tokenized commandline. - * @param argc Where to store the argument count. - * @param argv Where to store the argument list. Should be at least - * CMD_ARGC_MAX elements long. - * @return CMD_SUCCESS if everything went alright, an error otherwise. + * @param completions linked list of cmd_token + * @return deduplicated and sorted vector with */ -static int -cmd_parse(struct cmd_element *cmd_element, - vector vline, - int *argc, const char **argv) +vector +completions_to_vec (struct list *completions) { - enum matcher_rv rv = cmd_element_match(cmd_element, - FILTER_RELAXED, - vline, -1, - NULL, NULL, - argc, argv); - switch (rv) - { - case MATCHER_COMPLETE: - return CMD_SUCCESS; - - case MATCHER_NO_MATCH: - return CMD_ERR_NO_MATCH; + vector comps = vector_init (VECTOR_MIN_SIZE); - case MATCHER_AMBIGUOUS: - return CMD_ERR_AMBIGUOUS; - - case MATCHER_EXCEED_ARGC_MAX: - return CMD_ERR_EXEED_ARGC_MAX; + struct listnode *ln; + struct cmd_token *token, *cr = NULL; + unsigned int i, exists; + for (ALL_LIST_ELEMENTS_RO(completions,ln,token)) + { + if (token->type == END_TKN && (cr = token)) + continue; - default: - return CMD_ERR_INCOMPLETE; + // linear search for token in completions vector + exists = 0; + for (i = 0; i < vector_active (comps) && !exists; i++) + { + struct cmd_token *curr = vector_slot (comps, i); +#ifdef VTYSH_DEBUG + exists = !strcmp (curr->text, token->text) && + !strcmp (curr->desc, token->desc); +#else + exists = !strcmp (curr->text, token->text); +#endif /* VTYSH_DEBUG */ } -} - -/* Check ambiguous match */ -static int -is_cmd_ambiguous (vector cmd_vector, - const char *command, - vector matches, - enum match_type type) -{ - unsigned int i; - unsigned int j; - const char *str = NULL; - const char *matched = NULL; - vector match_vector; - struct cmd_token *cmd_token; - - if (command == NULL) - command = ""; - - for (i = 0; i < vector_active (matches); i++) - if ((match_vector = vector_slot (matches, i)) != NULL) - { - int match = 0; - - for (j = 0; j < vector_active (match_vector); j++) - if ((cmd_token = vector_slot (match_vector, j)) != NULL) - { - enum match_type ret; - - assert(cmd_token->type == TOKEN_TERMINAL); - if (cmd_token->type != TOKEN_TERMINAL) - continue; - - str = cmd_token->cmd; - - switch (type) - { - case exact_match: - if (!TERMINAL_RECORD (cmd_token->terminal) - && strcmp (command, str) == 0) - match++; - break; - case partly_match: - if (!TERMINAL_RECORD (cmd_token->terminal) - && strncmp (command, str, strlen (command)) == 0) - { - if (matched && strcmp (matched, str) != 0) - return 1; /* There is ambiguous match. */ - else - matched = str; - match++; - } - break; - case range_match: - if (cmd_range_match (str, command)) - { - if (matched && strcmp (matched, str) != 0) - return 1; - else - matched = str; - match++; - } - break; -#ifdef HAVE_IPV6 - case ipv6_match: - if (cmd_token->terminal == TERMINAL_IPV6) - match++; - break; - case ipv6_prefix_match: - if ((ret = cmd_ipv6_prefix_match (command)) != no_match) - { - if (ret == partly_match) - return 2; /* There is incomplete match. */ - - match++; - } - break; -#endif /* HAVE_IPV6 */ - case ipv4_match: - if (cmd_token->terminal == TERMINAL_IPV4) - match++; - break; - case ipv4_prefix_match: - if ((ret = cmd_ipv4_prefix_match (command)) != no_match) - { - if (ret == partly_match) - return 2; /* There is incomplete match. */ - - match++; - } - break; - case extend_match: - if (TERMINAL_RECORD (cmd_token->terminal)) - match++; - break; - case no_match: - default: - break; - } - } - if (!match) - vector_slot (cmd_vector, i) = NULL; - } - return 0; -} -/* If src matches dst return dst string, otherwise return NULL */ -static const char * -cmd_entry_function (const char *src, struct cmd_token *token) -{ - const char *dst = token->cmd; - - /* Skip variable arguments. */ - if (TERMINAL_RECORD (token->terminal)) - return NULL; - - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - /* Matched with input string. */ - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - - return NULL; -} - -/* If src matches dst return dst string, otherwise return NULL */ -/* This version will return the dst string always if it is - CMD_VARIABLE for '?' key processing */ -static const char * -cmd_entry_function_desc (const char *src, struct cmd_token *token) -{ - const char *dst = token->cmd; - - switch (token->terminal) - { - case TERMINAL_VARARG: - return dst; - - case TERMINAL_RANGE: - if (cmd_range_match (dst, src)) - return dst; - else - return NULL; - - case TERMINAL_IPV6: - if (cmd_ipv6_match (src)) - return dst; - else - return NULL; - - case TERMINAL_IPV6_PREFIX: - if (cmd_ipv6_prefix_match (src)) - return dst; - else - return NULL; - - case TERMINAL_IPV4: - if (cmd_ipv4_match (src)) - return dst; - else - return NULL; - - case TERMINAL_IPV4_PREFIX: - if (cmd_ipv4_prefix_match (src)) - return dst; - else - return NULL; - - /* Optional or variable commands always match on '?' */ - case TERMINAL_OPTION: - case TERMINAL_VARIABLE: - return dst; - - case TERMINAL_LITERAL: - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - else - return NULL; + if (!exists) + vector_set (comps, token); + } - default: - assert(0); - return NULL; - } -} + // sort completions + qsort (comps->index, + vector_active (comps), + sizeof (void *), + &compare_completions); -/** - * Check whether a string is already present in a vector of strings. - * @param v A vector of char*. - * @param str A char*. - * @return 0 if str is already present in the vector, 1 otherwise. - */ -static int -cmd_unique_string (vector v, const char *str) -{ - unsigned int i; - char *match; + // make <cr> the first element, if it is present + if (cr) + { + vector_set_index (comps, vector_active (comps), NULL); + memmove (comps->index + 1, comps->index, (comps->alloced - 1) * sizeof (void *)); + vector_set_index (comps, 0, cr); + } - for (i = 0; i < vector_active (v); i++) - if ((match = vector_slot (v, i)) != NULL) - if (strcmp (match, str) == 0) - return 0; - return 1; + return comps; } - /** - * Check whether a struct cmd_token matching a given string is already - * present in a vector of struct cmd_token. - * @param v A vector of struct cmd_token*. - * @param str A char* which should be searched for. - * @return 0 if there is a struct cmd_token* with its cmd matching str, - * 1 otherwise. + * Generates a vector of cmd_token representing possible completions + * on the current input. + * + * @param vline the vectorized input line + * @param vty the vty with the node to match on + * @param status pointer to matcher status code + * @return vector of struct cmd_token * with possible completions */ -static int -desc_unique_string (vector v, const char *str) -{ - unsigned int i; - struct cmd_token *token; - - for (i = 0; i < vector_active (v); i++) - if ((token = vector_slot (v, i)) != NULL) - if (strcmp (token->cmd, str) == 0) - return 0; - return 1; -} - -static int -cmd_try_do_shortcut (enum node_type node, char* first_word) { - if ( first_word != NULL && - node != AUTH_NODE && - node != VIEW_NODE && - node != AUTH_ENABLE_NODE && - node != ENABLE_NODE && - 0 == strcmp( "do", first_word ) ) - return 1; - return 0; -} - -static void -cmd_matches_free(vector *matches) -{ - unsigned int i; - vector cmd_matches; - - for (i = 0; i < vector_active(*matches); i++) - if ((cmd_matches = vector_slot(*matches, i)) != NULL) - vector_free(cmd_matches); - vector_free(*matches); - *matches = NULL; -} - -static int -cmd_describe_cmp(const void *a, const void *b) -{ - const struct cmd_token *first = *(struct cmd_token * const *)a; - const struct cmd_token *second = *(struct cmd_token * const *)b; - - return strcmp(first->cmd, second->cmd); -} - -static void -cmd_describe_sort(vector matchvec) -{ - qsort(matchvec->index, vector_active(matchvec), - sizeof(void*), cmd_describe_cmp); -} - -/* '?' describe command support. */ static vector -cmd_describe_command_real (vector vline, struct vty *vty, int *status) +cmd_complete_command_real (vector vline, struct vty *vty, int *status) { - unsigned int i; - vector cmd_vector; -#define INIT_MATCHVEC_SIZE 10 - vector matchvec; - struct cmd_element *cmd_element; - unsigned int index; - int ret; - enum match_type match; - char *command = NULL; - vector matches = NULL; - vector match_vector; - uint32_t command_found = 0; - const char *last_word; - - /* Set index. */ - if (vector_active (vline) == 0) - { - *status = CMD_ERR_NO_MATCH; - return NULL; - } - - index = vector_active (vline) - 1; - - /* Make copy vector of current node's command vector. */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + struct list *completions; + struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node); - /* Prepare match vector */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); + enum matcher_rv rv = command_complete (cmdgraph, vline, &completions); - /* Filter commands and build a list how they could possibly continue. */ - for (i = 0; i <= index; i++) - { - command = vector_slot (vline, i); - - if (matches) - cmd_matches_free(&matches); - - ret = cmd_vector_filter(cmd_vector, - FILTER_RELAXED, - vline, i, - &match, - &matches); - - if (ret != CMD_SUCCESS) - { - vector_free (cmd_vector); - vector_free (matchvec); - cmd_matches_free(&matches); - *status = ret; - return NULL; - } - - /* The last match may well be ambigious, so break here */ - if (i == index) - break; - - if (match == vararg_match) - { - /* We found a vararg match - so we can throw out the current matches here - * and don't need to continue checking the command input */ - unsigned int j, k; - - for (j = 0; j < vector_active (matches); j++) - if ((match_vector = vector_slot (matches, j)) != NULL) - for (k = 0; k < vector_active (match_vector); k++) - { - struct cmd_token *token = vector_slot (match_vector, k); - vector_set (matchvec, token); - } - - *status = CMD_SUCCESS; - vector_set(matchvec, &token_cr); - vector_free (cmd_vector); - cmd_matches_free(&matches); - cmd_describe_sort(matchvec); - return matchvec; - } - - ret = is_cmd_ambiguous(cmd_vector, command, matches, match); - if (ret == 1) - { - vector_free (cmd_vector); - vector_free (matchvec); - cmd_matches_free(&matches); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } - else if (ret == 2) - { - vector_free (cmd_vector); - vector_free (matchvec); - cmd_matches_free(&matches); - *status = CMD_ERR_NO_MATCH; - return NULL; - } - } - - /* Make description vector. */ - for (i = 0; i < vector_active (matches); i++) { - if ((cmd_element = vector_slot (cmd_vector, i)) != NULL && - !(cmd_element->attr == CMD_ATTR_DEPRECATED || - cmd_element->attr == CMD_ATTR_HIDDEN)) - { - unsigned int j; - vector vline_trimmed; - - command_found++; - last_word = vector_slot(vline, vector_active(vline) - 1); - if (last_word == NULL || !strlen(last_word)) - { - vline_trimmed = vector_copy(vline); - vector_unset(vline_trimmed, vector_active(vline_trimmed) - 1); - - if (cmd_is_complete(cmd_element, vline_trimmed) - && desc_unique_string(matchvec, command_cr)) - { - if (match != vararg_match) - vector_set(matchvec, &token_cr); - } - - vector_free(vline_trimmed); - } - - match_vector = vector_slot (matches, i); - if (match_vector) - for (j = 0; j < vector_active(match_vector); j++) - { - struct cmd_token *token = vector_slot(match_vector, j); - const char *string; - - string = cmd_entry_function_desc(command, token); - if (string && desc_unique_string(matchvec, string)) - vector_set(matchvec, token); - } - } - } - - /* - * We can get into this situation when the command is complete - * but the last part of the command is an optional piece of - * cli. - */ - last_word = vector_slot(vline, vector_active(vline) - 1); - if (command_found == 0 && (last_word == NULL || !strlen(last_word))) { - vector_set(matchvec, &token_cr); + if (MATCHER_ERROR(rv)) + { + *status = CMD_ERR_NO_MATCH; + return NULL; } - vector_free (cmd_vector); - cmd_matches_free(&matches); + vector comps = completions_to_vec (completions); + list_delete (completions); - if (vector_slot (matchvec, 0) == NULL) - { - vector_free (matchvec); + // set status code appropriately + switch (vector_active (comps)) + { + case 0: *status = CMD_ERR_NO_MATCH; - return NULL; - } + break; + case 1: + *status = CMD_COMPLETE_FULL_MATCH; + break; + default: + *status = CMD_COMPLETE_LIST_MATCH; + } - *status = CMD_SUCCESS; - cmd_describe_sort(matchvec); - return matchvec; + return comps; } vector @@ -2261,291 +929,108 @@ cmd_describe_command (vector vline, struct vty *vty, int *status) shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) - { - vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); - } + for (index = 1; index < vector_active (vline); index++) + { + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); + } - ret = cmd_describe_command_real (shifted_vline, vty, status); + ret = cmd_complete_command_real (shifted_vline, vty, status); vector_free(shifted_vline); vty->node = onode; return ret; } - - return cmd_describe_command_real (vline, vty, status); + return cmd_complete_command_real (vline, vty, status); } +/** + * Generate possible tab-completions for the given input. This function only + * returns results that would result in a valid command if used as Readline + * completions (as is the case in vtysh). For instance, if the passed vline ends + * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned. + * + * @param vline vectorized input line + * @param vty the vty + * @param status location to store matcher status code in + * @return set of valid strings for use with Readline as tab-completions. + */ -/* Check LCD of matched command. */ -static int -cmd_lcd (char **matched) -{ - int i; - int j; - int lcd = -1; - char *s1, *s2; - char c1, c2; - - if (matched[0] == NULL || matched[1] == NULL) - return 0; - - for (i = 1; matched[i] != NULL; i++) - { - s1 = matched[i - 1]; - s2 = matched[i]; - - for (j = 0; (c1 = s1[j]) && (c2 = s2[j]); j++) - if (c1 != c2) - break; - - if (lcd < 0) - lcd = j; - else - { - if (lcd > j) - lcd = j; - } - } - return lcd; -} - -static int -cmd_complete_cmp(const void *a, const void *b) +char ** +cmd_complete_command (vector vline, struct vty *vty, int *status) { - const char *first = *(char * const *)a; - const char *second = *(char * const *)b; - - if (!first) - { - if (!second) - return 0; - return 1; - } - if (!second) - return -1; + char **ret = NULL; + int original_node = vty->node; + vector input_line = vector_init (vector_count (vline)); - return strcmp(first, second); -} + // if the first token is 'do' we'll want to execute the command in the enable node + int do_shortcut = cmd_try_do_shortcut (vty->node, vector_slot (vline, 0)); + vty->node = do_shortcut ? ENABLE_NODE : original_node; -static void -cmd_complete_sort(vector matchvec) -{ - qsort(matchvec->index, vector_active(matchvec), - sizeof(void*), cmd_complete_cmp); -} + // construct the input line we'll be matching on + unsigned int offset = (do_shortcut) ? 1 : 0; + for (unsigned index = 0; index + offset < vector_active (vline); index++) + vector_set_index (input_line, index, vector_lookup (vline, index + offset)); -/* Command line completion support. */ -static char ** -cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib) -{ - unsigned int i; - vector cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); -#define INIT_MATCHVEC_SIZE 10 - vector matchvec; - unsigned int index; - char **match_str; - struct cmd_token *token; - char *command; - int lcd; - vector matches = NULL; - vector match_vector; + // get token completions -- this is a copying operation + vector comps = NULL, initial_comps; + initial_comps = cmd_complete_command_real (input_line, vty, status); - if (vector_active (vline) == 0) + if (!MATCHER_ERROR (*status)) + { + assert (initial_comps); + // filter out everything that is not suitable for a tab-completion + comps = vector_init (VECTOR_MIN_SIZE); + for (unsigned int i = 0; i < vector_active(initial_comps); i++) { - vector_free (cmd_vector); - *status = CMD_ERR_NO_MATCH; - return NULL; + struct cmd_token *token = vector_slot (initial_comps, i); + if (token->type == WORD_TKN) + vector_set (comps, token); } - else - index = vector_active (vline) - 1; + vector_free (initial_comps); - /* First, filter by command string */ - for (i = 0; i <= index; i++) + // since we filtered results, we need to re-set status code + switch (vector_active (comps)) { - command = vector_slot (vline, i); - enum match_type match; - int ret; - - if (matches) - cmd_matches_free(&matches); - - /* First try completion match, if there is exactly match return 1 */ - ret = cmd_vector_filter(cmd_vector, - FILTER_RELAXED, - vline, i, - &match, - &matches); - - if (ret != CMD_SUCCESS) - { - vector_free(cmd_vector); - cmd_matches_free(&matches); - *status = ret; - return NULL; - } - - /* Break here - the completion mustn't be checked to be non-ambiguous */ - if (i == index) - break; - - /* If there is exact match then filter ambiguous match else check - ambiguousness. */ - ret = is_cmd_ambiguous (cmd_vector, command, matches, match); - if (ret == 1) - { - vector_free (cmd_vector); - cmd_matches_free(&matches); - *status = CMD_ERR_AMBIGUOUS; - return NULL; - } + case 0: + *status = CMD_ERR_NO_MATCH; + break; + case 1: + *status = CMD_COMPLETE_FULL_MATCH; + break; + default: + *status = CMD_COMPLETE_LIST_MATCH; } - - /* Prepare match vector. */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); - /* Build the possible list of continuations into a list of completions */ - for (i = 0; i < vector_active (matches); i++) - if ((match_vector = vector_slot (matches, i))) + // copy completions text into an array of char* + ret = XMALLOC (MTYPE_TMP, (vector_active (comps)+1) * sizeof (char *)); + unsigned int i; + for (i = 0; i < vector_active (comps); i++) { - const char *string; - unsigned int j; - - for (j = 0; j < vector_active (match_vector); j++) - if ((token = vector_slot (match_vector, j))) - { - string = cmd_entry_function (vector_slot (vline, index), - token); - if (string && cmd_unique_string (matchvec, string)) - vector_set (matchvec, (islib != 0 ? - XSTRDUP (MTYPE_TMP, string) : - strdup (string) /* rl freed */)); - } + struct cmd_token *token = vector_slot (comps, i); + ret[i] = XSTRDUP (MTYPE_TMP, token->text); + vector_unset (comps, i); } + // set the last element to NULL, because this array is used in + // a Readline completion_generator function which expects NULL + // as a sentinel value + ret[i] = NULL; + vector_free (comps); + comps = NULL; + } + else if (initial_comps) + vector_free (initial_comps); - /* We don't need cmd_vector any more. */ - vector_free (cmd_vector); - cmd_matches_free(&matches); - - /* No matched command */ - if (vector_slot (matchvec, 0) == NULL) - { - vector_free (matchvec); - - /* In case of 'command \t' pattern. Do you need '?' command at - the end of the line. */ - if (vector_slot (vline, index) == '\0') - *status = CMD_ERR_NOTHING_TODO; - else - *status = CMD_ERR_NO_MATCH; - return NULL; - } - - /* Only one matched */ - if (vector_slot (matchvec, 1) == NULL) - { - size_t index_size = matchvec->alloced * sizeof (void *); - match_str = XMALLOC (MTYPE_TMP, index_size); - memcpy (match_str, matchvec->index, index_size); - vector_free (matchvec); - - *status = CMD_COMPLETE_FULL_MATCH; - return match_str; - } - /* Make it sure last element is NULL. */ - vector_set (matchvec, NULL); - - /* Check LCD of matched strings. */ - if (vector_slot (vline, index) != NULL) - { - lcd = cmd_lcd ((char **) matchvec->index); - - if (lcd) - { - int len = strlen (vector_slot (vline, index)); - - if (len < lcd) - { - char *lcdstr; - - lcdstr = (islib != 0 ? - XMALLOC (MTYPE_TMP, lcd + 1) : - malloc(lcd + 1)); - memcpy (lcdstr, matchvec->index[0], lcd); - lcdstr[lcd] = '\0'; - - /* Free matchvec. */ - for (i = 0; i < vector_active (matchvec); i++) - { - if (vector_slot (matchvec, i)) - { - if (islib != 0) - XFREE (MTYPE_TMP, vector_slot (matchvec, i)); - else - free (vector_slot (matchvec, i)); - } - } - vector_free (matchvec); - - /* Make new matchvec. */ - matchvec = vector_init (INIT_MATCHVEC_SIZE); - vector_set (matchvec, lcdstr); - - size_t index_size = matchvec->alloced * sizeof (void *); - match_str = XMALLOC (MTYPE_TMP, index_size); - memcpy (match_str, matchvec->index, index_size); - vector_free (matchvec); - - *status = CMD_COMPLETE_MATCH; - return match_str; - } - } - } - - match_str = (char **) matchvec->index; - cmd_complete_sort(matchvec); - vector_only_wrapper_free (matchvec); - *status = CMD_COMPLETE_LIST_MATCH; - return match_str; -} - -char ** -cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) -{ - char **ret; - - if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) - { - enum node_type onode; - vector shifted_vline; - unsigned int index; - - onode = vty->node; - vty->node = ENABLE_NODE; - /* We can try it on enable node, cos' the vty is authenticated */ - - shifted_vline = vector_init (vector_count(vline)); - /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) - { - vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); - } - - ret = cmd_complete_command_real (shifted_vline, vty, status, islib); + // comps should always be null here + assert (!comps); - vector_free(shifted_vline); - vty->node = onode; - return ret; - } + // free the adjusted input line + vector_free (input_line); - return cmd_complete_command_real (vline, vty, status, islib); -} + // reset vty->node to its original value + vty->node = original_node; -char ** -cmd_complete_command (vector vline, struct vty *vty, int *status) -{ - return cmd_complete_command_lib (vline, vty, status, 0); + return ret; } /* return parent node */ @@ -2563,13 +1048,15 @@ node_parent ( enum node_type node ) case BGP_VPNV6_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: - case BGP_VNC_NVE_GROUP_NODE: - case BGP_VNC_L2_GROUP_NODE: + case BGP_VNC_NVE_GROUP_NODE: + case BGP_VNC_L2_GROUP_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: + case BGP_EVPN_NODE: ret = BGP_NODE; break; case KEYCHAIN_KEY_NODE: @@ -2602,109 +1089,57 @@ node_parent ( enum node_type node ) /* Execute command by argument vline vector. */ static int cmd_execute_command_real (vector vline, - enum filter_type filter, - struct vty *vty, - struct cmd_element **cmd) + enum filter_type filter, + struct vty *vty, + const struct cmd_element **cmd) { - unsigned int i; - unsigned int index; - vector cmd_vector; - struct cmd_element *cmd_element; - struct cmd_element *matched_element; - unsigned int matched_count, incomplete_count; - int argc; - const char *argv[CMD_ARGC_MAX]; - enum match_type match = 0; - char *command; - int ret; - vector matches; - - /* Make copy of command elements. */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); - - for (index = 0; index < vector_active (vline); index++) - { - command = vector_slot (vline, index); - ret = cmd_vector_filter(cmd_vector, - filter, - vline, index, - &match, - &matches); - - if (ret != CMD_SUCCESS) - { - cmd_matches_free(&matches); - return ret; - } - - if (match == vararg_match) - { - cmd_matches_free(&matches); - break; - } - - ret = is_cmd_ambiguous (cmd_vector, command, matches, match); - cmd_matches_free(&matches); - - if (ret == 1) - { - vector_free(cmd_vector); - return CMD_ERR_AMBIGUOUS; - } - else if (ret == 2) - { - vector_free(cmd_vector); - return CMD_ERR_NO_MATCH; - } - } + struct list *argv_list; + enum matcher_rv status; + const struct cmd_element *matched_element = NULL; - /* Check matched count. */ - matched_element = NULL; - matched_count = 0; - incomplete_count = 0; + struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node); + status = command_match (cmdgraph, vline, &argv_list, &matched_element); - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i))) - { - if (cmd_is_complete(cmd_element, vline)) - { - matched_element = cmd_element; - matched_count++; - } - else - { - incomplete_count++; - } - } - - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); + if (cmd) + *cmd = matched_element; - /* To execute command, matched_count must be 1. */ - if (matched_count == 0) - { - if (incomplete_count) - return CMD_ERR_INCOMPLETE; - else - return CMD_ERR_NO_MATCH; + // if matcher error, return corresponding CMD_ERR + if (MATCHER_ERROR(status)) + { + if (argv_list) + list_delete (argv_list); + switch (status) + { + case MATCHER_INCOMPLETE: + return CMD_ERR_INCOMPLETE; + case MATCHER_AMBIGUOUS: + return CMD_ERR_AMBIGUOUS; + default: + return CMD_ERR_NO_MATCH; } + } - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; - - ret = cmd_parse(matched_element, vline, &argc, argv); - if (ret != CMD_SUCCESS) - return ret; + // build argv array from argv list + struct cmd_token **argv = XMALLOC (MTYPE_TMP, argv_list->count * sizeof (struct cmd_token *)); + struct listnode *ln; + struct cmd_token *token; + unsigned int i = 0; + for (ALL_LIST_ELEMENTS_RO(argv_list,ln,token)) + argv[i++] = token; - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; + int argc = argv_list->count; + int ret; if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; + ret = CMD_SUCCESS_DAEMON; + else + ret = matched_element->func (matched_element, vty, argc, argv); - /* Execute matched command. */ - return (*matched_element->func) (matched_element, vty, argc, argv); + // delete list and cmd_token's in it + list_delete (argv_list); + XFREE (MTYPE_TMP, argv); + + return ret; } /** @@ -2723,14 +1158,16 @@ cmd_execute_command_real (vector vline, * as to why no command could be executed. */ int -cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, - int vtysh) { - int ret, saved_ret, tried = 0; +cmd_execute_command (vector vline, struct vty *vty, + const struct cmd_element **cmd, + int vtysh) +{ + int ret, saved_ret = 0; enum node_type onode, try_node; onode = try_node = vty->node; - if ( cmd_try_do_shortcut(vty->node, vector_slot(vline, 0) ) ) + if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) { vector shifted_vline; unsigned int index; @@ -2740,10 +1177,8 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, shifted_vline = vector_init (vector_count(vline)); /* use memcpy? */ - for (index = 1; index < vector_active (vline); index++) - { - vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); - } + for (index = 1; index < vector_active (vline); index++) + vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd); @@ -2752,30 +1187,27 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, return ret; } - saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); if (vtysh) return saved_ret; - /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ - while ( ret != CMD_SUCCESS && ret != CMD_WARNING - && vty->node > CONFIG_NODE ) + if (ret != CMD_SUCCESS && ret != CMD_WARNING) { - try_node = node_parent(try_node); - vty->node = try_node; - ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); - tried = 1; - if (ret == CMD_SUCCESS || ret == CMD_WARNING) - { - /* succesfull command, leave the node as is */ - return ret; - } + /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */ + while (vty->node > CONFIG_NODE) + { + try_node = node_parent(try_node); + vty->node = try_node; + ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd); + if (ret == CMD_SUCCESS || ret == CMD_WARNING) + return ret; + } + /* no command succeeded, reset the vty to the original node */ + vty->node = onode; } - /* no command succeeded, reset the vty to the original node and - return the error for this node */ - if ( tried ) - vty->node = onode; + + /* return command status for original node */ return saved_ret; } @@ -2794,7 +1226,7 @@ cmd_execute_command (vector vline, struct vty *vty, struct cmd_element **cmd, */ int cmd_execute_command_strict (vector vline, struct vty *vty, - struct cmd_element **cmd) + const struct cmd_element **cmd) { return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); } @@ -2812,7 +1244,7 @@ cmd_execute_command_strict (vector vline, struct vty *vty, * as to why no command could be executed. */ int -command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon) +command_config_read_one_line (struct vty *vty, const struct cmd_element **cmd, int use_daemon) { vector vline; int saved_node; @@ -2838,7 +1270,7 @@ command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) && - ret != CMD_SUCCESS && + ret != CMD_SUCCESS && ret != CMD_WARNING && vty->node > CONFIG_NODE) { vty->node = node_parent(vty->node); @@ -2849,14 +1281,16 @@ command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use // stay at the same node if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) && - ret != CMD_SUCCESS && + ret != CMD_SUCCESS && ret != CMD_WARNING) { - vty->node = saved_node; - memcpy(vty->error_buf, vty->buf, VTY_BUFSIZ); + vty->node = saved_node; } } + if (ret != CMD_SUCCESS && ret != CMD_WARNING) + memcpy (vty->error_buf, vty->buf, VTY_BUFSIZ); + cmd_free_strvec (vline); return ret; @@ -2872,13 +1306,13 @@ config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num) while (fgets (vty->buf, VTY_BUFSIZ, fp)) { if (!error_ret) - ++(*line_num); + ++(*line_num); ret = command_config_read_one_line (vty, NULL, 0); if (ret != CMD_SUCCESS && ret != CMD_WARNING && - ret != CMD_ERR_NOTHING_TODO) - error_ret = ret; + ret != CMD_ERR_NOTHING_TODO) + error_ret = ret; } if (error_ret) { @@ -2906,7 +1340,7 @@ DEFUN (config_terminal, } /* Enable command */ -DEFUN (enable, +DEFUN (enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") @@ -2922,7 +1356,7 @@ DEFUN (enable, } /* Disable command */ -DEFUN (disable, +DEFUN (disable, config_disable_cmd, "disable", "Turn off privileged mode command\n") @@ -2938,14 +1372,21 @@ DEFUN (config_exit, "exit", "Exit current mode and down to previous mode\n") { + cmd_exit (vty); + return CMD_SUCCESS; +} + +void +cmd_exit (struct vty *vty) +{ switch (vty->node) { case VIEW_NODE: case ENABLE_NODE: if (vty_shell (vty)) - exit (0); + exit (0); else - vty->status = VTY_CLOSE; + vty->status = VTY_CLOSE; break; case CONFIG_NODE: vty->node = ENABLE_NODE; @@ -2976,11 +1417,13 @@ DEFUN (config_exit, case BGP_VPNV6_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: + case BGP_EVPN_NODE: vty->node = BGP_NODE; break; case LDP_IPV4_NODE: @@ -3005,15 +1448,18 @@ DEFUN (config_exit, default: break; } - return CMD_SUCCESS; } -/* quit is alias of exit. */ -ALIAS (config_exit, +/* ALIAS_FIXME */ +DEFUN (config_quit, config_quit_cmd, "quit", "Exit current mode and down to previous mode\n") - +{ + return config_exit (self, vty, argc, argv); +} + + /* End of configuration. */ DEFUN (config_end, config_end_cmd, @@ -3036,6 +1482,7 @@ DEFUN (config_end, case BGP_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: @@ -3045,6 +1492,7 @@ DEFUN (config_end, case BGP_IPV4M_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: + case BGP_EVPN_NODE: case RMAP_NODE: case OSPF_NODE: case OSPF6_NODE: @@ -3094,8 +1542,8 @@ DEFUN (config_help, "help", "Description of the interactive help system\n") { - vty_out (vty, - "Quagga VTY provides advanced help feature. When you need help,%s\ + vty_out (vty, + "Quagga VTY provides advanced help feature. When you need help,%s\ anytime at the command line please press '?'.%s\ %s\ If nothing matches, the help list will be empty and you must backup%s\ @@ -3107,37 +1555,98 @@ argument.%s\ 2. Partial help is provided when an abbreviated argument is entered%s\ and you want to know what arguments match the input%s\ (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + return CMD_SUCCESS; +} + +static void +permute (struct graph_node *start, struct vty *vty) +{ + static struct list *position = NULL; + if (!position) position = list_new (); + + // recursive dfs + listnode_add (position, start); + for (unsigned int i = 0; i < vector_active (start->to); i++) + { + struct graph_node *gn = vector_slot (start->to, i); + struct cmd_token *tok = gn->data; + if (tok->attr == CMD_ATTR_HIDDEN || + tok->attr == CMD_ATTR_DEPRECATED) + continue; + else if (tok->type == END_TKN || gn == start) + { + struct graph_node *gnn; + struct listnode *ln; + vty_out (vty, " "); + for (ALL_LIST_ELEMENTS_RO (position,ln,gnn)) + { + struct cmd_token *tt = gnn->data; + if (tt->type < SPECIAL_TKN) + vty_out (vty, " %s", tt->text); + } + if (gn == start) + vty_out (vty, "..."); + vty_out (vty, VTY_NEWLINE); + } + else + permute (gn, vty); + } + list_delete_node (position, listtail(position)); +} + +int +cmd_list_cmds (struct vty *vty, int do_permute) +{ + struct cmd_node *node = vector_slot (cmdvec, vty->node); + + if (do_permute) + permute (vector_slot (node->cmdgraph->nodes, 0), vty); + else + { + /* loop over all commands at this node */ + struct cmd_element *element = NULL; + for (unsigned int i = 0; i < vector_active(node->cmd_vector); i++) + if ((element = vector_slot (node->cmd_vector, i)) && + element->attr != CMD_ATTR_DEPRECATED && + element->attr != CMD_ATTR_HIDDEN) + vty_out (vty, " %s%s", element->string, VTY_NEWLINE); + } return CMD_SUCCESS; } /* Help display function for all node. */ DEFUN (config_list, config_list_cmd, - "list", - "Print command list\n") + "list [permutations]", + "Print command list\n" + "Print all possible command permutations\n") { - unsigned int i; - struct cmd_node *cnode = vector_slot (cmdvec, vty->node); - struct cmd_element *cmd; - - for (i = 0; i < vector_active (cnode->cmd_vector); i++) - if ((cmd = vector_slot (cnode->cmd_vector, i)) != NULL - && !(cmd->attr == CMD_ATTR_DEPRECATED - || cmd->attr == CMD_ATTR_HIDDEN)) - vty_out (vty, " %s%s", cmd->string, - VTY_NEWLINE); - return CMD_SUCCESS; + return cmd_list_cmds (vty, argc == 2); +} + +DEFUN (show_commandtree, + show_commandtree_cmd, + "show commandtree [permutations]", + SHOW_STR + "Show command tree\n" + "Permutations that we are interested in\n") +{ + return cmd_list_cmds (vty, argc == 3); } /* Write current configuration into file. */ -DEFUN (config_write_file, - config_write_file_cmd, - "write file", + +DEFUN (config_write, + config_write_cmd, + "write [<file|memory|terminal>]", "Write running configuration to memory, network, or terminal\n" - "Write to configuration file\n") + "Write to configuration file\n" + "Write configuration currently in memory\n" + "Write configuration to terminal\n") { + int idx_type = 1; unsigned int i; int fd, dirfd; struct cmd_node *node; @@ -3148,6 +1657,36 @@ DEFUN (config_write_file, struct vty *file_vty; struct stat conf_stat; + // if command was 'write terminal' or 'show running-config' + if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal") || + !strcmp(argv[0]->text, "show"))) + { + if (vty->type == VTY_SHELL_SERV) + { + for (i = 0; i < vector_active (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) + { + if ((*node->func) (vty)) + vty_out (vty, "!%s", VTY_NEWLINE); + } + } + else + { + vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, + VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); + + for (i = 0; i < vector_active (cmdvec); i++) + if ((node = vector_slot (cmdvec, i)) && node->func) + { + if ((*node->func) (vty)) + vty_out (vty, "!%s", VTY_NEWLINE); + } + vty_out (vty, "end%s",VTY_NEWLINE); + } + return CMD_SUCCESS; + } + if (host.noconfig) return CMD_SUCCESS; @@ -3155,7 +1694,7 @@ DEFUN (config_write_file, if (host.config == NULL) { vty_out (vty, "Can't save to configuration file, using vtysh.%s", - VTY_NEWLINE); + VTY_NEWLINE); return CMD_WARNING; } @@ -3185,16 +1724,15 @@ DEFUN (config_write_file, config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8); sprintf (config_file_tmp, "%s.XXXXXX", config_file); - + /* Open file to configuration write. */ fd = mkstemp (config_file_tmp); if (fd < 0) { vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp, - VTY_NEWLINE); + VTY_NEWLINE); goto finished; } - if (fchmod (fd, CONFIGFILE_MASK) != 0) { vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s", @@ -3215,119 +1753,80 @@ DEFUN (config_write_file, for (i = 0; i < vector_active (cmdvec); i++) if ((node = vector_slot (cmdvec, i)) && node->func) { - if ((*node->func) (file_vty)) - vty_out (file_vty, "!\n"); + if ((*node->func) (file_vty)) + vty_out (file_vty, "!\n"); } vty_close (file_vty); if (stat(config_file, &conf_stat) >= 0) { if (unlink (config_file_sav) != 0) - if (errno != ENOENT) - { - vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, - VTY_NEWLINE); - goto finished; - } + if (errno != ENOENT) + { + vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav, + VTY_NEWLINE); + goto finished; + } if (link (config_file, config_file_sav) != 0) { vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav, VTY_NEWLINE); goto finished; } - fsync (dirfd); + if (dirfd >= 0) + fsync (dirfd); } if (rename (config_file_tmp, config_file) != 0) { vty_out (vty, "Can't save configuration file %s.%s", config_file, - VTY_NEWLINE); + VTY_NEWLINE); goto finished; } - fsync (dirfd); + if (dirfd >= 0) + fsync (dirfd); vty_out (vty, "Configuration saved to %s%s", config_file, - VTY_NEWLINE); + VTY_NEWLINE); ret = CMD_SUCCESS; finished: if (ret != CMD_SUCCESS) unlink (config_file_tmp); - close (dirfd); + if (dirfd >= 0) + close (dirfd); XFREE (MTYPE_TMP, config_file_tmp); XFREE (MTYPE_TMP, config_file_sav); return ret; } -ALIAS (config_write_file, - config_write_cmd, - "write", - "Write running configuration to memory, network, or terminal\n") - -ALIAS (config_write_file, - config_write_memory_cmd, - "write memory", - "Write running configuration to memory, network, or terminal\n" - "Write configuration to the file (same as write file)\n") +/* ALIAS_FIXME for 'write <terminal|memory>' */ +DEFUN (show_running_config, + show_running_config_cmd, + "show running-config", + SHOW_STR + "running configuration (same as write terminal/memory)\n") +{ + return config_write (self, vty, argc, argv); +} -ALIAS (config_write_file, - copy_runningconfig_startupconfig_cmd, - "copy running-config startup-config", +/* ALIAS_FIXME for 'write file' */ +DEFUN (copy_runningconf_startupconf, + copy_runningconf_startupconf_cmd, + "copy running-config startup-config", "Copy configuration\n" "Copy running config to... \n" "Copy running config to startup config (same as write file)\n") - -/* Write current configuration into the terminal. */ -DEFUN (config_write_terminal, - config_write_terminal_cmd, - "write terminal", - "Write running configuration to memory, network, or terminal\n" - "Write to terminal\n") { - unsigned int i; - struct cmd_node *node; - - if (host.noconfig) - return CMD_SUCCESS; - - if (vty->type == VTY_SHELL_SERV) - { - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh) - { - if ((*node->func) (vty)) - vty_out (vty, "!%s", VTY_NEWLINE); - } - } - else - { - vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE, - VTY_NEWLINE); - vty_out (vty, "!%s", VTY_NEWLINE); - - for (i = 0; i < vector_active (cmdvec); i++) - if ((node = vector_slot (cmdvec, i)) && node->func) - { - if ((*node->func) (vty)) - vty_out (vty, "!%s", VTY_NEWLINE); - } - vty_out (vty, "end%s",VTY_NEWLINE); - } - return CMD_SUCCESS; + return config_write (self, vty, argc, argv); } - -/* Write current configuration into the terminal. */ -ALIAS (config_write_terminal, - show_running_config_cmd, - "show running-config", - SHOW_STR - "running configuration\n") +/** -- **/ /* Write startup configuration into the terminal. */ DEFUN (show_startup_config, show_startup_config_cmd, "show startup-config", SHOW_STR - "Contentes of startup configuration\n") + "Contents of startup configuration\n") { char buf[BUFSIZ]; FILE *confp; @@ -3341,7 +1840,7 @@ DEFUN (show_startup_config, if (confp == NULL) { vty_out (vty, "Can't open configuration file [%s] due to '%s'%s", - host.config, safe_strerror(errno), VTY_NEWLINE); + host.config, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } @@ -3350,7 +1849,7 @@ DEFUN (show_startup_config, char *cp = buf; while (*cp != '\r' && *cp != '\n' && *cp != '\0') - cp++; + cp++; *cp = '\0'; vty_out (vty, "%s%s", buf, VTY_NEWLINE); @@ -3361,77 +1860,67 @@ DEFUN (show_startup_config, return CMD_SUCCESS; } +int +cmd_hostname_set (const char *hostname) +{ + XFREE (MTYPE_HOST, host.name); + host.name = hostname ? XSTRDUP (MTYPE_HOST, hostname) : NULL; + return CMD_SUCCESS; +} + /* Hostname configuration */ -DEFUN (config_hostname, +DEFUN (config_hostname, hostname_cmd, "hostname WORD", "Set system's network name\n" "This system's network name\n") { - if (!isalpha((int) *argv[0])) + struct cmd_token *word = argv[1]; + + if (!isalpha((int) word->arg[0])) { vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE); return CMD_WARNING; } - if (host.name) - XFREE (MTYPE_HOST, host.name); - - host.name = XSTRDUP (MTYPE_HOST, argv[0]); - return CMD_SUCCESS; + return cmd_hostname_set (word->arg); } -DEFUN (config_no_hostname, +DEFUN (config_no_hostname, no_hostname_cmd, "no hostname [HOSTNAME]", NO_STR "Reset system's network name\n" "Host name of this router\n") { - if (host.name) - XFREE (MTYPE_HOST, host.name); - host.name = NULL; - return CMD_SUCCESS; + return cmd_hostname_set (NULL); } /* VTY interface password set. */ -DEFUN (config_password, password_cmd, - "password (8|) WORD", +DEFUN (config_password, + password_cmd, + "password [(8-8)] WORD", "Assign the terminal connection password\n" "Specifies a HIDDEN password will follow\n" - "dummy string \n" - "The HIDDEN line password string\n") + "The password string\n") { - /* Argument check. */ - if (argc == 0) - { - vty_out (vty, "Please specify password.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (argc == 2) - { - if (*argv[0] == '8') - { - if (host.password) - XFREE (MTYPE_HOST, host.password); - host.password = NULL; - if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); - return CMD_SUCCESS; - } - else - { - vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); - return CMD_WARNING; - } - } + int idx_8 = 1; + int idx_word = 2; + if (argc == 3) // '8' was specified + { + if (host.password) + XFREE (MTYPE_HOST, host.password); + host.password = NULL; + if (host.password_encrypt) + XFREE (MTYPE_HOST, host.password_encrypt); + host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[idx_word]->arg); + return CMD_SUCCESS; + } - if (!isalnum ((int) *argv[0])) + if (!isalnum (argv[idx_8]->arg[0])) { - vty_out (vty, - "Please specify string starting with alphanumeric%s", VTY_NEWLINE); + vty_out (vty, + "Please specify string starting with alphanumeric%s", VTY_NEWLINE); return CMD_WARNING; } @@ -3442,62 +1931,53 @@ DEFUN (config_password, password_cmd, if (host.encrypt) { if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); - host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); + XFREE (MTYPE_HOST, host.password_encrypt); + host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[idx_8]->arg)); } else - host.password = XSTRDUP (MTYPE_HOST, argv[0]); + host.password = XSTRDUP (MTYPE_HOST, argv[idx_8]->arg); return CMD_SUCCESS; } -ALIAS (config_password, password_text_cmd, - "password LINE", - "Assign the terminal connection password\n" - "The UNENCRYPTED (cleartext) line password\n") - /* VTY enable password set. */ -DEFUN (config_enable_password, enable_password_cmd, - "enable password (8|) WORD", +DEFUN (config_enable_password, + enable_password_cmd, + "enable password [(8-8)] WORD", "Modify enable password parameters\n" "Assign the privileged level password\n" "Specifies a HIDDEN password will follow\n" - "dummy string \n" "The HIDDEN 'enable' password string\n") { - /* Argument check. */ - if (argc == 0) - { - vty_out (vty, "Please specify password.%s", VTY_NEWLINE); - return CMD_WARNING; - } + int idx_8 = 2; + int idx_word = 3; /* Crypt type is specified. */ - if (argc == 2) + if (argc == 4) { - if (*argv[0] == '8') - { - if (host.enable) - XFREE (MTYPE_HOST, host.enable); - host.enable = NULL; - - if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[1]); - - return CMD_SUCCESS; - } + if (argv[idx_8]->arg[0] == '8') + { + if (host.enable) + XFREE (MTYPE_HOST, host.enable); + host.enable = NULL; + + if (host.enable_encrypt) + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[idx_word]->arg); + + return CMD_SUCCESS; + } else - { - vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); - return CMD_WARNING; - } + { + vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE); + return CMD_WARNING; + } } - if (!isalnum ((int) *argv[0])) + if (!isalnum (argv[idx_8]->arg[0])) { - vty_out (vty, - "Please specify string starting with alphanumeric%s", VTY_NEWLINE); + vty_out (vty, + "Please specify string starting with alphanumeric%s", VTY_NEWLINE); return CMD_WARNING; } @@ -3509,24 +1989,18 @@ DEFUN (config_enable_password, enable_password_cmd, if (host.encrypt) { if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); - host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[0])); + XFREE (MTYPE_HOST, host.enable_encrypt); + host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[idx_8]->arg)); } else - host.enable = XSTRDUP (MTYPE_HOST, argv[0]); + host.enable = XSTRDUP (MTYPE_HOST, argv[idx_8]->arg); return CMD_SUCCESS; } -ALIAS (config_enable_password, - enable_password_text_cmd, - "enable password LINE", - "Modify enable password parameters\n" - "Assign the privileged level password\n" - "The UNENCRYPTED (cleartext) 'enable' password\n") - /* VTY enable password delete. */ -DEFUN (no_config_enable_password, no_enable_password_cmd, +DEFUN (no_config_enable_password, + no_enable_password_cmd, "no enable password", NO_STR "Modify enable password parameters\n" @@ -3542,7 +2016,7 @@ DEFUN (no_config_enable_password, no_enable_password_cmd, return CMD_SUCCESS; } - + DEFUN (service_password_encrypt, service_password_encrypt_cmd, "service password-encryption", @@ -3557,13 +2031,13 @@ DEFUN (service_password_encrypt, if (host.password) { if (host.password_encrypt) - XFREE (MTYPE_HOST, host.password_encrypt); + XFREE (MTYPE_HOST, host.password_encrypt); host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password)); } if (host.enable) { if (host.enable_encrypt) - XFREE (MTYPE_HOST, host.enable_encrypt); + XFREE (MTYPE_HOST, host.enable_encrypt); host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable)); } @@ -3593,16 +2067,18 @@ DEFUN (no_service_password_encrypt, return CMD_SUCCESS; } -DEFUN (config_terminal_length, config_terminal_length_cmd, - "terminal length <0-512>", +DEFUN (config_terminal_length, + config_terminal_length_cmd, + "terminal length (0-512)", "Set terminal line parameters\n" "Set number of lines on a screen\n" "Number of lines on screen (0 for no pausing)\n") { + int idx_number = 2; int lines; char *endptr = NULL; - lines = strtol (argv[0], &endptr, 10); + lines = strtol (argv[idx_number]->arg, &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') { vty_out (vty, "length is malformed%s", VTY_NEWLINE); @@ -3613,7 +2089,8 @@ DEFUN (config_terminal_length, config_terminal_length_cmd, return CMD_SUCCESS; } -DEFUN (config_terminal_no_length, config_terminal_no_length_cmd, +DEFUN (config_terminal_no_length, + config_terminal_no_length_cmd, "terminal no length", "Set terminal line parameters\n" NO_STR @@ -3623,16 +2100,18 @@ DEFUN (config_terminal_no_length, config_terminal_no_length_cmd, return CMD_SUCCESS; } -DEFUN (service_terminal_length, service_terminal_length_cmd, - "service terminal-length <0-512>", +DEFUN (service_terminal_length, + service_terminal_length_cmd, + "service terminal-length (0-512)", "Set up miscellaneous service\n" "System wide terminal length configuration\n" "Number of lines of VTY (0 means no line control)\n") { + int idx_number = 2; int lines; char *endptr = NULL; - lines = strtol (argv[0], &endptr, 10); + lines = strtol (argv[idx_number]->arg, &endptr, 10); if (lines < 0 || lines > 512 || *endptr != '\0') { vty_out (vty, "length is malformed%s", VTY_NEWLINE); @@ -3643,8 +2122,9 @@ DEFUN (service_terminal_length, service_terminal_length_cmd, return CMD_SUCCESS; } -DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, - "no service terminal-length [<0-512>]", +DEFUN (no_service_terminal_length, + no_service_terminal_length_cmd, + "no service terminal-length [(0-512)]", NO_STR "Set up miscellaneous service\n" "System wide terminal length configuration\n" @@ -3655,15 +2135,15 @@ DEFUN (no_service_terminal_length, no_service_terminal_length_cmd, } DEFUN_HIDDEN (do_echo, - echo_cmd, - "echo .MESSAGE", - "Echo a message back to the vty\n" - "The message to echo\n") + echo_cmd, + "echo MESSAGE...", + "Echo a message back to the vty\n" + "The message to echo\n") { char *message; - vty_out (vty, "%s%s", ((message = argv_concat(argv, argc, 0)) ? message : ""), - VTY_NEWLINE); + vty_out (vty, "%s%s", ((message = argv_concat (argv, argc, 1)) ? message : ""), + VTY_NEWLINE); if (message) XFREE(MTYPE_TMP, message); return CMD_SUCCESS; @@ -3671,18 +2151,20 @@ DEFUN_HIDDEN (do_echo, DEFUN (config_logmsg, config_logmsg_cmd, - "logmsg "LOG_LEVELS" .MESSAGE", + "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...", "Send a message to enabled logging destinations\n" LOG_LEVEL_DESC "The message to send\n") { + int idx_log_level = 1; + int idx_message = 2; int level; char *message; - if ((level = level_match(argv[0])) == ZLOG_DISABLED) + if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; - zlog(NULL, level, "%s", ((message = argv_concat(argv, argc, 1)) ? message : "")); + zlog(level, "%s", ((message = argv_concat(argv, argc, idx_message)) ? message : "")); if (message) XFREE(MTYPE_TMP, message); @@ -3702,8 +2184,8 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s, facility %s, ident %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], - facility_name(zl->facility), zl->ident); + zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]], + facility_name(zl->facility), zl->ident); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Stdout logging: "); @@ -3711,7 +2193,7 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); + zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Monitor logging: "); @@ -3719,7 +2201,7 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); + zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "File logging: "); @@ -3728,91 +2210,85 @@ DEFUN (show_logging, vty_out (vty, "disabled"); else vty_out (vty, "level %s, filename %s", - zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], - zl->filename); + zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]], + zl->filename); vty_out (vty, "%s", VTY_NEWLINE); vty_out (vty, "Protocol name: %s%s", - zlog_proto_names[zl->protocol], VTY_NEWLINE); + zl->protoname, VTY_NEWLINE); vty_out (vty, "Record priority: %s%s", - (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); + (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE); vty_out (vty, "Timestamp precision: %d%s", - zl->timestamp_precision, VTY_NEWLINE); + zl->timestamp_precision, VTY_NEWLINE); return CMD_SUCCESS; } DEFUN (config_log_stdout, config_log_stdout_cmd, - "log stdout", - "Logging control\n" - "Set stdout logging level\n") -{ - zlog_set_level (NULL, ZLOG_DEST_STDOUT, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN (config_log_stdout_level, - config_log_stdout_level_cmd, - "log stdout "LOG_LEVELS, + "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", "Logging control\n" "Set stdout logging level\n" LOG_LEVEL_DESC) { + int idx_log_level = 2; + + if (argc == idx_log_level) + { + zlog_set_level (ZLOG_DEST_STDOUT, zlog_default->default_lvl); + return CMD_SUCCESS; + } int level; - if ((level = level_match(argv[0])) == ZLOG_DISABLED) + if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; - zlog_set_level (NULL, ZLOG_DEST_STDOUT, level); + zlog_set_level (ZLOG_DEST_STDOUT, level); return CMD_SUCCESS; } DEFUN (no_config_log_stdout, no_config_log_stdout_cmd, - "no log stdout [LEVEL]", + "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", NO_STR "Logging control\n" "Cancel logging to stdout\n" - "Logging level\n") + LOG_LEVEL_DESC) { - zlog_set_level (NULL, ZLOG_DEST_STDOUT, ZLOG_DISABLED); + zlog_set_level (ZLOG_DEST_STDOUT, ZLOG_DISABLED); return CMD_SUCCESS; } DEFUN (config_log_monitor, config_log_monitor_cmd, - "log monitor", - "Logging control\n" - "Set terminal line (monitor) logging level\n") -{ - zlog_set_level (NULL, ZLOG_DEST_MONITOR, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN (config_log_monitor_level, - config_log_monitor_level_cmd, - "log monitor "LOG_LEVELS, + "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", "Logging control\n" "Set terminal line (monitor) logging level\n" LOG_LEVEL_DESC) { + int idx_log_level = 2; + + if (argc == idx_log_level) + { + zlog_set_level (ZLOG_DEST_MONITOR, zlog_default->default_lvl); + return CMD_SUCCESS; + } int level; - if ((level = level_match(argv[0])) == ZLOG_DISABLED) + if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; - zlog_set_level (NULL, ZLOG_DEST_MONITOR, level); + zlog_set_level (ZLOG_DEST_MONITOR, level); return CMD_SUCCESS; } DEFUN (no_config_log_monitor, no_config_log_monitor_cmd, - "no log monitor [LEVEL]", + "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", NO_STR "Logging control\n" "Disable terminal line (monitor) logging\n" - "Logging level\n") + LOG_LEVEL_DESC) { - zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED); + zlog_set_level (ZLOG_DEST_MONITOR, ZLOG_DISABLED); return CMD_SUCCESS; } @@ -3822,19 +2298,19 @@ set_log_file(struct vty *vty, const char *fname, int loglevel) int ret; char *p = NULL; const char *fullpath; - + /* Path detection. */ if (! IS_DIRECTORY_SEP (*fname)) { char cwd[MAXPATHLEN+1]; cwd[MAXPATHLEN] = '\0'; - + if (getcwd (cwd, MAXPATHLEN) == NULL) { zlog_err ("config_log_file: Unable to alloc mem!"); return CMD_WARNING; } - + if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2)) == NULL) { @@ -3847,7 +2323,7 @@ set_log_file(struct vty *vty, const char *fname, int loglevel) else fullpath = fname; - ret = zlog_set_file (NULL, fullpath, loglevel); + ret = zlog_set_file (fullpath, loglevel); if (p) XFREE (MTYPE_TMP, p); @@ -3872,38 +2348,36 @@ set_log_file(struct vty *vty, const char *fname, int loglevel) DEFUN (config_log_file, config_log_file_cmd, - "log file FILENAME", - "Logging control\n" - "Logging to file\n" - "Logging filename\n") -{ - return set_log_file(vty, argv[0], zlog_default->default_lvl); -} - -DEFUN (config_log_file_level, - config_log_file_level_cmd, - "log file FILENAME "LOG_LEVELS, + "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", "Logging control\n" "Logging to file\n" "Logging filename\n" LOG_LEVEL_DESC) { - int level; - - if ((level = level_match(argv[1])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - return set_log_file(vty, argv[0], level); + int idx_filename = 2; + int idx_log_levels = 3; + if (argc == 4) + { + int level; + if ((level = level_match(argv[idx_log_levels]->arg)) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + return set_log_file(vty, argv[idx_filename]->arg, level); + } + else + return set_log_file(vty, argv[idx_filename]->arg, zlog_default->default_lvl); } DEFUN (no_config_log_file, no_config_log_file_cmd, - "no log file [FILENAME]", + "no log file [FILENAME [LEVEL]]", NO_STR "Logging control\n" "Cancel logging to file\n" - "Logging file name\n") + "Logging file name\n" + "Logging file name\n" + "Logging level\n") { - zlog_reset_file (NULL); + zlog_reset_file (); if (host.logfile) XFREE (MTYPE_HOST, host.logfile); @@ -3913,117 +2387,79 @@ DEFUN (no_config_log_file, return CMD_SUCCESS; } -ALIAS (no_config_log_file, - no_config_log_file_level_cmd, - "no log file FILENAME LEVEL", - NO_STR - "Logging control\n" - "Cancel logging to file\n" - "Logging file name\n" - "Logging level\n") - DEFUN (config_log_syslog, config_log_syslog_cmd, - "log syslog", - "Logging control\n" - "Set syslog logging level\n") -{ - zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); - return CMD_SUCCESS; -} - -DEFUN (config_log_syslog_level, - config_log_syslog_level_cmd, - "log syslog "LOG_LEVELS, + "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", "Logging control\n" "Set syslog logging level\n" LOG_LEVEL_DESC) { - int level; - - if ((level = level_match(argv[0])) == ZLOG_DISABLED) - return CMD_ERR_NO_MATCH; - zlog_set_level (NULL, ZLOG_DEST_SYSLOG, level); - return CMD_SUCCESS; -} - -DEFUN_DEPRECATED (config_log_syslog_facility, - config_log_syslog_facility_cmd, - "log syslog facility "LOG_FACILITIES, - "Logging control\n" - "Logging goes to syslog\n" - "(Deprecated) Facility parameter for syslog messages\n" - LOG_FACILITY_DESC) -{ - int facility; - - if ((facility = facility_match(argv[0])) < 0) - return CMD_ERR_NO_MATCH; - - zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); - zlog_default->facility = facility; - return CMD_SUCCESS; + int idx_log_levels = 2; + if (argc == 3) + { + int level; + if ((level = level_match (argv[idx_log_levels]->arg)) == ZLOG_DISABLED) + return CMD_ERR_NO_MATCH; + zlog_set_level (ZLOG_DEST_SYSLOG, level); + return CMD_SUCCESS; + } + else + { + zlog_set_level (ZLOG_DEST_SYSLOG, zlog_default->default_lvl); + return CMD_SUCCESS; + } } DEFUN (no_config_log_syslog, no_config_log_syslog_cmd, - "no log syslog [LEVEL]", + "no log syslog [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>] [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]", NO_STR "Logging control\n" "Cancel logging to syslog\n" - "Logging level\n") + LOG_FACILITY_DESC + LOG_LEVEL_DESC) { - zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED); + zlog_set_level (ZLOG_DEST_SYSLOG, ZLOG_DISABLED); return CMD_SUCCESS; } -ALIAS (no_config_log_syslog, - no_config_log_syslog_facility_cmd, - "no log syslog facility "LOG_FACILITIES, - NO_STR - "Logging control\n" - "Logging goes to syslog\n" - "Facility parameter for syslog messages\n" - LOG_FACILITY_DESC) - DEFUN (config_log_facility, config_log_facility_cmd, - "log facility "LOG_FACILITIES, + "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>", "Logging control\n" "Facility parameter for syslog messages\n" LOG_FACILITY_DESC) { - int facility; + int idx_target = 2; + int facility = facility_match(argv[idx_target]->arg); - if ((facility = facility_match(argv[0])) < 0) - return CMD_ERR_NO_MATCH; zlog_default->facility = facility; return CMD_SUCCESS; } DEFUN (no_config_log_facility, no_config_log_facility_cmd, - "no log facility [FACILITY]", + "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]", NO_STR "Logging control\n" "Reset syslog facility to default (daemon)\n" - "Syslog facility\n") + LOG_FACILITY_DESC) { zlog_default->facility = LOG_DAEMON; return CMD_SUCCESS; } DEFUN_DEPRECATED (config_log_trap, - config_log_trap_cmd, - "log trap "LOG_LEVELS, - "Logging control\n" - "(Deprecated) Set logging level and default for all destinations\n" - LOG_LEVEL_DESC) + config_log_trap_cmd, + "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>", + "Logging control\n" + "(Deprecated) Set logging level and default for all destinations\n" + LOG_LEVEL_DESC) { int new_level ; int i; - - if ((new_level = level_match(argv[0])) == ZLOG_DISABLED) + + if ((new_level = level_match(argv[2]->arg)) == ZLOG_DISABLED) return CMD_ERR_NO_MATCH; zlog_default->default_lvl = new_level; @@ -4034,12 +2470,12 @@ DEFUN_DEPRECATED (config_log_trap, } DEFUN_DEPRECATED (no_config_log_trap, - no_config_log_trap_cmd, - "no log trap [LEVEL]", - NO_STR - "Logging control\n" - "Permit all logging information\n" - "Logging level\n") + no_config_log_trap_cmd, + "no log trap [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]", + NO_STR + "Logging control\n" + "Permit all logging information\n" + LOG_LEVEL_DESC) { zlog_default->default_lvl = LOG_DEBUG; return CMD_SUCCESS; @@ -4068,20 +2504,15 @@ DEFUN (no_config_log_record_priority, DEFUN (config_log_timestamp_precision, config_log_timestamp_precision_cmd, - "log timestamp precision <0-6>", + "log timestamp precision (0-6)", "Logging control\n" "Timestamp configuration\n" "Set the timestamp precision\n" "Number of subsecond digits\n") { - if (argc != 1) - { - vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); - return CMD_WARNING; - } - + int idx_number = 3; VTY_GET_INTEGER_RANGE("Timestamp Precision", - zlog_default->timestamp_precision, argv[0], 0, 6); + zlog_default->timestamp_precision, argv[idx_number]->arg, 0, 6); return CMD_SUCCESS; } @@ -4112,7 +2543,7 @@ cmd_banner_motd_file (const char *file) if (in == rpath) { if (host.motdfile) - XFREE (MTYPE_HOST, host.motdfile); + XFREE (MTYPE_HOST, host.motdfile); host.motdfile = XSTRDUP (MTYPE_HOST, file); } else @@ -4129,13 +2560,14 @@ DEFUN (banner_motd_file, "Banner from a file\n" "Filename\n") { - int cmd = cmd_banner_motd_file (argv[0]); + int idx_file = 3; + const char *filename = argv[idx_file]->arg; + int cmd = cmd_banner_motd_file (filename); if (cmd == CMD_ERR_NO_FILE) - vty_out (vty, "%s does not exist", argv[0]); + vty_out (vty, "%s does not exist", filename); else if (cmd == CMD_WARNING) - vty_out (vty, "%s must be in %s", - argv[0], SYSCONFDIR); + vty_out (vty, "%s must be in %s", filename, SYSCONFDIR); return cmd; } @@ -4159,43 +2591,12 @@ DEFUN (no_banner_motd, "Strings for motd\n") { host.motd = NULL; - if (host.motdfile) + if (host.motdfile) XFREE (MTYPE_HOST, host.motdfile); host.motdfile = NULL; return CMD_SUCCESS; } -DEFUN (show_commandtree, - show_commandtree_cmd, - "show commandtree", - NO_STR - "Show command tree\n") -{ - /* TBD */ - vector cmd_vector; - unsigned int i; - - vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE); - - /* vector of all commands installed at this node */ - cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); - - /* loop over all commands at this node */ - for (i = 0; i < vector_active(cmd_vector); ++i) - { - struct cmd_element *cmd_element; - - /* A cmd_element (seems to be) is an individual command */ - if ((cmd_element = vector_slot (cmd_vector, i)) == NULL) - continue; - - vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE); - } - - vector_free (cmd_vector); - return CMD_SUCCESS; -} - /* Set config filename. Called from vty.c */ void host_config_set (const char *filename) @@ -4220,9 +2621,6 @@ install_default (enum node_type node) install_element (node, &config_help_cmd); install_element (node, &config_list_cmd); - install_element (node, &config_write_terminal_cmd); - install_element (node, &config_write_file_cmd); - install_element (node, &config_write_memory_cmd); install_element (node, &config_write_cmd); install_element (node, &show_running_config_cmd); } @@ -4237,12 +2635,6 @@ cmd_init (int terminal) { qobj_init (); - command_cr = XSTRDUP(MTYPE_CMD_TOKENS, "<cr>"); - token_cr.type = TOKEN_TERMINAL; - token_cr.terminal = TERMINAL_LITERAL; - token_cr.cmd = command_cr; - token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); - /* Allocate initial top vector of commands. */ cmdvec = vector_init (VECTOR_MIN_SIZE); @@ -4285,10 +2677,7 @@ cmd_init (int terminal) install_element (ENABLE_NODE, &config_end_cmd); install_element (ENABLE_NODE, &config_disable_cmd); install_element (ENABLE_NODE, &config_terminal_cmd); - install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); - install_element (ENABLE_NODE, &config_write_terminal_cmd); - install_element (ENABLE_NODE, &config_write_file_cmd); - install_element (ENABLE_NODE, &config_write_memory_cmd); + install_element (ENABLE_NODE, ©_runningconf_startupconf_cmd); install_element (ENABLE_NODE, &config_write_cmd); install_element (ENABLE_NODE, &show_running_config_cmd); } @@ -4299,38 +2688,27 @@ cmd_init (int terminal) install_element (ENABLE_NODE, &config_logmsg_cmd); install_default (CONFIG_NODE); - install_element (VIEW_NODE, &show_thread_cpu_cmd); - install_element (ENABLE_NODE, &clear_thread_cpu_cmd); - - install_element (VIEW_NODE, &show_work_queues_cmd); + thread_cmd_init (); + workqueue_cmd_init (); } - + install_element (CONFIG_NODE, &hostname_cmd); install_element (CONFIG_NODE, &no_hostname_cmd); if (terminal > 0) { install_element (CONFIG_NODE, &password_cmd); - install_element (CONFIG_NODE, &password_text_cmd); install_element (CONFIG_NODE, &enable_password_cmd); - install_element (CONFIG_NODE, &enable_password_text_cmd); install_element (CONFIG_NODE, &no_enable_password_cmd); install_element (CONFIG_NODE, &config_log_stdout_cmd); - install_element (CONFIG_NODE, &config_log_stdout_level_cmd); install_element (CONFIG_NODE, &no_config_log_stdout_cmd); install_element (CONFIG_NODE, &config_log_monitor_cmd); - install_element (CONFIG_NODE, &config_log_monitor_level_cmd); install_element (CONFIG_NODE, &no_config_log_monitor_cmd); install_element (CONFIG_NODE, &config_log_file_cmd); - install_element (CONFIG_NODE, &config_log_file_level_cmd); install_element (CONFIG_NODE, &no_config_log_file_cmd); - install_element (CONFIG_NODE, &no_config_log_file_level_cmd); install_element (CONFIG_NODE, &config_log_syslog_cmd); - install_element (CONFIG_NODE, &config_log_syslog_level_cmd); - install_element (CONFIG_NODE, &config_log_syslog_facility_cmd); install_element (CONFIG_NODE, &no_config_log_syslog_cmd); - install_element (CONFIG_NODE, &no_config_log_syslog_facility_cmd); install_element (CONFIG_NODE, &config_log_facility_cmd); install_element (CONFIG_NODE, &no_config_log_facility_cmd); install_element (CONFIG_NODE, &config_log_trap_cmd); @@ -4349,90 +2727,78 @@ cmd_init (int terminal) vrf_install_commands (); } - srandom(time(NULL)); + +#ifdef DEV_BUILD + grammar_sandbox_init(); +#endif } -static void -cmd_terminate_token(struct cmd_token *token) +struct cmd_token * +new_cmd_token (enum cmd_token_type type, u_char attr, + const char *text, const char *desc) { - unsigned int i, j; - vector keyword_vect; + struct cmd_token *token = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token)); + token->type = type; + token->attr = attr; + token->text = text ? XSTRDUP (MTYPE_CMD_TEXT, text) : NULL; + token->desc = desc ? XSTRDUP (MTYPE_CMD_DESC, desc) : NULL; + token->refcnt = 1; + token->arg = NULL; + token->allowrepeat = false; - if (token->multiple) - { - for (i = 0; i < vector_active(token->multiple); i++) - cmd_terminate_token(vector_slot(token->multiple, i)); - vector_free(token->multiple); - token->multiple = NULL; - } + return token; +} - if (token->keyword) - { - for (i = 0; i < vector_active(token->keyword); i++) - { - keyword_vect = vector_slot(token->keyword, i); - for (j = 0; j < vector_active(keyword_vect); j++) - cmd_terminate_token(vector_slot(keyword_vect, j)); - vector_free(keyword_vect); - } - vector_free(token->keyword); - token->keyword = NULL; - } +void +del_cmd_token (struct cmd_token *token) +{ + if (!token) return; - XFREE(MTYPE_CMD_TOKENS, token->cmd); - XFREE(MTYPE_CMD_TOKENS, token->desc); + if (token->text) + XFREE (MTYPE_CMD_TEXT, token->text); + if (token->desc) + XFREE (MTYPE_CMD_DESC, token->desc); + if (token->arg) + XFREE (MTYPE_CMD_ARG, token->arg); - XFREE(MTYPE_CMD_TOKENS, token); + XFREE (MTYPE_CMD_TOKENS, token); } -static void -cmd_terminate_element(struct cmd_element *cmd) +struct cmd_token * +copy_cmd_token (struct cmd_token *token) { - unsigned int i; - - if (cmd->tokens == NULL) - return; + struct cmd_token *copy = new_cmd_token (token->type, token->attr, NULL, NULL); + copy->max = token->max; + copy->min = token->min; + copy->text = token->text ? XSTRDUP (MTYPE_CMD_TEXT, token->text) : NULL; + copy->desc = token->desc ? XSTRDUP (MTYPE_CMD_DESC, token->desc) : NULL; + copy->arg = token->arg ? XSTRDUP (MTYPE_CMD_ARG, token->arg) : NULL; - for (i = 0; i < vector_active(cmd->tokens); i++) - cmd_terminate_token(vector_slot(cmd->tokens, i)); - - vector_free(cmd->tokens); - cmd->tokens = NULL; + return copy; } void cmd_terminate () { - unsigned int i, j; struct cmd_node *cmd_node; - struct cmd_element *cmd_element; - vector cmd_node_v; if (cmdvec) { - for (i = 0; i < vector_active (cmdvec); i++) + for (unsigned int i = 0; i < vector_active (cmdvec); i++) if ((cmd_node = vector_slot (cmdvec, i)) != NULL) - { - cmd_node_v = cmd_node->cmd_vector; - - for (j = 0; j < vector_active (cmd_node_v); j++) - if ((cmd_element = vector_slot (cmd_node_v, j)) != NULL) - cmd_terminate_element(cmd_element); - - vector_free (cmd_node_v); - hash_clean (cmd_node->cmd_hash, NULL); - hash_free (cmd_node->cmd_hash); - cmd_node->cmd_hash = NULL; - } + { + // deleting the graph delets the cmd_element as well + graph_delete_graph (cmd_node->cmdgraph); + vector_free (cmd_node->cmd_vector); + hash_clean (cmd_node->cmd_hash, NULL); + hash_free (cmd_node->cmd_hash); + cmd_node->cmd_hash = NULL; + } vector_free (cmdvec); cmdvec = NULL; } - if (command_cr) - XFREE(MTYPE_CMD_TOKENS, command_cr); - if (token_cr.desc) - XFREE(MTYPE_CMD_TOKENS, token_cr.desc); if (host.name) XFREE (MTYPE_HOST, host.name); if (host.password) diff --git a/lib/command.h b/lib/command.h index d2fc969d70..d62f7655ee 100644 --- a/lib/command.h +++ b/lib/command.h @@ -8,7 +8,7 @@ * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2, or (at your * option) any later version. - * + * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @@ -26,10 +26,12 @@ #include "vector.h" #include "vty.h" #include "lib/route_types.h" +#include "graph.h" #include "memory.h" #include "hash.h" DECLARE_MTYPE(HOST) +DECLARE_MTYPE(CMD_ARG) /* for test-commands.c */ DECLARE_MTYPE(STRVEC) @@ -68,42 +70,44 @@ struct host }; /* There are some command levels which called from command node. */ -enum node_type +enum node_type { - AUTH_NODE, /* Authentication mode of vty interface. */ - VIEW_NODE, /* View node. Default mode of vty interface. */ - AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ - ENABLE_NODE, /* Enable node. */ - CONFIG_NODE, /* Config node. Default mode of config file. */ - SERVICE_NODE, /* Service node. */ - DEBUG_NODE, /* Debug node. */ + AUTH_NODE, /* Authentication mode of vty interface. */ + VIEW_NODE, /* View node. Default mode of vty interface. */ + AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ + ENABLE_NODE, /* Enable node. */ + CONFIG_NODE, /* Config node. Default mode of config file. */ + SERVICE_NODE, /* Service node. */ + DEBUG_NODE, /* Debug node. */ VRF_DEBUG_NODE, /* Vrf Debug node. */ DEBUG_VNC_NODE, /* Debug VNC node. */ - AAA_NODE, /* AAA node. */ - KEYCHAIN_NODE, /* Key-chain node. */ - KEYCHAIN_KEY_NODE, /* Key-chain key node. */ - NS_NODE, /* Logical-Router node. */ - VRF_NODE, /* VRF mode node. */ - INTERFACE_NODE, /* Interface mode node. */ - ZEBRA_NODE, /* zebra connection node. */ - TABLE_NODE, /* rtm_table selection node. */ - RIP_NODE, /* RIP protocol mode node. */ - RIPNG_NODE, /* RIPng protocol mode node. */ - BGP_NODE, /* BGP protocol mode which includes BGP4+ */ - BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ - BGP_VPNV6_NODE, /* BGP MPLS-VPN PE exchange. */ - BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ - BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ - BGP_IPV6_NODE, /* BGP IPv6 address family */ - BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ - BGP_ENCAP_NODE, /* BGP ENCAP SAFI */ - BGP_ENCAPV6_NODE, /* BGP ENCAP SAFI */ + AAA_NODE, /* AAA node. */ + KEYCHAIN_NODE, /* Key-chain node. */ + KEYCHAIN_KEY_NODE, /* Key-chain key node. */ + NS_NODE, /* Logical-Router node. */ + VRF_NODE, /* VRF mode node. */ + INTERFACE_NODE, /* Interface mode node. */ + ZEBRA_NODE, /* zebra connection node. */ + TABLE_NODE, /* rtm_table selection node. */ + RIP_NODE, /* RIP protocol mode node. */ + RIPNG_NODE, /* RIPng protocol mode node. */ + BGP_NODE, /* BGP protocol mode which includes BGP4+ */ + BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ + BGP_VPNV6_NODE, /* BGP MPLS-VPN PE exchange. */ + BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ + BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ + BGP_IPV6_NODE, /* BGP IPv6 address family */ + BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ + BGP_ENCAP_NODE, /* BGP ENCAP SAFI */ + BGP_ENCAPV6_NODE, /* BGP ENCAP SAFI */ + BGP_VRF_POLICY_NODE, /* BGP VRF policy */ BGP_VNC_DEFAULTS_NODE, /* BGP VNC nve defaults */ BGP_VNC_NVE_GROUP_NODE, /* BGP VNC nve group */ BGP_VNC_L2_GROUP_NODE, /* BGP VNC L2 group */ RFP_DEFAULTS_NODE, /* RFP defaults node */ - OSPF_NODE, /* OSPF protocol mode */ - OSPF6_NODE, /* OSPF protocol for IPv6 mode */ + BGP_EVPN_NODE, /* BGP EVPN node. */ + OSPF_NODE, /* OSPF protocol mode */ + OSPF6_NODE, /* OSPF protocol for IPv6 mode */ LDP_NODE, /* LDP protocol mode */ LDP_IPV4_NODE, /* LDP IPv4 address family */ LDP_IPV6_NODE, /* LDP IPv6 address family */ @@ -111,43 +115,46 @@ enum node_type LDP_IPV6_IFACE_NODE, /* LDP IPv6 Interface */ LDP_L2VPN_NODE, /* LDP L2VPN node */ LDP_PSEUDOWIRE_NODE, /* LDP Pseudowire node */ - ISIS_NODE, /* ISIS protocol mode */ - PIM_NODE, /* PIM protocol mode */ - MASC_NODE, /* MASC for multicast. */ - IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ - IP_NODE, /* Static ip route node. */ - ACCESS_NODE, /* Access list node. */ - PREFIX_NODE, /* Prefix list node. */ - ACCESS_IPV6_NODE, /* Access list node. */ - PREFIX_IPV6_NODE, /* Prefix list node. */ - AS_LIST_NODE, /* AS list node. */ - COMMUNITY_LIST_NODE, /* Community list node. */ - RMAP_NODE, /* Route map node. */ - SMUX_NODE, /* SNMP configuration node. */ - DUMP_NODE, /* Packet dump node. */ - FORWARDING_NODE, /* IP forwarding node. */ + ISIS_NODE, /* ISIS protocol mode */ + PIM_NODE, /* PIM protocol mode */ + MASC_NODE, /* MASC for multicast. */ + IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ + IP_NODE, /* Static ip route node. */ + ACCESS_NODE, /* Access list node. */ + PREFIX_NODE, /* Prefix list node. */ + ACCESS_IPV6_NODE, /* Access list node. */ + PREFIX_IPV6_NODE, /* Prefix list node. */ + AS_LIST_NODE, /* AS list node. */ + COMMUNITY_LIST_NODE, /* Community list node. */ + RMAP_NODE, /* Route map node. */ + SMUX_NODE, /* SNMP configuration node. */ + DUMP_NODE, /* Packet dump node. */ + FORWARDING_NODE, /* IP forwarding node. */ PROTOCOL_NODE, /* protocol filtering node */ MPLS_NODE, /* MPLS config node */ - VTY_NODE, /* Vty node. */ - LINK_PARAMS_NODE, /* Link-parameters node */ + VTY_NODE, /* Vty node. */ + LINK_PARAMS_NODE, /* Link-parameters node */ }; /* Node which has some commands and prompt string and configuration function pointer . */ -struct cmd_node +struct cmd_node { /* Node index. */ - enum node_type node; + enum node_type node; /* Prompt character at vty interface. */ - const char *prompt; + const char *prompt; /* Is this node's configuration goes to vtysh ? */ int vtysh; - + /* Node's configuration write function */ int (*func) (struct vty *); + /* Node's command graph */ + struct graph *cmdgraph; + /* Vector of this node's command list. */ vector cmd_vector; @@ -155,63 +162,67 @@ struct cmd_node struct hash *cmd_hash; }; -enum +/** + * Types for tokens. + * + * The type determines what kind of data the token can match (in the + * matching use case) or hold (in the argv use case). + */ +enum cmd_token_type { - CMD_ATTR_DEPRECATED = 1, - CMD_ATTR_HIDDEN, + WORD_TKN, // words + VARIABLE_TKN, // almost anything + RANGE_TKN, // integer range + IPV4_TKN, // IPV4 addresses + IPV4_PREFIX_TKN, // IPV4 network prefixes + IPV6_TKN, // IPV6 prefixes + IPV6_PREFIX_TKN, // IPV6 network prefixes + + /* plumbing types */ + FORK_TKN, // marks subgraph beginning + JOIN_TKN, // marks subgraph end + START_TKN, // first token in line + END_TKN, // last token in line + + SPECIAL_TKN = FORK_TKN, }; -/* Structure of command element. */ -struct cmd_element +/* Command attributes */ +enum { - const char *string; /* Command specification by string. */ - int (*func) (struct cmd_element *, struct vty *, int, const char *[]); - const char *doc; /* Documentation of this command. */ - int daemon; /* Daemon to which this command belong. */ - vector tokens; /* Vector of cmd_tokens */ - u_char attr; /* Command attributes */ + CMD_ATTR_NORMAL, + CMD_ATTR_DEPRECATED, + CMD_ATTR_HIDDEN, }; - -enum cmd_token_type +/* Comamand token struct. */ +struct cmd_token { - TOKEN_TERMINAL = 0, - TOKEN_MULTIPLE, - TOKEN_KEYWORD, -}; + enum cmd_token_type type; // token type + u_char attr; // token attributes + bool allowrepeat; // matcher allowed to match token repetively? + uint32_t refcnt; -enum cmd_terminal_type -{ - _TERMINAL_BUG = 0, - TERMINAL_LITERAL, - TERMINAL_OPTION, - TERMINAL_VARIABLE, - TERMINAL_VARARG, - TERMINAL_RANGE, - TERMINAL_IPV4, - TERMINAL_IPV4_PREFIX, - TERMINAL_IPV6, - TERMINAL_IPV6_PREFIX, -}; + char *text; // token text + char *desc; // token description + long long min, max; // for ranges + char *arg; // user input that matches this token -/* argument to be recorded on argv[] if it's not a literal */ -#define TERMINAL_RECORD(t) ((t) >= TERMINAL_OPTION) + struct graph_node *forkjoin; // paired FORK/JOIN for JOIN/FORK +}; -/* Command description structure. */ -struct cmd_token +/* Structure of command element. */ +struct cmd_element { - enum cmd_token_type type; - enum cmd_terminal_type terminal; - - /* Used for type == MULTIPLE */ - vector multiple; /* vector of cmd_token, type == FINAL */ + const char *string; /* Command specification by string. */ + const char *doc; /* Documentation of this command. */ + int daemon; /* Daemon to which this command belong. */ + u_char attr; /* Command attributes */ - /* Used for type == KEYWORD */ - vector keyword; /* vector of vector of cmd_tokens */ + /* handler function for command */ + int (*func) (const struct cmd_element *, struct vty *, int, struct cmd_token *[]); - /* Used for type == TERMINAL */ - char *cmd; /* Command string. */ - char *desc; /* Command's description. */ + const char *name; /* symbol name for debugging */ }; /* Return value of the commands. */ @@ -233,193 +244,30 @@ struct cmd_token #define CMD_ARGC_MAX 25 /* Turn off these macros when uisng cpp with extract.pl */ -#ifndef VTYSH_EXTRACT_PL +#ifndef VTYSH_EXTRACT_PL /* helper defines for end-user DEFUN* macros */ #define DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attrs, dnum) \ - struct cmd_element cmdname = \ + static struct cmd_element cmdname = \ { \ .string = cmdstr, \ .func = funcname, \ .doc = helpstr, \ .attr = attrs, \ .daemon = dnum, \ + .name = #cmdname, \ }; #define DEFUN_CMD_FUNC_DECL(funcname) \ - static int funcname (struct cmd_element *, struct vty *, int, const char *[]); + static int funcname (const struct cmd_element *, struct vty *, int, struct cmd_token *[]); #define DEFUN_CMD_FUNC_TEXT(funcname) \ static int funcname \ - (struct cmd_element *self __attribute__ ((unused)), \ + (const struct cmd_element *self __attribute__ ((unused)), \ struct vty *vty __attribute__ ((unused)), \ int argc __attribute__ ((unused)), \ - const char *argv[] __attribute__ ((unused)) ) + struct cmd_token *argv[] __attribute__ ((unused)) ) -/* DEFUN for vty command interafce. Little bit hacky ;-). - * - * DEFUN(funcname, cmdname, cmdstr, helpstr) - * - * funcname - * ======== - * - * Name of the function that will be defined. - * - * cmdname - * ======= - * - * Name of the struct that will be defined for the command. - * - * cmdstr - * ====== - * - * The cmdstr defines the command syntax. It is used by the vty subsystem - * and vtysh to perform matching and completion in the cli. So you have to take - * care to construct it adhering to the following grammar. The names used - * for the production rules losely represent the names used in lib/command.c - * - * cmdstr = cmd_token , { " " , cmd_token } ; - * - * cmd_token = cmd_terminal - * | cmd_multiple - * | cmd_keyword ; - * - * cmd_terminal_fixed = fixed_string - * | variable - * | range - * | ipv4 - * | ipv4_prefix - * | ipv6 - * | ipv6_prefix ; - * - * cmd_terminal = cmd_terminal_fixed - * | option - * | vararg ; - * - * multiple_part = cmd_terminal_fixed ; - * cmd_multiple = "(" , multiple_part , ( "|" | { "|" , multiple_part } ) , ")" ; - * - * keyword_part = fixed_string , { " " , ( cmd_terminal_fixed | cmd_multiple ) } ; - * cmd_keyword = "{" , keyword_part , { "|" , keyword_part } , "}" ; - * - * lowercase = "a" | ... | "z" ; - * uppercase = "A" | ... | "Z" ; - * digit = "0" | ... | "9" ; - * number = digit , { digit } ; - * - * fixed_string = (lowercase | digit) , { lowercase | digit | uppercase | "-" | "_" } ; - * variable = uppercase , { uppercase | "_" } ; - * range = "<" , number , "-" , number , ">" ; - * ipv4 = "A.B.C.D" ; - * ipv4_prefix = "A.B.C.D/M" ; - * ipv6 = "X:X::X:X" ; - * ipv6_prefix = "X:X::X:X/M" ; - * option = "[" , variable , "]" ; - * vararg = "." , variable ; - * - * To put that all in a textual description: A cmdstr is a sequence of tokens, - * separated by spaces. - * - * Terminal Tokens: - * - * A very simple cmdstring would be something like: "show ip bgp". It consists - * of three Terminal Tokens, each containing a fixed string. When this command - * is called, no arguments will be passed down to the function implementing it, - * as it only consists of fixed strings. - * - * Apart from fixed strings, Terminal Tokens can also contain variables: - * An example would be "show ip bgp A.B.C.D". This command expects an IPv4 - * as argument. As this is a variable, the IP address entered by the user will - * be passed down as an argument. Apart from two exceptions, the other options - * for Terminal Tokens behave exactly as we just discussed and only make a - * difference for the CLI. The two exceptions will be discussed in the next - * paragraphs. - * - * A Terminal Token can contain a so called option match. This is a simple - * string variable that the user may omit. An example would be: - * "show interface [IFNAME]". If the user calls this without an interface as - * argument, no arguments will be passed down to the function implementing - * this command. Otherwise, the interface name will be provided to the function - * as a regular argument. - - * Also, a Terminal Token can contain a so called vararg. This is used e.g. in - * "show ip bgp regexp .LINE". The last token is a vararg match and will - * consume all the arguments the user inputs on the command line and append - * those to the list of arguments passed down to the function implementing this - * command. (Therefore, it doesn't make much sense to have any tokens after a - * vararg because the vararg will already consume all the words the user entered - * in the CLI) - * - * Multiple Tokens: - * - * The Multiple Token type can be used if there are multiple possibilities what - * arguments may be used for a command, but it should map to the same function - * nonetheless. An example would be "ip route A.B.C.D/M (reject|blackhole)" - * In that case both "reject" and "blackhole" would be acceptable as last - * arguments. The words matched by Multiple Tokens are always added to the - * argument list, even if they are matched by fixed strings. Such a Multiple - * Token can contain almost any type of token that would also be acceptable - * for a Terminal Token, the exception are optional variables and varag. - * - * There is one special case that is used in some places of Quagga that should be - * pointed out here shortly. An example would be "password (8|) WORD". This - * construct is used to have fixed strings communicated as arguments. (The "8" - * will be passed down as an argument in this case) It does not mean that - * the "8" is optional. Another historic and possibly surprising property of - * this construct is that it consumes two parts of helpstr. (Help - * strings will be explained later) - * - * Keyword Tokens: - * - * There are commands that take a lot of different and possibly optional arguments. - * An example from ospf would be the "default-information originate" command. This - * command takes a lot of optional arguments that may be provided in any order. - * To accomodate such commands, the Keyword Token has been implemented. - * Using the keyword token, the "default-information originate" command and all - * its possible options can be represented using this single cmdstr: - * "default-information originate \ - * {always|metric <0-16777214>|metric-type (1|2)|route-map WORD}" - * - * Keywords always start with a fixed string and may be followed by arguments. - * Except optional variables and vararg, everything is permitted here. - * - * For the special case of a keyword without arguments, either NULL or the - * keyword itself will be pushed as an argument, depending on whether the - * keyword is present. - * For the other keywords, arguments will be only pushed for - * variables/Multiple Tokens. If the keyword is not present, the arguments that - * would have been pushed will be substituted by NULL. - * - * A few examples: - * "default information originate metric-type 1 metric 1000" - * would yield the following arguments: - * { NULL, "1000", "1", NULL } - * - * "default information originate always route-map RMAP-DEFAULT" - * would yield the following arguments: - * { "always", NULL, NULL, "RMAP-DEFAULT" } - * - * helpstr - * ======= - * - * The helpstr is used to show a short explantion for the commands that - * are available when the user presses '?' on the CLI. It is the concatenation - * of the helpstrings for all the tokens that make up the command. - * - * There should be one helpstring for each token in the cmdstr except those - * containing other tokens, like Multiple or Keyword Tokens. For those, there - * will only be the helpstrings of the contained tokens. - * - * The individual helpstrings are expected to be in the same order as their - * respective Tokens appear in the cmdstr. They should each be terminated with - * a linefeed. The last helpstring should be terminated with a linefeed as well. - * - * Care should also be taken to avoid having similar tokens with different - * helpstrings. Imagine e.g. the commands "show ip ospf" and "show ip bgp". - * they both contain a helpstring for "show", but only one will be displayed - * when the user enters "sh?". If those two helpstrings differ, it is not - * defined which one will be shown and the behavior is therefore unpredictable. - */ #define DEFUN(funcname, cmdname, cmdstr, helpstr) \ DEFUN_CMD_FUNC_DECL(funcname) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ @@ -499,7 +347,6 @@ struct cmd_token */ #define CMD_CREATE_STR(s) CMD_CREATE_STR_HELPER(s) #define CMD_CREATE_STR_HELPER(s) #s -#define CMD_RANGE_STR(a,s) "<" CMD_CREATE_STR(a) "-" CMD_CREATE_STR(s) ">" /* Common descriptions. */ #define SHOW_STR "Show running system information\n" @@ -531,17 +378,16 @@ struct cmd_token #define IFNAME_STR "Interface name(e.g. ep0)\n" #define IP6_STR "IPv6 Information\n" #define OSPF6_STR "Open Shortest Path First (OSPF) for IPv6\n" -#define OSPF6_ROUTER_STR "Enable a routing process\n" -#define OSPF6_INSTANCE_STR "<1-65535> Instance ID\n" -#define SECONDS_STR "<1-65535> Seconds\n" +#define OSPF6_INSTANCE_STR "(1-65535) Instance ID\n" +#define SECONDS_STR "Seconds\n" #define ROUTE_STR "Routing Table\n" #define PREFIX_LIST_STR "Build a prefix list\n" #define OSPF6_DUMP_TYPE_LIST \ -"(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)" +"<neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr>" #define ISIS_STR "IS-IS information\n" #define AREA_TAG_STR "[area tag]\n" -#define COMMUNITY_AANN_STR "Community number where AA and NN are <0-65535>\n" -#define COMMUNITY_VAL_STR "Community number in AA:NN format (where AA and NN are <0-65535>) or local-AS|no-advertise|no-export|internet or additive\n" +#define COMMUNITY_AANN_STR "Community number where AA and NN are (0-65535)\n" +#define COMMUNITY_VAL_STR "Community number in AA:NN format (where AA and NN are (0-65535)) or local-AS|no-advertise|no-export|internet or additive\n" #define MPLS_TE_STR "MPLS-TE specific commands\n" #define LINK_PARAMS_STR "Configure interface link parameters\n" #define OSPF_RI_STR "OSPF Router Information specific commands\n" @@ -552,62 +398,56 @@ struct cmd_token /* IPv4 only machine should not accept IPv6 address for peer's IP address. So we replace VTY command string like below. */ -#ifdef HAVE_IPV6 -#define NEIGHBOR_CMD "neighbor (A.B.C.D|X:X::X:X) " -#define NO_NEIGHBOR_CMD "no neighbor (A.B.C.D|X:X::X:X) " #define NEIGHBOR_ADDR_STR "Neighbor address\nIPv6 address\n" -#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|X:X::X:X|WORD) " -#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|X:X::X:X|WORD) " #define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor IPv6 address\nInterface name or neighbor tag\n" #define NEIGHBOR_ADDR_STR3 "Neighbor address\nIPv6 address\nInterface name\n" -#else -#define NEIGHBOR_CMD "neighbor A.B.C.D " -#define NO_NEIGHBOR_CMD "no neighbor A.B.C.D " -#define NEIGHBOR_ADDR_STR "Neighbor address\n" -#define NEIGHBOR_CMD2 "neighbor (A.B.C.D|WORD) " -#define NO_NEIGHBOR_CMD2 "no neighbor (A.B.C.D|WORD) " -#define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n" -#endif /* HAVE_IPV6 */ - -/* Dynamic neighbor (listen range) configuration */ -#ifdef HAVE_IPV6 -#define LISTEN_RANGE_CMD "bgp listen range (A.B.C.D/M|X:X::X:X/M) " -#define LISTEN_RANGE_ADDR_STR "Neighbor address\nNeighbor IPv6 address\n" -#else -#define LISTEN_RANGE_CMD "bgp listen range A.B.C.D/M " -#define LISTEN_RANGE_ADDR_STR "Neighbor address\n" -#endif /* HAVE_IPV6 */ /* Prototypes. */ extern void install_node (struct cmd_node *, int (*) (struct vty *)); extern void install_default (enum node_type); extern void install_element (enum node_type, struct cmd_element *); +/* known issue with uninstall_element: changes to cmd_token->attr (i.e. + * deprecated/hidden) are not reversed. */ +extern void uninstall_element (enum node_type, struct cmd_element *); + /* Concatenates argv[shift] through argv[argc-1] into a single NUL-terminated string with a space between each element (allocated using XMALLOC(MTYPE_TMP)). Returns NULL if shift >= argc. */ -extern char *argv_concat (const char **argv, int argc, int shift); +extern char *argv_concat (struct cmd_token **argv, int argc, int shift); +extern int argv_find (struct cmd_token **argv, int argc, const char *text, int *index); extern vector cmd_make_strvec (const char *); extern void cmd_free_strvec (vector); extern vector cmd_describe_command (vector, struct vty *, int *status); extern char **cmd_complete_command (vector, struct vty *, int *status); -extern char **cmd_complete_command_lib (vector, struct vty *, int *status, int islib); extern const char *cmd_prompt (enum node_type); -extern int command_config_read_one_line (struct vty *vty, struct cmd_element **, int use_config_node); +extern int command_config_read_one_line (struct vty *vty, const struct cmd_element **, int use_config_node); extern int config_from_file (struct vty *, FILE *, unsigned int *line_num); extern enum node_type node_parent (enum node_type); -extern int cmd_execute_command (vector, struct vty *, struct cmd_element **, int); -extern int cmd_execute_command_strict (vector, struct vty *, struct cmd_element **); +extern int cmd_execute_command (vector, struct vty *, const struct cmd_element **, int); +extern int cmd_execute_command_strict (vector, struct vty *, const struct cmd_element **); extern void cmd_init (int); extern void cmd_terminate (void); +extern void cmd_exit (struct vty *vty); +extern int cmd_list_cmds (struct vty *vty, int do_permute); + +extern int cmd_hostname_set (const char *hostname); + +/* NOT safe for general use; call this only if DEV_BUILD! */ +extern void grammar_sandbox_init (void); + +/* memory management for cmd_token */ +extern struct cmd_token *new_cmd_token (enum cmd_token_type, u_char attr, + const char *text, const char *desc); +extern void del_cmd_token (struct cmd_token *); +extern struct cmd_token *copy_cmd_token (struct cmd_token *); + +extern vector completions_to_vec (struct list *completions); +extern void cmd_merge_graphs (struct graph *old, struct graph *new, int direction); +extern void command_parse_format (struct graph *graph, struct cmd_element *cmd); /* Export typical functions. */ -extern struct cmd_element config_end_cmd; -extern struct cmd_element config_exit_cmd; -extern struct cmd_element config_quit_cmd; -extern struct cmd_element config_help_cmd; -extern struct cmd_element config_list_cmd; extern const char *host_config_get (void); extern void host_config_set (const char *); @@ -616,8 +456,9 @@ extern void print_version (const char *); extern int cmd_banner_motd_file (const char *); /* struct host global, ick */ -extern struct host host; +extern struct host host; + +/* text for <cr> command */ +#define CMD_CR_TEXT "<cr>" -/* "<cr>" global */ -extern char *command_cr; #endif /* _ZEBRA_COMMAND_H */ diff --git a/lib/command_lex.l b/lib/command_lex.l new file mode 100644 index 0000000000..deec1757c2 --- /dev/null +++ b/lib/command_lex.l @@ -0,0 +1,85 @@ +/* + * Command format string lexer for CLI backend. + * + * -- + * Copyright (C) 2015 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +%{ +#include "command_parse.h" + +#define YY_USER_ACTION yylloc->last_column += yyleng; +#define LOC_STEP do { if (yylloc) { \ + yylloc->first_column = yylloc->last_column; \ + yylloc->first_line = yylloc->last_line; \ + } } while(0) +%} + +WORD (\-|\+)?[a-z0-9\*][-+_a-zA-Z0-9\*]* +IPV4 A\.B\.C\.D +IPV4_PREFIX A\.B\.C\.D\/M +IPV6 X:X::X:X +IPV6_PREFIX X:X::X:X\/M +VARIABLE [A-Z][-_a-zA-Z:0-9]+ +NUMBER (\-|\+)?[0-9]{1,20} +RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\) + +/* yytext shall be a pointer */ +%pointer +%option noyywrap +%option nounput +%option noinput +%option outfile="command_lex.c" +%option header-file="command_lex.h" +%option prefix="cmd_yy" +%option reentrant +%option bison-bridge +%option bison-locations + +%% +%{ + LOC_STEP; +%} + +[ \t]+ LOC_STEP /* ignore whitespace */; +{WORD} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return WORD;} +{IPV4} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV4;} +{IPV4_PREFIX} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV4_PREFIX;} +{IPV6} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV6;} +{IPV6_PREFIX} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV6_PREFIX;} +{VARIABLE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return VARIABLE;} +{RANGE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return RANGE;} +. {return yytext[0];} +%% + +YY_BUFFER_STATE buffer; + +void set_lexer_string (yyscan_t *scn, const char *string) +{ + *scn = NULL; + yylex_init(scn); + buffer = yy_scan_string (string, *scn); +} + +void cleanup_lexer (yyscan_t *scn) +{ + // yy_delete_buffer (buffer, *scn); + yylex_destroy(*scn); +} diff --git a/lib/command_match.c b/lib/command_match.c new file mode 100644 index 0000000000..bbd9cd091d --- /dev/null +++ b/lib/command_match.c @@ -0,0 +1,1027 @@ +/* + * Input matching routines for CLI backend. + * + * -- + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#include "command_match.h" +#include "memory.h" + +DEFINE_MTYPE_STATIC(LIB, CMD_MATCHSTACK, "Command Match Stack") + +#define MAXDEPTH 64 + +#ifdef TRACE_MATCHER +#define TM 1 +#else +#define TM 0 +#endif + +#define trace_matcher(...) \ + do { if (TM) fprintf (stderr, __VA_ARGS__); } while (0); + +/* matcher helper prototypes */ +static int +add_nexthops (struct list *, struct graph_node *, + struct graph_node **, size_t); + +static struct list * +command_match_r (struct graph_node *, vector, unsigned int, + struct graph_node **); + +static int +score_precedence (enum cmd_token_type); + +static enum match_type +min_match_level (enum cmd_token_type); + +static void +del_arglist (struct list *); + +static struct cmd_token * +disambiguate_tokens (struct cmd_token *, struct cmd_token *, char *); + +static struct list * +disambiguate (struct list *, struct list *, vector, unsigned int); + +int +compare_completions (const void *, const void *); + +/* token matcher prototypes */ +static enum match_type +match_token (struct cmd_token *, char *); + +static enum match_type +match_ipv4 (const char *); + +static enum match_type +match_ipv4_prefix (const char *); + +static enum match_type +match_ipv6 (const char *); + +static enum match_type +match_ipv6_prefix (const char *); + +static enum match_type +match_range (struct cmd_token *, const char *); + +static enum match_type +match_word (struct cmd_token *, const char *); + +static enum match_type +match_variable (struct cmd_token *, const char *); + +/* matching functions */ +static enum matcher_rv matcher_rv; + +enum matcher_rv +command_match (struct graph *cmdgraph, + vector vline, + struct list **argv, + const struct cmd_element **el) +{ + struct graph_node *stack[MAXDEPTH]; + matcher_rv = MATCHER_NO_MATCH; + + // prepend a dummy token to match that pesky start node + vector vvline = vector_init (vline->alloced + 1); + vector_set_index (vvline, 0, (void *) XSTRDUP (MTYPE_TMP, "dummy")); + memcpy (vvline->index + 1, vline->index, sizeof (void *) * vline->alloced); + vvline->active = vline->active + 1; + + struct graph_node *start = vector_slot (cmdgraph->nodes, 0); + if ((*argv = command_match_r (start, vvline, 0, stack))) // successful match + { + struct listnode *head = listhead (*argv); + struct listnode *tail = listtail (*argv); + + // delete dummy start node + del_cmd_token ((struct cmd_token *) head->data); + list_delete_node (*argv, head); + + // get cmd_element out of list tail + *el = listgetdata (tail); + list_delete_node (*argv, tail); + + // now argv is an ordered list of cmd_token matching the user + // input, with each cmd_token->arg holding the corresponding input + assert (*el); + } + + if (!*el) { + trace_matcher ("No match\n"); + } + else { + trace_matcher ("Matched command\n->string %s\n->desc %s\n", (*el)->string, (*el)->doc); + } + + // free the leader token we alloc'd + XFREE (MTYPE_TMP, vector_slot (vvline, 0)); + // free vector + vector_free (vvline); + + return matcher_rv; +} + +/** + * Builds an argument list given a DFA and a matching input line. + * + * First the function determines if the node it is passed matches the first + * token of input. If it does not, it returns NULL (MATCHER_NO_MATCH). If it + * does match, then it saves the input token as the head of an argument list. + * + * The next step is to see if there is further input in the input line. If + * there is not, the current node's children are searched to see if any of them + * are leaves (type END_TKN). If this is the case, then the bottom of the + * recursion stack has been reached, the leaf is pushed onto the argument list, + * the current node is pushed, and the resulting argument list is + * returned (MATCHER_OK). If it is not the case, NULL is returned, indicating + * that there is no match for the input along this path (MATCHER_INCOMPLETE). + * + * If there is further input, then the function recurses on each of the current + * node's children, passing them the input line minus the token that was just + * matched. For each child, the return value of the recursive call is + * inspected. If it is null, then there is no match for the input along the + * subgraph headed by that child. If it is not null, then there is at least one + * input match in that subgraph (more on this in a moment). + * + * If a recursive call on a child returns a non-null value, then it has matched + * the input given it on the subgraph that starts with that child. However, due + * to the flexibility of the grammar, it is sometimes the case that two or more + * child graphs match the same input (two or more of the recursive calls have + * non-NULL return values). This is not a valid state, since only one true + * match is possible. In order to resolve this conflict, the function keeps a + * reference to the child node that most specifically matches the input. This + * is done by assigning each node type a precedence. If a child is found to + * match the remaining input, then the precedence values of the current + * best-matching child and this new match are compared. The node with higher + * precedence is kept, and the other match is discarded. Due to the recursive + * nature of this function, it is only necessary to compare the precedence of + * immediate children, since all subsequent children will already have been + * disambiguated in this way. + * + * In the event that two children are found to match with the same precedence, + * then the input is ambiguous for the passed cmd_element and NULL is returned. + * + * @param[in] start the start node. + * @param[in] vline the vectorized input line. + * @param[in] n the index of the first input token. + * @return A linked list of n elements. The first n-1 elements are pointers to + * struct cmd_token and represent the sequence of tokens matched by the input. + * The ->arg field of each token points to a copy of the input matched on it. + * The final nth element is a pointer to struct cmd_element, which is the + * command that was matched. + * + * If no match was found, the return value is NULL. + */ +static struct list * +command_match_r (struct graph_node *start, vector vline, unsigned int n, + struct graph_node **stack) +{ + assert (n < vector_active (vline)); + + // get the minimum match level that can count as a full match + struct cmd_token *token = start->data; + enum match_type minmatch = min_match_level (token->type); + + /* check history/stack of tokens + * this disallows matching the same one more than once if there is a + * circle in the graph (used for keyword arguments) */ + if (n == MAXDEPTH) + return NULL; + if (!token->allowrepeat) + for (size_t s = 0; s < n; s++) + if (stack[s] == start) + return NULL; + + // get the current operating input token + char *input_token = vector_slot (vline, n); + +#ifdef TRACE_MATCHER + fprintf (stdout, "\"%-20s\" matches \"%-30s\" ? ", input_token, token->text); + enum match_type mt = match_token (token, input_token); + fprintf (stdout, "min: %d - ", minmatch); + switch (mt) + { + case trivial_match: + fprintf (stdout, "trivial_match "); + break; + case no_match: + fprintf (stdout, "no_match "); + break; + case partly_match: + fprintf (stdout, "partly_match "); + break; + case exact_match: + fprintf (stdout, "exact_match "); + break; + } + if (mt >= minmatch) fprintf (stdout, " MATCH"); + fprintf (stdout, "\n"); +#endif + + // if we don't match this node, die + if (match_token (token, input_token) < minmatch) + return NULL; + + stack[n] = start; + + // pointers for iterating linklist + struct listnode *ln; + struct graph_node *gn; + + // get all possible nexthops + struct list *next = list_new(); + add_nexthops (next, start, NULL, 0); + + // determine the best match + int ambiguous = 0; + struct list *currbest = NULL; + for (ALL_LIST_ELEMENTS_RO (next,ln,gn)) + { + // if we've matched all input we're looking for END_TKN + if (n+1 == vector_active (vline)) + { + struct cmd_token *tok = gn->data; + if (tok->type == END_TKN) + { + if (currbest) // there is more than one END_TKN in the follow set + { + ambiguous = 1; + break; + } + currbest = list_new(); + // node should have one child node with the element + struct graph_node *leaf = vector_slot (gn->to, 0); + // last node in the list will hold the cmd_element; + // this is important because list_delete() expects + // that all nodes have the same data type, so when + // deleting this list the last node must be + // manually deleted + struct cmd_element *el = leaf->data; + listnode_add (currbest, el); + currbest->del = (void (*)(void *)) &del_cmd_token; + // do not break immediately; continue walking through the follow set + // to ensure that there is exactly one END_TKN + } + continue; + } + + // else recurse on candidate child node + struct list *result = command_match_r (gn, vline, n+1, stack); + + // save the best match + if (result && currbest) + { + // pick the best of two matches + struct list *newbest = disambiguate (currbest, result, vline, n+1); + // set ambiguity flag + ambiguous = !newbest || (ambiguous && newbest == currbest); + // delete the unnecessary result + struct list *todelete = ((newbest && newbest == result) ? currbest : result); + del_arglist (todelete); + + currbest = newbest ? newbest : currbest; + } + else if (result) + currbest = result; + } + + if (currbest) + { + if (ambiguous) + { + del_arglist (currbest); + currbest = NULL; + matcher_rv = MATCHER_AMBIGUOUS; + } + else + { + // copy token, set arg and prepend to currbest + struct cmd_token *token = start->data; + struct cmd_token *copy = copy_cmd_token (token); + copy->arg = XSTRDUP (MTYPE_CMD_ARG, input_token); + listnode_add_before (currbest, currbest->head, copy); + matcher_rv = MATCHER_OK; + } + } + else if (n+1 == vector_active (vline) && matcher_rv == MATCHER_NO_MATCH) + matcher_rv = MATCHER_INCOMPLETE; + + // cleanup + list_delete (next); + + return currbest; +} + +static void +stack_del (void *val) +{ + XFREE (MTYPE_CMD_MATCHSTACK, val); +} + +enum matcher_rv +command_complete (struct graph *graph, + vector vline, + struct list **completions) +{ + // pointer to next input token to match + char *input_token; + + struct list *current = list_new(), // current nodes to match input token against + *next = list_new(); // possible next hops after current input token + current->del = next->del = stack_del; + + // pointers used for iterating lists + struct graph_node **gstack, **newstack; + struct listnode *node; + + // add all children of start node to list + struct graph_node *start = vector_slot (graph->nodes, 0); + add_nexthops (next, start, &start, 0); + + unsigned int idx; + for (idx = 0; idx < vector_active (vline) && next->count > 0; idx++) + { + list_delete (current); + current = next; + next = list_new(); + next->del = stack_del; + + input_token = vector_slot (vline, idx); + + int exact_match_exists = 0; + for (ALL_LIST_ELEMENTS_RO (current,node,gstack)) + if (!exact_match_exists) + exact_match_exists = (match_token (gstack[0]->data, input_token) == exact_match); + else + break; + + for (ALL_LIST_ELEMENTS_RO (current,node,gstack)) + { + struct cmd_token *token = gstack[0]->data; + + if (token->attr == CMD_ATTR_HIDDEN || token->attr == CMD_ATTR_DEPRECATED) + continue; + + enum match_type minmatch = min_match_level (token->type); + trace_matcher ("\"%s\" matches \"%s\" (%d) ? ", + input_token, token->text, token->type); + + unsigned int last_token = (vector_active (vline) - 1 == idx); + enum match_type matchtype = match_token (token, input_token); + switch (matchtype) + { + // occurs when last token is whitespace + case trivial_match: + trace_matcher ("trivial_match\n"); + assert(last_token); + newstack = XMALLOC (MTYPE_CMD_MATCHSTACK, + sizeof(struct graph_node *)); + /* we're not recursing here, just the first element is OK */ + newstack[0] = gstack[0]; + listnode_add (next, newstack); + break; + case partly_match: + trace_matcher ("trivial_match\n"); + if (exact_match_exists && !last_token) + break; + case exact_match: + trace_matcher ("exact_match\n"); + if (last_token) + { + newstack = XMALLOC (MTYPE_CMD_MATCHSTACK, + sizeof(struct graph_node *)); + /* same as above, not recursing on this */ + newstack[0] = gstack[0]; + listnode_add (next, newstack); + } + else if (matchtype >= minmatch) + add_nexthops (next, gstack[0], gstack, idx + 1); + break; + default: + trace_matcher ("no_match\n"); + break; + } + } + } + + /* Variable summary + * ----------------------------------------------------------------- + * token = last input token processed + * idx = index in `command` of last token processed + * current = set of all transitions from the previous input token + * next = set of all nodes reachable from all nodes in `matched` + */ + + matcher_rv = + idx == vector_active(vline) && next->count ? + MATCHER_OK : + MATCHER_NO_MATCH; + + *completions = NULL; + if (!MATCHER_ERROR(matcher_rv)) + { + // extract cmd_token into list + *completions = list_new (); + for (ALL_LIST_ELEMENTS_RO (next,node,gstack)) { + listnode_add (*completions, gstack[0]->data); + } + } + + list_delete (current); + list_delete (next); + + return matcher_rv; +} + +/** + * Adds all children that are reachable by one parser hop to the given list. + * special tokens except END_TKN are treated as transparent. + * + * @param[in] list to add the nexthops to + * @param[in] node to start calculating nexthops from + * @param[in] stack listing previously visited nodes, if non-NULL. + * @param[in] stackpos how many valid entries are in stack + * @return the number of children added to the list + * + * NB: non-null "stack" means that new stacks will be added to "list" as + * output, instead of direct node pointers! + */ +static int +add_nexthops (struct list *list, struct graph_node *node, + struct graph_node **stack, size_t stackpos) +{ + int added = 0; + struct graph_node *child; + struct graph_node **nextstack; + for (unsigned int i = 0; i < vector_active (node->to); i++) + { + child = vector_slot (node->to, i); + size_t j; + struct cmd_token *token = child->data; + if (!token->allowrepeat && stack) + { + for (j = 0; j < stackpos; j++) + if (child == stack[j]) + break; + if (j != stackpos) + continue; + } + if (token->type >= SPECIAL_TKN && token->type != END_TKN) + { + added += add_nexthops (list, child, stack, stackpos); + } + else + { + if (stack) + { + nextstack = XMALLOC (MTYPE_CMD_MATCHSTACK, + (stackpos + 1) * sizeof(struct graph_node *)); + nextstack[0] = child; + memcpy(nextstack + 1, stack, stackpos * sizeof(struct graph_node *)); + + listnode_add (list, nextstack); + } + else + listnode_add (list, child); + added++; + } + } + + return added; +} + +/** + * Determines the node types for which a partial match may count as a full + * match. Enables command abbrevations. + * + * @param[in] type node type + * @return minimum match level needed to for a token to fully match + */ +static enum match_type +min_match_level (enum cmd_token_type type) +{ + switch (type) + { + // anything matches a start node, for the sake of recursion + case START_TKN: + return no_match; + // allowing words to partly match enables command abbreviation + case WORD_TKN: + return partly_match; + default: + return exact_match; + } +} + +/** + * Assigns precedence scores to node types. + * + * @param[in] type node type to score + * @return precedence score + */ +static int +score_precedence (enum cmd_token_type type) +{ + switch (type) + { + // some of these are mutually exclusive, so they share + // the same precedence value + case IPV4_TKN: + case IPV4_PREFIX_TKN: + case IPV6_TKN: + case IPV6_PREFIX_TKN: + case RANGE_TKN: + return 2; + case WORD_TKN: + return 3; + case VARIABLE_TKN: + return 4; + default: + return 10; + } +} + +/** + * Picks the better of two possible matches for a token. + * + * @param[in] first candidate node matching token + * @param[in] second candidate node matching token + * @param[in] token the token being matched + * @return the best-matching node, or NULL if the two are entirely ambiguous + */ +static struct cmd_token * +disambiguate_tokens (struct cmd_token *first, + struct cmd_token *second, + char *input_token) +{ + // if the types are different, simply go off of type precedence + if (first->type != second->type) + { + int firstprec = score_precedence (first->type); + int secndprec = score_precedence (second->type); + if (firstprec != secndprec) + return firstprec < secndprec ? first : second; + else + return NULL; + } + + // if they're the same, return the more exact match + enum match_type fmtype = match_token (first, input_token); + enum match_type smtype = match_token (second, input_token); + if (fmtype != smtype) + return fmtype > smtype ? first : second; + + return NULL; +} + +/** + * Picks the better of two possible matches for an input line. + * + * @param[in] first candidate list of cmd_token matching vline + * @param[in] second candidate list of cmd_token matching vline + * @param[in] vline the input line being matched + * @param[in] n index into vline to start comparing at + * @return the best-matching list, or NULL if the two are entirely ambiguous + */ +static struct list * +disambiguate (struct list *first, + struct list *second, + vector vline, + unsigned int n) +{ + // doesn't make sense for these to be inequal length + assert (first->count == second->count); + assert (first->count == vector_active (vline) - n+1); + + struct listnode *fnode = listhead (first), + *snode = listhead (second); + struct cmd_token *ftok = listgetdata (fnode), + *stok = listgetdata (snode), + *best = NULL; + + // compare each token, if one matches better use that one + for (unsigned int i = n; i < vector_active (vline); i++) + { + char *token = vector_slot(vline, i); + if ((best = disambiguate_tokens (ftok, stok, token))) + return best == ftok ? first : second; + fnode = listnextnode (fnode); + snode = listnextnode (snode); + ftok = listgetdata (fnode); + stok = listgetdata (snode); + } + + return NULL; +} + +/* + * Deletion function for arglist. + * + * Since list->del for arglists expects all listnode->data to hold cmd_token, + * but arglists have cmd_element as the data for the tail, this function + * manually deletes the tail before deleting the rest of the list as usual. + * + * The cmd_element at the end is *not* a copy. It is the one and only. + * + * @param list the arglist to delete + */ +static void +del_arglist (struct list *list) +{ + // manually delete last node + struct listnode *tail = listtail (list); + tail->data = NULL; + list_delete_node (list, tail); + + // delete the rest of the list as usual + list_delete (list); +} + +/*---------- token level matching functions ----------*/ + +static enum match_type +match_token (struct cmd_token *token, char *input_token) +{ + // nothing trivially matches everything + if (!input_token) + return trivial_match; + + switch (token->type) { + case WORD_TKN: + return match_word (token, input_token); + case IPV4_TKN: + return match_ipv4 (input_token); + case IPV4_PREFIX_TKN: + return match_ipv4_prefix (input_token); + case IPV6_TKN: + return match_ipv6 (input_token); + case IPV6_PREFIX_TKN: + return match_ipv6_prefix (input_token); + case RANGE_TKN: + return match_range (token, input_token); + case VARIABLE_TKN: + return match_variable (token, input_token); + case END_TKN: + default: + return no_match; + } +} + +#define IPV4_ADDR_STR "0123456789." +#define IPV4_PREFIX_STR "0123456789./" + +static enum match_type +match_ipv4 (const char *str) +{ + const char *sp; + int dots = 0, nums = 0; + char buf[4]; + + for (;;) + { + memset (buf, 0, sizeof (buf)); + sp = str; + while (*str != '\0') + { + if (*str == '.') + { + if (dots >= 3) + return no_match; + + if (*(str + 1) == '.') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + if (!isdigit ((int) *str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy (buf, sp, str - sp); + if (atoi (buf) > 255) + return no_match; + + nums++; + + if (*str == '\0') + break; + + str++; + } + + if (nums < 4) + return partly_match; + + return exact_match; +} + +static enum match_type +match_ipv4_prefix (const char *str) +{ + const char *sp; + int dots = 0; + char buf[4]; + + for (;;) + { + memset (buf, 0, sizeof (buf)); + sp = str; + while (*str != '\0' && *str != '/') + { + if (*str == '.') + { + if (dots == 3) + return no_match; + + if (*(str + 1) == '.' || *(str + 1) == '/') + return no_match; + + if (*(str + 1) == '\0') + return partly_match; + + dots++; + break; + } + + if (!isdigit ((int) *str)) + return no_match; + + str++; + } + + if (str - sp > 3) + return no_match; + + strncpy (buf, sp, str - sp); + if (atoi (buf) > 255) + return no_match; + + if (dots == 3) + { + if (*str == '/') + { + if (*(str + 1) == '\0') + return partly_match; + + str++; + break; + } + else if (*str == '\0') + return partly_match; + } + + if (*str == '\0') + return partly_match; + + str++; + } + + sp = str; + while (*str != '\0') + { + if (!isdigit ((int) *str)) + return no_match; + + str++; + } + + if (atoi (sp) > 32) + return no_match; + + return exact_match; +} + + +#define IPV6_ADDR_STR "0123456789abcdefABCDEF:." +#define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./" +#define STATE_START 1 +#define STATE_COLON 2 +#define STATE_DOUBLE 3 +#define STATE_ADDR 4 +#define STATE_DOT 5 +#define STATE_SLASH 6 +#define STATE_MASK 7 + +static enum match_type +match_ipv6 (const char *str) +{ + struct sockaddr_in6 sin6_dummy; + int ret; + + 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; +} + +static enum match_type +match_ipv6_prefix (const char *str) +{ + int state = STATE_START; + int colons = 0, nums = 0, double_colon = 0; + int mask; + const char *sp = NULL; + char *endptr = NULL; + + if (str == NULL) + return partly_match; + + if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) + return no_match; + + while (*str != '\0' && state != STATE_MASK) + { + switch (state) + { + case STATE_START: + if (*str == ':') + { + if (*(str + 1) != ':' && *(str + 1) != '\0') + return no_match; + colons--; + state = STATE_COLON; + } + else + { + sp = str; + state = STATE_ADDR; + } + + continue; + case STATE_COLON: + colons++; + if (*(str + 1) == '/') + return no_match; + else if (*(str + 1) == ':') + state = STATE_DOUBLE; + else + { + sp = str + 1; + state = STATE_ADDR; + } + break; + case STATE_DOUBLE: + if (double_colon) + return no_match; + + if (*(str + 1) == ':') + return no_match; + else + { + if (*(str + 1) != '\0' && *(str + 1) != '/') + colons++; + sp = str + 1; + + if (*(str + 1) == '/') + state = STATE_SLASH; + else + state = STATE_ADDR; + } + + double_colon++; + nums += 1; + break; + case STATE_ADDR: + if (*(str + 1) == ':' || *(str + 1) == '.' + || *(str + 1) == '\0' || *(str + 1) == '/') + { + if (str - sp > 3) + return no_match; + + for (; sp <= str; sp++) + if (*sp == '/') + return no_match; + + nums++; + + if (*(str + 1) == ':') + state = STATE_COLON; + else if (*(str + 1) == '.') + { + if (colons || double_colon) + state = STATE_DOT; + else + return no_match; + } + else if (*(str + 1) == '/') + state = STATE_SLASH; + } + break; + case STATE_DOT: + state = STATE_ADDR; + break; + case STATE_SLASH: + if (*(str + 1) == '\0') + return partly_match; + + state = STATE_MASK; + break; + default: + break; + } + + if (nums > 11) + return no_match; + + if (colons > 7) + return no_match; + + str++; + } + + if (state < STATE_MASK) + return partly_match; + + mask = strtol (str, &endptr, 10); + if (*endptr != '\0') + return no_match; + + if (mask < 0 || mask > 128) + return no_match; + + return exact_match; +} + +static enum match_type +match_range (struct cmd_token *token, const char *str) +{ + assert (token->type == RANGE_TKN); + + char *endptr = NULL; + long long val; + + val = strtoll (str, &endptr, 10); + if (*endptr != '\0') + return no_match; + + if (val < token->min || val > token->max) + return no_match; + else + return exact_match; +} + +static enum match_type +match_word (struct cmd_token *token, const char *word) +{ + assert (token->type == WORD_TKN); + + // if the passed token is 0 length, partly match + if (!strlen(word)) + return partly_match; + + // if the passed token is strictly a prefix of the full word, partly match + if (strlen (word) < strlen (token->text)) + return !strncmp (token->text, word, strlen (word)) ? + partly_match : + no_match; + + // if they are the same length and exactly equal, exact match + else if (strlen (word) == strlen (token->text)) + return !strncmp (token->text, word, strlen (word)) ? exact_match : no_match; + + return no_match; +} + +static enum match_type +match_variable (struct cmd_token *token, const char *word) +{ + assert (token->type == VARIABLE_TKN); + return exact_match; +} diff --git a/lib/command_match.h b/lib/command_match.h new file mode 100644 index 0000000000..9e18b8d905 --- /dev/null +++ b/lib/command_match.h @@ -0,0 +1,113 @@ +/* + * Input matching routines for CLI backend. + * + * -- + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_COMMAND_MATCH_H +#define _ZEBRA_COMMAND_MATCH_H + +#include "graph.h" +#include "linklist.h" +#include "command.h" + +/* These definitions exist in command.c in the current engine but should be + * relocated here in the new engine + */ +enum filter_type +{ + FILTER_RELAXED, + FILTER_STRICT +}; + +/* matcher result value */ +enum matcher_rv +{ + MATCHER_NO_MATCH, + MATCHER_INCOMPLETE, + MATCHER_AMBIGUOUS, + MATCHER_OK, +}; + +/* completion match types */ +enum match_type +{ + trivial_match, // the input is null + no_match, // the input does not match + partly_match, // the input matches but is incomplete + exact_match // the input matches and is complete +}; + +/* Defines which matcher_rv values constitute an error. Should be used with + * 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 \ + ) + +/** + * Attempt to find an exact command match for a line of user input. + * + * @param[in] cmdgraph command graph to match against + * @param[in] vline vectorized input string + * @param[out] argv pointer to argument list if successful match, NULL + * otherwise. The elements of this list are pointers to struct cmd_token + * and represent the sequence of tokens matched by the inpu. The ->arg + * field of each token points to a copy of the input matched on it. These + * may be safely deleted or modified. + * @param[out] element pointer to matched cmd_element if successful match, + * or NULL when MATCHER_ERROR(rv) is true. The cmd_element may *not* be + * safely deleted or modified; it is the instance initialized on startup. + * @return matcher status + */ +enum matcher_rv +command_match (struct graph *cmdgraph, + vector vline, + struct list **argv, + const struct cmd_element **element); + +/** + * Compiles possible completions for a given line of user input. + * + * @param[in] start the start node of the DFA to match against + * @param[in] vline vectorized input string + * @param[out] completions pointer to list of cmd_token representing + * acceptable next inputs, or NULL when MATCHER_ERROR(rv) is true. + * The elements of this list are pointers to struct cmd_token and take on a + * variety of forms depending on the passed vline. If the last element in vline + * is NULL, all previous elements are considered to be complete words (the case + * when a space is the last token of the line) and completions are generated + * based on what could follow that input. If the last element in vline is not + * NULL and each sequential element matches the corresponding tokens of one or + * more commands exactly (e.g. 'encapv4' and not 'en') the same result is + * generated. If the last element is not NULL and the best possible match is a + * partial match, then the result generated will be all possible continuations + * of that element (e.g. 'encapv4', 'encapv6', etc for input 'en'). + * @return matcher status + */ +enum matcher_rv +command_complete (struct graph *cmdgraph, + vector vline, + struct list **completions); + +#endif /* _ZEBRA_COMMAND_MATCH_H */ diff --git a/lib/command_parse.y b/lib/command_parse.y new file mode 100644 index 0000000000..0c415af3aa --- /dev/null +++ b/lib/command_parse.y @@ -0,0 +1,435 @@ +/* + * Command format string parser for CLI backend. + * + * -- + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +%{ +// compile with debugging facilities +#define YYDEBUG 1 +%} + +%locations +/* define parse.error verbose */ +%define api.pure full +/* define api.prefix {cmd_yy} */ + +/* names for generated header and parser files */ +%defines "command_parse.h" +%output "command_parse.c" + +/* note: code blocks are output in order, to both .c and .h: + * 1. %code requires + * 2. %union + bison forward decls + * 3. %code provides + * command_lex.h needs to be included at 3.; it needs the union and YYSTYPE. + * struct parser_ctx is needed for the bison forward decls. + */ +%code requires { + #include "stdlib.h" + #include "string.h" + #include "memory.h" + #include "command.h" + #include "log.h" + #include "graph.h" + + DECLARE_MTYPE(LEX) + + #define YYSTYPE CMD_YYSTYPE + #define YYLTYPE CMD_YYLTYPE + struct parser_ctx; + + /* subgraph semantic value */ + struct subgraph { + struct graph_node *start, *end; + }; +} + +%union { + long long number; + char *string; + struct graph_node *node; + struct subgraph subgraph; +} + +%code provides { + #ifndef FLEX_SCANNER + #include "command_lex.h" + #endif + + extern void set_lexer_string (yyscan_t *scn, const char *string); + extern void cleanup_lexer (yyscan_t *scn); + + struct parser_ctx { + yyscan_t scanner; + + struct cmd_element *el; + + struct graph *graph; + struct graph_node *currnode; + + /* pointers to copy of command docstring */ + char *docstr_start, *docstr; + }; +} + +/* union types for lexed tokens */ +%token <string> WORD +%token <string> IPV4 +%token <string> IPV4_PREFIX +%token <string> IPV6 +%token <string> IPV6_PREFIX +%token <string> VARIABLE +%token <string> RANGE + +/* union types for parsed rules */ +%type <node> start +%type <node> literal_token +%type <node> placeholder_token +%type <node> simple_token +%type <subgraph> selector +%type <subgraph> selector_token +%type <subgraph> selector_token_seq +%type <subgraph> selector_seq_seq + +%code { + + /* bison declarations */ + void + cmd_yyerror (CMD_YYLTYPE *locp, struct parser_ctx *ctx, char const *msg); + + /* helper functions for parser */ + static const char * + doc_next (struct parser_ctx *ctx); + + static struct graph_node * + new_token_node (struct parser_ctx *, + enum cmd_token_type type, + const char *text, + const char *doc); + + static void + terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx, + struct graph_node *); + + static void + cleanup (struct parser_ctx *ctx); + + #define scanner ctx->scanner +} + +/* yyparse parameters */ +%lex-param {yyscan_t scanner} +%parse-param {struct parser_ctx *ctx} + +/* called automatically before yyparse */ +%initial-action { + /* clear state pointers */ + ctx->currnode = vector_slot (ctx->graph->nodes, 0); + + /* copy docstring and keep a pointer to the copy */ + if (ctx->el->doc) + { + // allocate a new buffer, making room for a flag + size_t length = (size_t) strlen (ctx->el->doc) + 2; + ctx->docstr = malloc (length); + memcpy (ctx->docstr, ctx->el->doc, strlen (ctx->el->doc)); + // set the flag so doc_next knows when to print a warning + ctx->docstr[length - 2] = 0x03; + // null terminate + ctx->docstr[length - 1] = 0x00; + } + ctx->docstr_start = ctx->docstr; +} + +%% + +start: + cmd_token_seq +{ + // tack on the command element + terminate_graph (&@1, ctx, ctx->currnode); +} +| cmd_token_seq placeholder_token '.' '.' '.' +{ + if ((ctx->currnode = graph_add_edge (ctx->currnode, $2)) != $2) + graph_delete_node (ctx->graph, $2); + + ((struct cmd_token *)ctx->currnode->data)->allowrepeat = 1; + + // adding a node as a child of itself accepts any number + // of the same token, which is what we want for variadics + graph_add_edge (ctx->currnode, ctx->currnode); + + // tack on the command element + terminate_graph (&@1, ctx, ctx->currnode); +} +; + +cmd_token_seq: + /* empty */ +| cmd_token_seq cmd_token +; + +cmd_token: + simple_token +{ + if ((ctx->currnode = graph_add_edge (ctx->currnode, $1)) != $1) + graph_delete_node (ctx->graph, $1); +} +| selector +{ + graph_add_edge (ctx->currnode, $1.start); + ctx->currnode = $1.end; +} +; + +simple_token: + literal_token +| placeholder_token +; + +literal_token: WORD +{ + $$ = new_token_node (ctx, WORD_TKN, $1, doc_next(ctx)); + XFREE (MTYPE_LEX, $1); +} +; + +placeholder_token: + IPV4 +{ + $$ = new_token_node (ctx, IPV4_TKN, $1, doc_next(ctx)); + XFREE (MTYPE_LEX, $1); +} +| IPV4_PREFIX +{ + $$ = new_token_node (ctx, IPV4_PREFIX_TKN, $1, doc_next(ctx)); + XFREE (MTYPE_LEX, $1); +} +| IPV6 +{ + $$ = new_token_node (ctx, IPV6_TKN, $1, doc_next(ctx)); + XFREE (MTYPE_LEX, $1); +} +| IPV6_PREFIX +{ + $$ = new_token_node (ctx, IPV6_PREFIX_TKN, $1, doc_next(ctx)); + XFREE (MTYPE_LEX, $1); +} +| VARIABLE +{ + $$ = new_token_node (ctx, VARIABLE_TKN, $1, doc_next(ctx)); + XFREE (MTYPE_LEX, $1); +} +| RANGE +{ + $$ = new_token_node (ctx, RANGE_TKN, $1, doc_next(ctx)); + struct cmd_token *token = $$->data; + + // get the numbers out + yylval.string++; + token->min = strtoll (yylval.string, &yylval.string, 10); + strsep (&yylval.string, "-"); + token->max = strtoll (yylval.string, &yylval.string, 10); + + // validate range + if (token->min > token->max) cmd_yyerror (&@1, ctx, "Invalid range."); + + XFREE (MTYPE_LEX, $1); +} + +/* <selector|set> productions */ +selector: '<' selector_seq_seq '>' +{ + $$ = $2; +}; + +selector_seq_seq: + selector_seq_seq '|' selector_token_seq +{ + $$ = $1; + graph_add_edge ($$.start, $3.start); + graph_add_edge ($3.end, $$.end); +} +| selector_token_seq +{ + $$.start = new_token_node (ctx, FORK_TKN, NULL, NULL); + $$.end = new_token_node (ctx, JOIN_TKN, NULL, NULL); + ((struct cmd_token *)$$.start->data)->forkjoin = $$.end; + ((struct cmd_token *)$$.end->data)->forkjoin = $$.start; + + graph_add_edge ($$.start, $1.start); + graph_add_edge ($1.end, $$.end); +} +; + +/* {keyword} productions */ +selector: '{' selector_seq_seq '}' +{ + $$ = $2; + graph_add_edge ($$.end, $$.start); + /* there is intentionally no start->end link, for two reasons: + * 1) this allows "at least 1 of" semantics, which are otherwise impossible + * 2) this would add a start->end->start loop in the graph that the current + * loop-avoidal fails to handle + * just use [{a|b}] if neccessary, that will work perfectly fine, and reason + * #1 is good enough to keep it this way. */ +}; + + +selector_token: + simple_token +{ + $$.start = $$.end = $1; +} +| selector +; + +selector_token_seq: + selector_token_seq selector_token +{ + graph_add_edge ($1.end, $2.start); + $$.start = $1.start; + $$.end = $2.end; +} +| selector_token +; + +/* [option] productions */ +selector: '[' selector_seq_seq ']' +{ + $$ = $2; + graph_add_edge ($$.start, $$.end); +} +; + +%% + +#undef scanner + +DEFINE_MTYPE(LIB, LEX, "Lexer token (temporary)") + +void +command_parse_format (struct graph *graph, struct cmd_element *cmd) +{ + struct parser_ctx ctx = { .graph = graph, .el = cmd }; + + // set to 1 to enable parser traces + yydebug = 0; + + set_lexer_string (&ctx.scanner, cmd->string); + + // parse command into DFA + cmd_yyparse (&ctx); + + /* cleanup lexer */ + cleanup_lexer (&ctx.scanner); + + // cleanup + cleanup (&ctx); +} + +/* parser helper functions */ + +void +yyerror (CMD_YYLTYPE *loc, struct parser_ctx *ctx, char const *msg) +{ + char *tmpstr = strdup(ctx->el->string); + char *line, *eol; + char spacing[256]; + int lineno = 0; + + zlog_err ("%s: FATAL parse error: %s", __func__, msg); + zlog_err ("%s: %d:%d-%d of this command definition:", __func__, loc->first_line, loc->first_column, loc->last_column); + + line = tmpstr; + do { + lineno++; + eol = strchr(line, '\n'); + if (eol) + *eol++ = '\0'; + + zlog_err ("%s: | %s", __func__, line); + if (lineno == loc->first_line && lineno == loc->last_line + && loc->first_column < (int)sizeof(spacing) - 1 + && loc->last_column < (int)sizeof(spacing) - 1) { + + int len = loc->last_column - loc->first_column; + if (len == 0) + len = 1; + + memset(spacing, ' ', loc->first_column - 1); + memset(spacing + loc->first_column - 1, '^', len); + spacing[loc->first_column - 1 + len] = '\0'; + zlog_err ("%s: | %s", __func__, spacing); + } + } while ((line = eol)); + free(tmpstr); +} + +static void +cleanup (struct parser_ctx *ctx) +{ + /* free resources */ + free (ctx->docstr_start); + + /* clear state pointers */ + ctx->currnode = NULL; + ctx->docstr_start = ctx->docstr = NULL; +} + +static void +terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx, + struct graph_node *finalnode) +{ + // end of graph should look like this + // * -> finalnode -> END_TKN -> cmd_element + struct cmd_element *element = ctx->el; + struct graph_node *end_token_node = + new_token_node (ctx, END_TKN, CMD_CR_TEXT, ""); + struct graph_node *end_element_node = + graph_new_node (ctx->graph, element, NULL); + + graph_add_edge (finalnode, end_token_node); + graph_add_edge (end_token_node, end_element_node); +} + +static const char * +doc_next (struct parser_ctx *ctx) +{ + const char *piece = ctx->docstr ? strsep (&ctx->docstr, "\n") : ""; + if (*piece == 0x03) + { + zlog_debug ("Ran out of docstring while parsing '%s'", ctx->el->string); + piece = ""; + } + + return piece; +} + +static struct graph_node * +new_token_node (struct parser_ctx *ctx, enum cmd_token_type type, + const char *text, const char *doc) +{ + struct cmd_token *token = new_cmd_token (type, ctx->el->attr, text, doc); + return graph_new_node (ctx->graph, token, (void (*)(void *)) &del_cmd_token); +} @@ -177,8 +177,11 @@ csv_decode_record(csv_record_t *rec) field = strpbrk(curr, ","); } field = strstr(curr, "\n"); + if (!field) + return; + fld = malloc(sizeof(csv_field_t)); - if (field && fld) { + if (fld) { fld->field = curr; fld->field_len = field-curr; TAILQ_INSERT_TAIL(&(rec->fields), fld, next_field); @@ -239,6 +242,9 @@ csv_encode (csv_t *csv, rec = malloc(sizeof(csv_record_t)); if (!rec) { log_error("record malloc failed\n"); + if (!buf) + free(str); + va_end(list); return (NULL); } csv_init_record(rec); @@ -255,6 +261,7 @@ csv_encode (csv_t *csv, if (!fld) { log_error("fld malloc failed\n"); csv_remove_record(csv, rec); + va_end(list); return (NULL); } if (tempc < (count - 1)) { @@ -416,6 +423,7 @@ csv_clone_record (csv_t *csv, csv_record_t *in_rec, csv_record_t **out_rec) curr = calloc(1, csv->buflen); if (!curr) { log_error("field str malloc failed\n"); + free(rec); return; } rec->record = curr; @@ -518,7 +526,7 @@ csv_concat_record (csv_t *csv, curr = (char *)calloc(1, csv->buflen); if (!curr) { log_error("field str malloc failed\n"); - return (NULL); + goto out_rec; } rec->record = curr; @@ -526,7 +534,7 @@ csv_concat_record (csv_t *csv, ret = strstr(rec1->record, "\n"); if (!ret) { log_error("rec1 str not properly formatted\n"); - return (NULL); + goto out_curr; } snprintf(curr, (int)(ret - rec1->record + 1), "%s", rec1->record); @@ -535,7 +543,7 @@ csv_concat_record (csv_t *csv, ret = strstr(rec2->record, "\n"); if (!ret) { log_error("rec2 str not properly formatted\n"); - return (NULL); + goto out_curr; } snprintf((curr+strlen(curr)), (int)(ret - rec2->record + 1), "%s", @@ -556,6 +564,12 @@ csv_concat_record (csv_t *csv, csv_insert_record(csv, rec); return rec; + +out_curr: + free(curr); +out_rec: + free(rec); + return NULL; } void @@ -569,6 +583,8 @@ csv_decode (csv_t *csv, char *inbuf) pos = strpbrk(buf, "\n"); while (pos != NULL) { rec = calloc(1, sizeof(csv_record_t)); + if (!rec) + return; csv_init_record(rec); TAILQ_INSERT_TAIL(&(csv->records), rec, next_record); csv->num_recs++; diff --git a/lib/distribute.c b/lib/distribute.c index 498410c22d..2e76e352cb 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -161,8 +161,8 @@ distribute_cmp (const struct distribute *dist1, const struct distribute *dist2) } /* Set access-list name to the distribute list. */ -static struct distribute * -distribute_list_set (const char *ifname, enum distribute_type type, +static void +distribute_list_set (const char *ifname, enum distribute_type type, const char *alist_name) { struct distribute *dist; @@ -175,14 +175,12 @@ distribute_list_set (const char *ifname, enum distribute_type type, /* Apply this distribute-list to the interface. */ (*distribute_add_hook) (dist); - - return dist; } /* Unset distribute-list. If matched distribute-list exist then return 1. */ static int -distribute_list_unset (const char *ifname, enum distribute_type type, +distribute_list_unset (const char *ifname, enum distribute_type type, const char *alist_name) { struct distribute *dist; @@ -192,9 +190,9 @@ distribute_list_unset (const char *ifname, enum distribute_type type, return 0; if (!dist->list[type]) - return 0; + return 0; if (strcmp (dist->list[type], alist_name) != 0) - return 0; + return 0; XFREE(MTYPE_DISTRIBUTE_NAME, dist->list[type]); dist->list[type] = NULL; @@ -208,7 +206,7 @@ distribute_list_unset (const char *ifname, enum distribute_type type, } /* Set access-list name to the distribute list. */ -static struct distribute * +static void distribute_list_prefix_set (const char *ifname, enum distribute_type type, const char *plist_name) { @@ -222,8 +220,6 @@ distribute_list_prefix_set (const char *ifname, enum distribute_type type, /* Apply this distribute-list to the interface. */ (*distribute_add_hook) (dist); - - return dist; } /* Unset distribute-list. If matched distribute-list exist then @@ -239,9 +235,9 @@ distribute_list_prefix_unset (const char *ifname, enum distribute_type type, return 0; if (!dist->prefix[type]) - return 0; + return 0; if (strcmp (dist->prefix[type], plist_name) != 0) - return 0; + return 0; XFREE(MTYPE_DISTRIBUTE_NAME, dist->prefix[type]); dist->prefix[type] = NULL; @@ -254,525 +250,101 @@ distribute_list_prefix_unset (const char *ifname, enum distribute_type type, return 1; } -DEFUN (distribute_list_all, - distribute_list_all_cmd, - "distribute-list WORD (in|out)", - "Filter networks in routing updates\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") -{ - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V4_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V4_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - /* Get interface name corresponding distribute list. */ - distribute_list_set (NULL, type, argv[0]); - - return CMD_SUCCESS; -} - -DEFUN (ipv6_distribute_list_all, - ipv6_distribute_list_all_cmd, - "ipv6 distribute-list WORD (in|out)", - "Filter networks in routing updates\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") -{ - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V6_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V6_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - /* Get interface name corresponding distribute list. */ - distribute_list_set (NULL, type, argv[0]); - - return CMD_SUCCESS; -} - -ALIAS (ipv6_distribute_list_all, - ipv6_as_v4_distribute_list_all_cmd, - "distribute-list WORD (in|out)", - "Filter networks in routing updates\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") - -DEFUN (no_distribute_list_all, - no_distribute_list_all_cmd, - "no distribute-list WORD (in|out)", - NO_STR - "Filter networks in routing updates\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") -{ - int ret; - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V4_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V4_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - ret = distribute_list_unset (NULL, type, argv[0]); - if (! ret) - { - vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_distribute_list_all, - no_ipv6_distribute_list_all_cmd, - "no ipv6 distribute-list WORD (in|out)", - NO_STR - "Filter networks in routing updates\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") -{ - int ret; - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V6_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V6_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - ret = distribute_list_unset (NULL, type, argv[0]); - if (! ret) - { - vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -ALIAS (no_ipv6_distribute_list_all, - no_ipv6_as_v4_distribute_list_all_cmd, - "no distribute-list WORD (in|out)", - NO_STR - "Filter networks in routing updates\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") - DEFUN (distribute_list, distribute_list_cmd, - "distribute-list WORD (in|out) WORD", + "distribute-list [prefix] WORD <in|out> [WORD]", "Filter networks in routing updates\n" "Access-list name\n" "Filter incoming routing updates\n" "Filter outgoing routing updates\n" "Interface name\n") { - enum distribute_type type; + int prefix = (argv[1]->type == WORD_TKN) ? 1 : 0; /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V4_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V4_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); - return CMD_WARNING; - } + enum distribute_type type = argv[2 + prefix]->arg[0] == 'i' ? + DISTRIBUTE_V4_IN : DISTRIBUTE_V4_OUT; + + /* Set appropriate function call */ + void (*distfn)(const char *, enum distribute_type, const char *) = prefix ? + &distribute_list_prefix_set : &distribute_list_set; + + /* if interface is present, get name */ + const char *ifname = NULL; + if (argv[argc - 1]->type == VARIABLE_TKN) + ifname = argv[argc - 1]->arg; /* Get interface name corresponding distribute list. */ - distribute_list_set (argv[2], type, argv[0]); + distfn (ifname, type, argv[1 + prefix]->arg); return CMD_SUCCESS; } DEFUN (ipv6_distribute_list, ipv6_distribute_list_cmd, - "ipv6 distribute-list WORD (in|out) WORD", - "Filter networks in routing updates\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") -{ - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V6_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V6_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); - return CMD_WARNING; - } - - /* Get interface name corresponding distribute list. */ - distribute_list_set (argv[2], type, argv[0]); - - return CMD_SUCCESS; -} - -ALIAS (ipv6_distribute_list, - ipv6_as_v4_distribute_list_cmd, - "distribute-list WORD (in|out) WORD", - "Filter networks in routing updates\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") - -DEFUN (no_distribute_list, no_distribute_list_cmd, - "no distribute-list WORD (in|out) WORD", - NO_STR + "ipv6 distribute-list [prefix] WORD <in|out> [WORD]", + "IPv6\n" "Filter networks in routing updates\n" + "Specify a prefix\n" "Access-list name\n" "Filter incoming routing updates\n" "Filter outgoing routing updates\n" "Interface name\n") { - int ret; - enum distribute_type type; + int prefix = (argv[2]->type == WORD_TKN) ? 1 : 0; /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V4_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V4_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); - return CMD_WARNING; - } + enum distribute_type type = argv[3 + prefix]->arg[0] == 'i' ? + DISTRIBUTE_V6_IN : DISTRIBUTE_V6_OUT; - ret = distribute_list_unset (argv[2], type, argv[0]); - if (! ret) - { - vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_distribute_list, - no_ipv6_distribute_list_cmd, - "no ipv6 distribute-list WORD (in|out) WORD", - NO_STR - "Filter networks in routing updates\n" - "Access-list name\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") -{ - int ret; - enum distribute_type type; + /* Set appropriate function call */ + void (*distfn)(const char *, enum distribute_type, const char *) = prefix ? + &distribute_list_prefix_set : &distribute_list_set; + + /* if interface is present, get name */ + const char *ifname = NULL; + if (argv[argc - 1]->type == VARIABLE_TKN) + ifname = argv[argc - 1]->arg; - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V6_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V6_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); - return CMD_WARNING; - } + /* Get interface name corresponding distribute list. */ + distfn (ifname, type, argv[1 + prefix]->arg); - ret = distribute_list_unset (argv[2], type, argv[0]); - if (! ret) - { - vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); - return CMD_WARNING; - } return CMD_SUCCESS; } - -ALIAS (no_ipv6_distribute_list, - no_ipv6_as_v4_distribute_list_cmd, - "no distribute-list WORD (in|out) WORD", + +DEFUN (no_distribute_list, + no_distribute_list_cmd, + "no [ipv6] distribute-list [prefix] WORD <in|out> [WORD]", NO_STR "Filter networks in routing updates\n" "Access-list name\n" "Filter incoming routing updates\n" "Filter outgoing routing updates\n" "Interface name\n") - -DEFUN (distribute_list_prefix_all, - distribute_list_prefix_all_cmd, - "distribute-list prefix WORD (in|out)", - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") { - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V4_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V4_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - /* Get interface name corresponding distribute list. */ - distribute_list_prefix_set (NULL, type, argv[0]); + int ipv6 = strmatch(argv[1]->text, "ipv6"); + int prefix = (argv[2 + ipv6]->type == WORD_TKN) ? 1 : 0; - return CMD_SUCCESS; -} - -DEFUN (ipv6_distribute_list_prefix_all, - ipv6_distribute_list_prefix_all_cmd, - "ipv6 distribute-list prefix WORD (in|out)", - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") -{ - enum distribute_type type; + int idx_alname = 2 + ipv6 + prefix; + int idx_disttype = idx_alname + 1; /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V6_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V6_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - /* Get interface name corresponding distribute list. */ - distribute_list_prefix_set (NULL, type, argv[0]); - - return CMD_SUCCESS; -} - -ALIAS (ipv6_distribute_list_prefix_all, - ipv6_as_v4_distribute_list_prefix_all_cmd, - "distribute-list prefix WORD (in|out)", - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") + enum distribute_type distin = (ipv6) ? DISTRIBUTE_V6_IN : DISTRIBUTE_V4_IN; + enum distribute_type distout = (ipv6) ? DISTRIBUTE_V6_OUT : DISTRIBUTE_V4_OUT; -DEFUN (no_distribute_list_prefix_all, - no_distribute_list_prefix_all_cmd, - "no distribute-list prefix WORD (in|out)", - NO_STR - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") -{ - int ret; - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V4_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V4_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - ret = distribute_list_prefix_unset (NULL, type, argv[0]); - if (! ret) - { - vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_distribute_list_prefix_all, - no_ipv6_distribute_list_prefix_all_cmd, - "no ipv6 distribute-list prefix WORD (in|out)", - NO_STR - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") -{ - int ret; - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V6_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V6_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - ret = distribute_list_prefix_unset (NULL, type, argv[0]); - if (! ret) - { - vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -ALIAS (no_ipv6_distribute_list_prefix_all, - no_ipv6_as_v4_distribute_list_prefix_all_cmd, - "no distribute-list prefix WORD (in|out)", - NO_STR - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n") - -DEFUN (distribute_list_prefix, distribute_list_prefix_cmd, - "distribute-list prefix WORD (in|out) WORD", - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") -{ - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V4_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V4_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - /* Get interface name corresponding distribute list. */ - distribute_list_prefix_set (argv[2], type, argv[0]); - - return CMD_SUCCESS; -} - -DEFUN (ipv6_distribute_list_prefix, - ipv6_distribute_list_prefix_cmd, - "ipv6 distribute-list prefix WORD (in|out) WORD", - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") -{ - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V6_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V6_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } + enum distribute_type type = argv[idx_disttype]->arg[0] == 'i' ? distin : distout; + /* Set appropriate function call */ + int (*distfn)(const char *, enum distribute_type, const char *) = prefix ? + &distribute_list_prefix_unset : &distribute_list_unset; + + /* if interface is present, get name */ + const char *ifname = NULL; + if (argv[argc - 1]->type == VARIABLE_TKN) + ifname = argv[argc - 1]->arg; /* Get interface name corresponding distribute list. */ - distribute_list_prefix_set (argv[2], type, argv[0]); - - return CMD_SUCCESS; -} - -ALIAS (ipv6_distribute_list_prefix, - ipv6_as_v4_distribute_list_prefix_cmd, - "distribute-list prefix WORD (in|out) WORD", - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") - -DEFUN (no_distribute_list_prefix, no_distribute_list_prefix_cmd, - "no distribute-list prefix WORD (in|out) WORD", - NO_STR - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") -{ - int ret; - enum distribute_type type; + int ret = distfn (ifname, type, argv[2 + prefix]->arg); - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V4_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V4_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - ret = distribute_list_prefix_unset (argv[2], type, argv[0]); if (! ret) { vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); @@ -781,52 +353,6 @@ DEFUN (no_distribute_list_prefix, no_distribute_list_prefix_cmd, return CMD_SUCCESS; } -DEFUN (no_ipv6_distribute_list_prefix, - no_ipv6_distribute_list_prefix_cmd, - "no ipv6 distribute-list prefix WORD (in|out) WORD", - NO_STR - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") -{ - int ret; - enum distribute_type type; - - /* Check of distribute list type. */ - if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_V6_IN; - else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_V6_OUT; - else - { - vty_out (vty, "distribute list direction must be [in|out]%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - ret = distribute_list_prefix_unset (argv[2], type, argv[0]); - if (! ret) - { - vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -ALIAS (no_ipv6_distribute_list_prefix, - no_ipv6_as_v4_distribute_list_prefix_cmd, - "no distribute-list prefix WORD (in|out) WORD", - NO_STR - "Filter networks in routing updates\n" - "Filter prefixes in routing updates\n" - "Name of an IP prefix-list\n" - "Filter incoming routing updates\n" - "Filter outgoing routing updates\n" - "Interface name\n") - static int distribute_print (struct vty *vty, char *tab[], int is_prefix, enum distribute_type type, int has_print) @@ -851,7 +377,7 @@ config_show_distribute (struct vty *vty) /* Output filter configuration. */ dist = distribute_lookup (NULL); - vty_out(vty, " Outgoing update filter list for all interface is"); + vty_out (vty, " Outgoing update filter list for all interface is"); has_print = 0; if (dist) { @@ -874,8 +400,8 @@ config_show_distribute (struct vty *vty) { dist = mp->data; if (dist->ifname) - { - vty_out (vty, " %s filtered by", dist->ifname); + { + vty_out (vty, " %s filtered by", dist->ifname); has_print = 0; has_print = distribute_print(vty, dist->list, 0, DISTRIBUTE_V4_OUT, has_print); @@ -886,16 +412,16 @@ config_show_distribute (struct vty *vty) has_print = distribute_print(vty, dist->prefix, 1, DISTRIBUTE_V6_OUT, has_print); if (has_print) - vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); else vty_out(vty, " nothing%s", VTY_NEWLINE); - } + } } /* Input filter configuration. */ dist = distribute_lookup (NULL); - vty_out(vty, " Incoming update filter list for all interface is"); + vty_out (vty, " Incoming update filter list for all interface is"); has_print = 0; if (dist) { @@ -917,9 +443,9 @@ config_show_distribute (struct vty *vty) for (mp = disthash->index[i]; mp; mp = mp->next) { dist = mp->data; - if (dist->ifname) - { - vty_out (vty, " %s filtered by", dist->ifname); + if (dist->ifname) + { + vty_out (vty, " %s filtered by", dist->ifname); has_print = 0; has_print = distribute_print(vty, dist->list, 0, DISTRIBUTE_V4_IN, has_print); @@ -930,10 +456,10 @@ config_show_distribute (struct vty *vty) has_print = distribute_print(vty, dist->prefix, 1, DISTRIBUTE_V6_IN, has_print); if (has_print) - vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); else vty_out(vty, " nothing%s", VTY_NEWLINE); - } + } } return 0; } @@ -997,39 +523,30 @@ distribute_list_init (int node) { disthash = hash_create (distribute_hash_make, (int (*) (const void *, const void *)) distribute_cmp); - /* install v4 */ - if (node == RIP_NODE) { - install_element (node, &distribute_list_all_cmd); - install_element (node, &no_distribute_list_all_cmd); - install_element (node, &distribute_list_cmd); - install_element (node, &no_distribute_list_cmd); - install_element (node, &distribute_list_prefix_all_cmd); - install_element (node, &no_distribute_list_prefix_all_cmd); - install_element (node, &distribute_list_prefix_cmd); - install_element (node, &no_distribute_list_prefix_cmd); - } + + install_element (node, &distribute_list_cmd); + install_element (node, &no_distribute_list_cmd); +/* + install_element (RIP_NODE, &distribute_list_cmd); + install_element (RIP_NODE, &no_distribute_list_cmd); + install_element (RIPNG_NODE, &distribute_list_cmd); + install_element (RIPNG_NODE, &no_distribute_list_cmd); + */ /* install v6 */ if (node == RIPNG_NODE) { - install_element (node, &ipv6_distribute_list_all_cmd); - install_element (node, &no_ipv6_distribute_list_all_cmd); - install_element (node, &ipv6_distribute_list_cmd); - install_element (node, &no_ipv6_distribute_list_cmd); - install_element (node, &ipv6_distribute_list_prefix_all_cmd); - install_element (node, &no_ipv6_distribute_list_prefix_all_cmd); - install_element (node, &ipv6_distribute_list_prefix_cmd); - install_element (node, &no_ipv6_distribute_list_prefix_cmd); + install_element (RIPNG_NODE, &ipv6_distribute_list_cmd); } - /* install v4 syntax command for v6 only protocols. */ - if (node == RIPNG_NODE) { - install_element (node, &ipv6_as_v4_distribute_list_all_cmd); - install_element (node, &no_ipv6_as_v4_distribute_list_all_cmd); - install_element (node, &ipv6_as_v4_distribute_list_cmd); - install_element (node, &no_ipv6_as_v4_distribute_list_cmd); - install_element (node, &ipv6_as_v4_distribute_list_prefix_all_cmd); - install_element (node, &no_ipv6_as_v4_distribute_list_prefix_all_cmd); - install_element (node, &ipv6_as_v4_distribute_list_prefix_cmd); - install_element (node, &no_ipv6_as_v4_distribute_list_prefix_cmd); - } + /* TODO: install v4 syntax command for v6 only protocols. */ + /* if (node == RIPNG_NODE) { + * install_element (node, &ipv6_as_v4_distribute_list_all_cmd); + * install_element (node, &no_ipv6_as_v4_distribute_list_all_cmd); + * install_element (node, &ipv6_as_v4_distribute_list_cmd); + * install_element (node, &no_ipv6_as_v4_distribute_list_cmd); + * install_element (node, &ipv6_as_v4_distribute_list_prefix_all_cmd); + * install_element (node, &no_ipv6_as_v4_distribute_list_prefix_all_cmd); + * install_element (node, &ipv6_as_v4_distribute_list_prefix_cmd); + * install_element (node, &no_ipv6_as_v4_distribute_list_prefix_cmd); + }*/ } diff --git a/lib/filter.c b/lib/filter.c index e9ba715c92..fd73d4de73 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -105,7 +105,6 @@ static struct access_master access_master_ipv4 = NULL, }; -#ifdef HAVE_IPV6 /* Static structure for IPv6 access_list's master. */ static struct access_master access_master_ipv6 = { @@ -114,17 +113,14 @@ static struct access_master access_master_ipv6 = NULL, NULL, }; -#endif /* HAVE_IPV6 */ static struct access_master * access_master_get (afi_t afi) { if (afi == AFI_IP) return &access_master_ipv4; -#ifdef HAVE_IPV6 else if (afi == AFI_IP6) return &access_master_ipv6; -#endif /* HAVE_IPV6 */ return NULL; } @@ -434,9 +430,7 @@ void access_list_add_hook (void (*func) (struct access_list *access)) { access_master_ipv4.add_hook = func; -#ifdef HAVE_IPV6 access_master_ipv6.add_hook = func; -#endif /* HAVE_IPV6 */ } /* Delete hook function. */ @@ -444,9 +438,7 @@ void access_list_delete_hook (void (*func) (struct access_list *access)) { access_master_ipv4.delete_hook = func; -#ifdef HAVE_IPV6 access_master_ipv6.delete_hook = func; -#endif /* HAVE_IPV6 */ } /* Add new filter to the end of specified access_list. */ @@ -704,7 +696,7 @@ filter_set_cisco (struct vty *vty, const char *name_str, const char *type_str, /* Standard access-list */ DEFUN (access_list_standard, access_list_standard_cmd, - "access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D A.B.C.D", + "access-list <(1-99)|(1300-1999)> <deny|permit> A.B.C.D A.B.C.D", "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" @@ -713,13 +705,17 @@ DEFUN (access_list_standard, "Address to match\n" "Wildcard bits\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], argv[3], + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 3; + int idx_ipv4_2 = 4; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg, NULL, NULL, 0, 1); } DEFUN (access_list_standard_nomask, access_list_standard_nomask_cmd, - "access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D", + "access-list <(1-99)|(1300-1999)> <deny|permit> A.B.C.D", "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" @@ -727,13 +723,16 @@ DEFUN (access_list_standard_nomask, "Specify packets to forward\n" "Address to match\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 3; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, "0.0.0.0", NULL, NULL, 0, 1); } DEFUN (access_list_standard_host, access_list_standard_host_cmd, - "access-list (<1-99>|<1300-1999>) (deny|permit) host A.B.C.D", + "access-list <(1-99)|(1300-1999)> <deny|permit> host A.B.C.D", "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" @@ -742,13 +741,16 @@ DEFUN (access_list_standard_host, "A single host address\n" "Address to match\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 4; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, "0.0.0.0", NULL, NULL, 0, 1); } DEFUN (access_list_standard_any, access_list_standard_any_cmd, - "access-list (<1-99>|<1300-1999>) (deny|permit) any", + "access-list <(1-99)|(1300-1999)> <deny|permit> any", "Add an access list entry\n" "IP standard access list\n" "IP standard access list (expanded range)\n" @@ -756,13 +758,15 @@ DEFUN (access_list_standard_any, "Specify packets to forward\n" "Any source host\n") { - return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + int idx_acl = 1; + int idx_permit_deny = 2; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", "255.255.255.255", NULL, NULL, 0, 1); } DEFUN (no_access_list_standard, no_access_list_standard_cmd, - "no access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D A.B.C.D", + "no access-list <(1-99)|(1300-1999)> <deny|permit> A.B.C.D A.B.C.D", NO_STR "Add an access list entry\n" "IP standard access list\n" @@ -772,13 +776,17 @@ DEFUN (no_access_list_standard, "Address to match\n" "Wildcard bits\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], argv[3], + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 4; + int idx_ipv4_2 = 5; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg, NULL, NULL, 0, 0); } DEFUN (no_access_list_standard_nomask, no_access_list_standard_nomask_cmd, - "no access-list (<1-99>|<1300-1999>) (deny|permit) A.B.C.D", + "no access-list <(1-99)|(1300-1999)> <deny|permit> A.B.C.D", NO_STR "Add an access list entry\n" "IP standard access list\n" @@ -787,13 +795,16 @@ DEFUN (no_access_list_standard_nomask, "Specify packets to forward\n" "Address to match\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 4; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, "0.0.0.0", NULL, NULL, 0, 0); } DEFUN (no_access_list_standard_host, no_access_list_standard_host_cmd, - "no access-list (<1-99>|<1300-1999>) (deny|permit) host A.B.C.D", + "no access-list <(1-99)|(1300-1999)> <deny|permit> host A.B.C.D", NO_STR "Add an access list entry\n" "IP standard access list\n" @@ -803,13 +814,16 @@ DEFUN (no_access_list_standard_host, "A single host address\n" "Address to match\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], "0.0.0.0", + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 5; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, "0.0.0.0", NULL, NULL, 0, 0); } DEFUN (no_access_list_standard_any, no_access_list_standard_any_cmd, - "no access-list (<1-99>|<1300-1999>) (deny|permit) any", + "no access-list <(1-99)|(1300-1999)> <deny|permit> any", NO_STR "Add an access list entry\n" "IP standard access list\n" @@ -818,14 +832,16 @@ DEFUN (no_access_list_standard_any, "Specify packets to forward\n" "Any source host\n") { - return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + int idx_acl = 2; + int idx_permit_deny = 3; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", "255.255.255.255", NULL, NULL, 0, 0); } /* Extended access-list */ DEFUN (access_list_extended, access_list_extended_cmd, - "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + "access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" @@ -837,13 +853,19 @@ DEFUN (access_list_extended, "Destination address\n" "Destination Wildcard bits\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], - argv[3], argv[4], argv[5], 1 ,1); + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 4; + int idx_ipv4_2 = 5; + int idx_ipv4_3 = 6; + int idx_ipv4_4 = 7; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, argv[idx_ipv4_4]->arg, 1 ,1); } DEFUN (access_list_extended_mask_any, access_list_extended_mask_any_cmd, - "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D any", + "access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D any", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" @@ -854,14 +876,18 @@ DEFUN (access_list_extended_mask_any, "Source wildcard bits\n" "Any destination host\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], - argv[3], "0.0.0.0", + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 4; + int idx_ipv4_2 = 5; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + argv[idx_ipv4_2]->arg, "0.0.0.0", "255.255.255.255", 1, 1); } DEFUN (access_list_extended_any_mask, access_list_extended_any_mask_cmd, - "access-list (<100-199>|<2000-2699>) (deny|permit) ip any A.B.C.D A.B.C.D", + "access-list <(100-199)|(2000-2699)> <deny|permit> ip any A.B.C.D A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" @@ -872,14 +898,18 @@ DEFUN (access_list_extended_any_mask, "Destination address\n" "Destination Wildcard bits\n") { - return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", - "255.255.255.255", argv[2], - argv[3], 1, 1); + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 5; + int idx_ipv4_2 = 6; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", + "255.255.255.255", argv[idx_ipv4]->arg, + argv[idx_ipv4_2]->arg, 1, 1); } DEFUN (access_list_extended_any_any, access_list_extended_any_any_cmd, - "access-list (<100-199>|<2000-2699>) (deny|permit) ip any any", + "access-list <(100-199)|(2000-2699)> <deny|permit> ip any any", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" @@ -889,14 +919,16 @@ DEFUN (access_list_extended_any_any, "Any source host\n" "Any destination host\n") { - return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + int idx_acl = 1; + int idx_permit_deny = 2; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", "255.255.255.255", "0.0.0.0", "255.255.255.255", 1, 1); } DEFUN (access_list_extended_mask_host, access_list_extended_mask_host_cmd, - "access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D host A.B.C.D", + "access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D host A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" @@ -908,14 +940,19 @@ DEFUN (access_list_extended_mask_host, "A single destination host\n" "Destination address\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], - argv[3], argv[4], + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 4; + int idx_ipv4_2 = 5; + int idx_ipv4_3 = 7; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, "0.0.0.0", 1, 1); } DEFUN (access_list_extended_host_mask, access_list_extended_host_mask_cmd, - "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D A.B.C.D A.B.C.D", + "access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D A.B.C.D A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" @@ -927,14 +964,19 @@ DEFUN (access_list_extended_host_mask, "Destination address\n" "Destination Wildcard bits\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], - "0.0.0.0", argv[3], - argv[4], 1, 1); + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 5; + int idx_ipv4_2 = 6; + int idx_ipv4_3 = 7; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + "0.0.0.0", argv[idx_ipv4_2]->arg, + argv[idx_ipv4_3]->arg, 1, 1); } DEFUN (access_list_extended_host_host, access_list_extended_host_host_cmd, - "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D host A.B.C.D", + "access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D host A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" @@ -946,14 +988,18 @@ DEFUN (access_list_extended_host_host, "A single destination host\n" "Destination address\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], - "0.0.0.0", argv[3], + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 5; + int idx_ipv4_2 = 7; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + "0.0.0.0", argv[idx_ipv4_2]->arg, "0.0.0.0", 1, 1); } DEFUN (access_list_extended_any_host, access_list_extended_any_host_cmd, - "access-list (<100-199>|<2000-2699>) (deny|permit) ip any host A.B.C.D", + "access-list <(100-199)|(2000-2699)> <deny|permit> ip any host A.B.C.D", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" @@ -964,14 +1010,17 @@ DEFUN (access_list_extended_any_host, "A single destination host\n" "Destination address\n") { - return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", - "255.255.255.255", argv[2], + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 6; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", + "255.255.255.255", argv[idx_ipv4]->arg, "0.0.0.0", 1, 1); } DEFUN (access_list_extended_host_any, access_list_extended_host_any_cmd, - "access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D any", + "access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D any", "Add an access list entry\n" "IP extended access list\n" "IP extended access list (expanded range)\n" @@ -982,14 +1031,17 @@ DEFUN (access_list_extended_host_any, "Source address\n" "Any destination host\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], + int idx_acl = 1; + int idx_permit_deny = 2; + int idx_ipv4 = 5; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, "0.0.0.0", "0.0.0.0", "255.255.255.255", 1, 1); } DEFUN (no_access_list_extended, no_access_list_extended_cmd, - "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", + "no access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D A.B.C.D A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" @@ -1002,13 +1054,19 @@ DEFUN (no_access_list_extended, "Destination address\n" "Destination Wildcard bits\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], - argv[3], argv[4], argv[5], 1, 0); + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 5; + int idx_ipv4_2 = 6; + int idx_ipv4_3 = 7; + int idx_ipv4_4 = 8; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, argv[idx_ipv4_4]->arg, 1, 0); } DEFUN (no_access_list_extended_mask_any, no_access_list_extended_mask_any_cmd, - "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D any", + "no access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D any", NO_STR "Add an access list entry\n" "IP extended access list\n" @@ -1020,14 +1078,18 @@ DEFUN (no_access_list_extended_mask_any, "Source wildcard bits\n" "Any destination host\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], - argv[3], "0.0.0.0", + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 5; + int idx_ipv4_2 = 6; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + argv[idx_ipv4_2]->arg, "0.0.0.0", "255.255.255.255", 1, 0); } DEFUN (no_access_list_extended_any_mask, no_access_list_extended_any_mask_cmd, - "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any A.B.C.D A.B.C.D", + "no access-list <(100-199)|(2000-2699)> <deny|permit> ip any A.B.C.D A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" @@ -1039,14 +1101,18 @@ DEFUN (no_access_list_extended_any_mask, "Destination address\n" "Destination Wildcard bits\n") { - return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", - "255.255.255.255", argv[2], - argv[3], 1, 0); + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 6; + int idx_ipv4_2 = 7; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", + "255.255.255.255", argv[idx_ipv4]->arg, + argv[idx_ipv4_2]->arg, 1, 0); } DEFUN (no_access_list_extended_any_any, no_access_list_extended_any_any_cmd, - "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any any", + "no access-list <(100-199)|(2000-2699)> <deny|permit> ip any any", NO_STR "Add an access list entry\n" "IP extended access list\n" @@ -1057,14 +1123,16 @@ DEFUN (no_access_list_extended_any_any, "Any source host\n" "Any destination host\n") { - return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", + int idx_acl = 2; + int idx_permit_deny = 3; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", "255.255.255.255", "0.0.0.0", "255.255.255.255", 1, 0); } DEFUN (no_access_list_extended_mask_host, no_access_list_extended_mask_host_cmd, - "no access-list (<100-199>|<2000-2699>) (deny|permit) ip A.B.C.D A.B.C.D host A.B.C.D", + "no access-list <(100-199)|(2000-2699)> <deny|permit> ip A.B.C.D A.B.C.D host A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" @@ -1077,14 +1145,19 @@ DEFUN (no_access_list_extended_mask_host, "A single destination host\n" "Destination address\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], - argv[3], argv[4], + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 5; + int idx_ipv4_2 = 6; + int idx_ipv4_3 = 8; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + argv[idx_ipv4_2]->arg, argv[idx_ipv4_3]->arg, "0.0.0.0", 1, 0); } DEFUN (no_access_list_extended_host_mask, no_access_list_extended_host_mask_cmd, - "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D A.B.C.D A.B.C.D", + "no access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D A.B.C.D A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" @@ -1097,14 +1170,19 @@ DEFUN (no_access_list_extended_host_mask, "Destination address\n" "Destination Wildcard bits\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], - "0.0.0.0", argv[3], - argv[4], 1, 0); + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 6; + int idx_ipv4_2 = 7; + int idx_ipv4_3 = 8; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + "0.0.0.0", argv[idx_ipv4_2]->arg, + argv[idx_ipv4_3]->arg, 1, 0); } DEFUN (no_access_list_extended_host_host, no_access_list_extended_host_host_cmd, - "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D host A.B.C.D", + "no access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D host A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" @@ -1117,14 +1195,18 @@ DEFUN (no_access_list_extended_host_host, "A single destination host\n" "Destination address\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], - "0.0.0.0", argv[3], + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 6; + int idx_ipv4_2 = 8; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, + "0.0.0.0", argv[idx_ipv4_2]->arg, "0.0.0.0", 1, 0); } DEFUN (no_access_list_extended_any_host, no_access_list_extended_any_host_cmd, - "no access-list (<100-199>|<2000-2699>) (deny|permit) ip any host A.B.C.D", + "no access-list <(100-199)|(2000-2699)> <deny|permit> ip any host A.B.C.D", NO_STR "Add an access list entry\n" "IP extended access list\n" @@ -1136,14 +1218,17 @@ DEFUN (no_access_list_extended_any_host, "A single destination host\n" "Destination address\n") { - return filter_set_cisco (vty, argv[0], argv[1], "0.0.0.0", - "255.255.255.255", argv[2], + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 7; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, "0.0.0.0", + "255.255.255.255", argv[idx_ipv4]->arg, "0.0.0.0", 1, 0); } DEFUN (no_access_list_extended_host_any, no_access_list_extended_host_any_cmd, - "no access-list (<100-199>|<2000-2699>) (deny|permit) ip host A.B.C.D any", + "no access-list <(100-199)|(2000-2699)> <deny|permit> ip host A.B.C.D any", NO_STR "Add an access list entry\n" "IP extended access list\n" @@ -1155,7 +1240,10 @@ DEFUN (no_access_list_extended_host_any, "Source address\n" "Any destination host\n") { - return filter_set_cisco (vty, argv[0], argv[1], argv[2], + int idx_acl = 2; + int idx_permit_deny = 3; + int idx_ipv4 = 6; + return filter_set_cisco (vty, argv[idx_acl]->arg, argv[idx_permit_deny]->arg, argv[idx_ipv4]->arg, "0.0.0.0", "0.0.0.0", "255.255.255.255", 1, 0); } @@ -1171,6 +1259,14 @@ filter_set_zebra (struct vty *vty, const char *name_str, const char *type_str, struct access_list *access; struct prefix p; + if (strlen(name_str) > ACL_NAMSIZ) + { + vty_out (vty, "%% ACL name %s is invalid: length exceeds " + "%d characters%s", + name_str, ACL_NAMSIZ, VTY_NEWLINE); + return CMD_WARNING; + } + /* Check of filter type. */ if (strncmp (type_str, "p", 1) == 0) type = FILTER_PERMIT; @@ -1193,7 +1289,6 @@ filter_set_zebra (struct vty *vty, const char *name_str, const char *type_str, return CMD_WARNING; } } -#ifdef HAVE_IPV6 else if (afi == AFI_IP6) { ret = str2prefix_ipv6 (prefix_str, (struct prefix_ipv6 *) &p); @@ -1204,7 +1299,6 @@ filter_set_zebra (struct vty *vty, const char *name_str, const char *type_str, return CMD_WARNING; } } -#endif /* HAVE_IPV6 */ else return CMD_WARNING; @@ -1241,22 +1335,9 @@ filter_set_zebra (struct vty *vty, const char *name_str, const char *type_str, return CMD_SUCCESS; } -/* Zebra access-list */ -DEFUN (access_list, - access_list_cmd, - "access-list WORD (deny|permit) A.B.C.D/M", - "Add an access list entry\n" - "IP zebra access-list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 10.0.0.0/8\n") -{ - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 0, 1); -} - DEFUN (access_list_exact, access_list_exact_cmd, - "access-list WORD (deny|permit) A.B.C.D/M exact-match", + "access-list WORD <deny|permit> A.B.C.D/M [exact-match]", "Add an access list entry\n" "IP zebra access-list name\n" "Specify packets to reject\n" @@ -1264,37 +1345,37 @@ DEFUN (access_list_exact, "Prefix to match. e.g. 10.0.0.0/8\n" "Exact match of the prefixes\n") { - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 1, 1); + int idx; + int exact = 0; + int idx_word = 1; + int idx_permit_deny = 2; + int idx_ipv4_prefixlen = 3; + idx = idx_ipv4_prefixlen; + + if (argv_find (argv, argc, "exact-match", &idx)) + exact = 1; + + return filter_set_zebra (vty, argv[idx_word]->arg, argv[idx_permit_deny]->arg, + AFI_IP, argv[idx_ipv4_prefixlen]->arg, exact, 1); } DEFUN (access_list_any, access_list_any_cmd, - "access-list WORD (deny|permit) any", - "Add an access list entry\n" - "IP zebra access-list name\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 10.0.0.0/8\n") -{ - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, "0.0.0.0/0", 0, 1); -} - -DEFUN (no_access_list, - no_access_list_cmd, - "no access-list WORD (deny|permit) A.B.C.D/M", - NO_STR + "access-list WORD <deny|permit> any", "Add an access list entry\n" "IP zebra access-list name\n" "Specify packets to reject\n" "Specify packets to forward\n" "Prefix to match. e.g. 10.0.0.0/8\n") { - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 0, 0); + int idx_word = 1; + int idx_permit_deny = 2; + return filter_set_zebra (vty, argv[idx_word]->arg, argv[idx_permit_deny]->arg, AFI_IP, "0.0.0.0/0", 0, 1); } DEFUN (no_access_list_exact, no_access_list_exact_cmd, - "no access-list WORD (deny|permit) A.B.C.D/M exact-match", + "no access-list WORD <deny|permit> A.B.C.D/M [exact-match]", NO_STR "Add an access list entry\n" "IP zebra access-list name\n" @@ -1303,12 +1384,22 @@ DEFUN (no_access_list_exact, "Prefix to match. e.g. 10.0.0.0/8\n" "Exact match of the prefixes\n") { - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, argv[2], 1, 0); + int idx; + int exact = 0; + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv4_prefixlen = 4; + idx = idx_ipv4_prefixlen; + + if (argv_find (argv, argc, "exact-match", &idx)) + exact = 1; + + return filter_set_zebra (vty, argv[idx_word]->arg, argv[idx_permit_deny]->arg, AFI_IP, argv[idx_ipv4_prefixlen]->arg, exact, 0); } DEFUN (no_access_list_any, no_access_list_any_cmd, - "no access-list WORD (deny|permit) any", + "no access-list WORD <deny|permit> any", NO_STR "Add an access list entry\n" "IP zebra access-list name\n" @@ -1316,12 +1407,14 @@ DEFUN (no_access_list_any, "Specify packets to forward\n" "Prefix to match. e.g. 10.0.0.0/8\n") { - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP, "0.0.0.0/0", 0, 0); + int idx_word = 2; + int idx_permit_deny = 3; + return filter_set_zebra (vty, argv[idx_word]->arg, argv[idx_permit_deny]->arg, AFI_IP, "0.0.0.0/0", 0, 0); } DEFUN (no_access_list_all, no_access_list_all_cmd, - "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD)", + "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD>", NO_STR "Add an access list entry\n" "IP standard access list\n" @@ -1330,14 +1423,15 @@ DEFUN (no_access_list_all, "IP extended access list (expanded range)\n" "IP zebra access-list name\n") { + int idx_acl = 2; struct access_list *access; struct access_master *master; /* Looking up access_list. */ - access = access_list_lookup (AFI_IP, argv[0]); + access = access_list_lookup (AFI_IP, argv[idx_acl]->arg); if (access == NULL) { - vty_out (vty, "%% access-list %s doesn't exist%s", argv[0], + vty_out (vty, "%% access-list %s doesn't exist%s", argv[idx_acl]->arg, VTY_NEWLINE); return CMD_WARNING; } @@ -1357,7 +1451,7 @@ DEFUN (no_access_list_all, DEFUN (access_list_remark, access_list_remark_cmd, - "access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark .LINE", + "access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark LINE...", "Add an access list entry\n" "IP standard access list\n" "IP extended access list\n" @@ -1367,23 +1461,25 @@ DEFUN (access_list_remark, "Access list entry comment\n" "Comment up to 100 characters\n") { + int idx_acl = 1; + int idx_remark = 3; struct access_list *access; - access = access_list_get (AFI_IP, argv[0]); + access = access_list_get (AFI_IP, argv[idx_acl]->arg); if (access->remark) { XFREE (MTYPE_TMP, access->remark); access->remark = NULL; } - access->remark = argv_concat(argv, argc, 1); + access->remark = argv_concat(argv, argc, idx_remark); return CMD_SUCCESS; } DEFUN (no_access_list_remark, no_access_list_remark_cmd, - "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark", + "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark", NO_STR "Add an access list entry\n" "IP standard access list\n" @@ -1393,12 +1489,14 @@ DEFUN (no_access_list_remark, "IP zebra access-list\n" "Access list entry comment\n") { - return vty_access_list_remark_unset (vty, AFI_IP, argv[0]); + int idx_acl = 2; + return vty_access_list_remark_unset (vty, AFI_IP, argv[idx_acl]->arg); } - -ALIAS (no_access_list_remark, - no_access_list_remark_arg_cmd, - "no access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD) remark .LINE", + +/* ALIAS_FIXME */ +DEFUN (no_access_list_remark_comment, + no_access_list_remark_comment_cmd, + "no access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD> remark LINE...", NO_STR "Add an access list entry\n" "IP standard access list\n" @@ -1408,38 +1506,38 @@ ALIAS (no_access_list_remark, "IP zebra access-list\n" "Access list entry comment\n" "Comment up to 100 characters\n") - -#ifdef HAVE_IPV6 -DEFUN (ipv6_access_list, - ipv6_access_list_cmd, - "ipv6 access-list WORD (deny|permit) X:X::X:X/M", - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 3ffe:506::/32\n") { - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 0, 1); + return no_access_list_remark (self, vty, argc, argv); } DEFUN (ipv6_access_list_exact, ipv6_access_list_exact_cmd, - "ipv6 access-list WORD (deny|permit) X:X::X:X/M exact-match", + "ipv6 access-list WORD <deny|permit> X:X::X:X/M [exact-match]", IPV6_STR "Add an access list entry\n" "IPv6 zebra access-list\n" "Specify packets to reject\n" "Specify packets to forward\n" - "Prefix to match. e.g. 3ffe:506::/32\n" + "IPv6 prefix\n" "Exact match of the prefixes\n") { - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 1, 1); + int idx; + int exact = 0; + int idx_word = 2; + int idx_allow = 3; + int idx_addr = 4; + idx = idx_addr; + + if (argv_find (argv, argc, "exact-match", &idx)) + exact = 1; + + return filter_set_zebra (vty, argv[idx_word]->arg, argv[idx_allow]->text, + AFI_IP6, argv[idx_addr]->arg, exact, 1); } DEFUN (ipv6_access_list_any, ipv6_access_list_any_cmd, - "ipv6 access-list WORD (deny|permit) any", + "ipv6 access-list WORD <deny|permit> any", IPV6_STR "Add an access list entry\n" "IPv6 zebra access-list\n" @@ -1447,26 +1545,14 @@ DEFUN (ipv6_access_list_any, "Specify packets to forward\n" "Any prefixi to match\n") { - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, "::/0", 0, 1); -} - -DEFUN (no_ipv6_access_list, - no_ipv6_access_list_cmd, - "no ipv6 access-list WORD (deny|permit) X:X::X:X/M", - NO_STR - IPV6_STR - "Add an access list entry\n" - "IPv6 zebra access-list\n" - "Specify packets to reject\n" - "Specify packets to forward\n" - "Prefix to match. e.g. 3ffe:506::/32\n") -{ - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 0, 0); + int idx_word = 2; + int idx_permit_deny = 3; + return filter_set_zebra (vty, argv[idx_word]->arg, argv[idx_permit_deny]->arg, AFI_IP6, "::/0", 0, 1); } DEFUN (no_ipv6_access_list_exact, no_ipv6_access_list_exact_cmd, - "no ipv6 access-list WORD (deny|permit) X:X::X:X/M exact-match", + "no ipv6 access-list WORD <deny|permit> X:X::X:X/M [exact-match]", NO_STR IPV6_STR "Add an access list entry\n" @@ -1476,12 +1562,23 @@ DEFUN (no_ipv6_access_list_exact, "Prefix to match. e.g. 3ffe:506::/32\n" "Exact match of the prefixes\n") { - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, argv[2], 1, 0); + int idx; + int exact = 0; + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv6_prefixlen = 5; + idx = idx_ipv6_prefixlen; + + if (argv_find (argv, argc, "exact-match", &idx)) + exact = 1; + + return filter_set_zebra (vty, argv[idx_word]->arg, argv[idx_permit_deny]->arg, + AFI_IP6, argv[idx_ipv6_prefixlen]->arg, exact, 0); } DEFUN (no_ipv6_access_list_any, no_ipv6_access_list_any_cmd, - "no ipv6 access-list WORD (deny|permit) any", + "no ipv6 access-list WORD <deny|permit> any", NO_STR IPV6_STR "Add an access list entry\n" @@ -1490,7 +1587,9 @@ DEFUN (no_ipv6_access_list_any, "Specify packets to forward\n" "Any prefixi to match\n") { - return filter_set_zebra (vty, argv[0], argv[1], AFI_IP6, "::/0", 0, 0); + int idx_word = 3; + int idx_permit_deny = 4; + return filter_set_zebra (vty, argv[idx_word]->arg, argv[idx_permit_deny]->arg, AFI_IP6, "::/0", 0, 0); } @@ -1502,14 +1601,15 @@ DEFUN (no_ipv6_access_list_all, "Add an access list entry\n" "IPv6 zebra access-list\n") { + int idx_word = 3; struct access_list *access; struct access_master *master; /* Looking up access_list. */ - access = access_list_lookup (AFI_IP6, argv[0]); + access = access_list_lookup (AFI_IP6, argv[idx_word]->arg); if (access == NULL) { - vty_out (vty, "%% access-list %s doesn't exist%s", argv[0], + vty_out (vty, "%% access-list %s doesn't exist%s", argv[idx_word]->arg, VTY_NEWLINE); return CMD_WARNING; } @@ -1529,23 +1629,25 @@ DEFUN (no_ipv6_access_list_all, DEFUN (ipv6_access_list_remark, ipv6_access_list_remark_cmd, - "ipv6 access-list WORD remark .LINE", + "ipv6 access-list WORD remark LINE...", IPV6_STR "Add an access list entry\n" "IPv6 zebra access-list\n" "Access list entry comment\n" "Comment up to 100 characters\n") { + int idx_word = 2; + int idx_line = 4; struct access_list *access; - access = access_list_get (AFI_IP6, argv[0]); + access = access_list_get (AFI_IP6, argv[idx_word]->arg); if (access->remark) { XFREE (MTYPE_TMP, access->remark); access->remark = NULL; } - access->remark = argv_concat(argv, argc, 1); + access->remark = argv_concat(argv, argc, idx_line); return CMD_SUCCESS; } @@ -1559,19 +1661,23 @@ DEFUN (no_ipv6_access_list_remark, "IPv6 zebra access-list\n" "Access list entry comment\n") { - return vty_access_list_remark_unset (vty, AFI_IP6, argv[0]); + int idx_word = 3; + return vty_access_list_remark_unset (vty, AFI_IP6, argv[idx_word]->arg); } - -ALIAS (no_ipv6_access_list_remark, - no_ipv6_access_list_remark_arg_cmd, - "no ipv6 access-list WORD remark .LINE", + +/* ALIAS_FIXME */ +DEFUN (no_ipv6_access_list_remark_comment, + no_ipv6_access_list_remark_comment_cmd, + "no ipv6 access-list WORD remark LINE...", NO_STR IPV6_STR "Add an access list entry\n" "IPv6 zebra access-list\n" "Access list entry comment\n" "Comment up to 100 characters\n") -#endif /* HAVE_IPV6 */ +{ + return no_ipv6_access_list_remark (self, vty, argc, argv); +} void config_write_access_zebra (struct vty *, struct filter *); void config_write_access_cisco (struct vty *, struct filter *); @@ -1591,9 +1697,7 @@ filter_show (struct vty *vty, const char *name, afi_t afi) return 0; /* Print the name of the protocol */ - if (zlog_default) - vty_out (vty, "%s:%s", - zlog_proto_names[zlog_default->protocol], VTY_NEWLINE); + vty_out(vty, "%s:%s", zlog_protoname(), VTY_NEWLINE); for (access = master->num.head; access; access = access->next) { @@ -1695,7 +1799,7 @@ DEFUN (show_ip_access_list, DEFUN (show_ip_access_list_name, show_ip_access_list_name_cmd, - "show ip access-list (<1-99>|<100-199>|<1300-1999>|<2000-2699>|WORD)", + "show ip access-list <(1-99)|(100-199)|(1300-1999)|(2000-2699)|WORD>", SHOW_STR IP_STR "List IP access lists\n" @@ -1705,10 +1809,10 @@ DEFUN (show_ip_access_list_name, "IP extended access list (expanded range)\n" "IP zebra access-list\n") { - return filter_show (vty, argv[0], AFI_IP); + int idx_acl = 3; + return filter_show (vty, argv[idx_acl]->arg, AFI_IP); } -#ifdef HAVE_IPV6 DEFUN (show_ipv6_access_list, show_ipv6_access_list_cmd, "show ipv6 access-list", @@ -1727,9 +1831,9 @@ DEFUN (show_ipv6_access_list_name, "List IPv6 access lists\n" "IPv6 zebra access-list\n") { - return filter_show (vty, argv[0], AFI_IP6); + int idx_word = 3; + return filter_show (vty, argv[idx_word]->arg, AFI_IP6); } -#endif /* HAVE_IPV6 */ void config_write_access_cisco (struct vty *vty, struct filter *mfilter) @@ -1918,10 +2022,8 @@ access_list_init_ipv4 (void) install_element (ENABLE_NODE, &show_ip_access_list_name_cmd); /* Zebra access-list */ - install_element (CONFIG_NODE, &access_list_cmd); install_element (CONFIG_NODE, &access_list_exact_cmd); install_element (CONFIG_NODE, &access_list_any_cmd); - install_element (CONFIG_NODE, &no_access_list_cmd); install_element (CONFIG_NODE, &no_access_list_exact_cmd); install_element (CONFIG_NODE, &no_access_list_any_cmd); @@ -1958,10 +2060,9 @@ access_list_init_ipv4 (void) install_element (CONFIG_NODE, &access_list_remark_cmd); install_element (CONFIG_NODE, &no_access_list_all_cmd); install_element (CONFIG_NODE, &no_access_list_remark_cmd); - install_element (CONFIG_NODE, &no_access_list_remark_arg_cmd); + install_element (CONFIG_NODE, &no_access_list_remark_comment_cmd); } -#ifdef HAVE_IPV6 static struct cmd_node access_ipv6_node = { ACCESS_IPV6_NODE, @@ -2012,34 +2113,27 @@ access_list_init_ipv6 (void) install_element (ENABLE_NODE, &show_ipv6_access_list_cmd); install_element (ENABLE_NODE, &show_ipv6_access_list_name_cmd); - install_element (CONFIG_NODE, &ipv6_access_list_cmd); install_element (CONFIG_NODE, &ipv6_access_list_exact_cmd); install_element (CONFIG_NODE, &ipv6_access_list_any_cmd); install_element (CONFIG_NODE, &no_ipv6_access_list_exact_cmd); - install_element (CONFIG_NODE, &no_ipv6_access_list_cmd); install_element (CONFIG_NODE, &no_ipv6_access_list_any_cmd); install_element (CONFIG_NODE, &no_ipv6_access_list_all_cmd); install_element (CONFIG_NODE, &ipv6_access_list_remark_cmd); install_element (CONFIG_NODE, &no_ipv6_access_list_remark_cmd); - install_element (CONFIG_NODE, &no_ipv6_access_list_remark_arg_cmd); + install_element (CONFIG_NODE, &no_ipv6_access_list_remark_comment_cmd); } -#endif /* HAVE_IPV6 */ void access_list_init () { access_list_init_ipv4 (); -#ifdef HAVE_IPV6 access_list_init_ipv6(); -#endif /* HAVE_IPV6 */ } void access_list_reset () { access_list_reset_ipv4 (); -#ifdef HAVE_IPV6 access_list_reset_ipv6(); -#endif /* HAVE_IPV6 */ } diff --git a/lib/filter.h b/lib/filter.h index e6ccd33b3a..6b5ccb52ec 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -25,6 +25,9 @@ #include "if.h" +/* Maximum ACL name length */ +#define ACL_NAMSIZ 128 + /* Filter direction. */ #define FILTER_IN 0 #define FILTER_OUT 1 diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c new file mode 100644 index 0000000000..e3a7c979fc --- /dev/null +++ b/lib/grammar_sandbox.c @@ -0,0 +1,661 @@ +/* + * Testing shim and API examples for the new CLI backend. + * + * This unit defines a number of commands in the old engine that can + * be used to test and interact with the new engine. + * -- + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "command.h" +#include "memory_vty.h" +#include "graph.h" +#include "linklist.h" +#include "command_match.h" + +#define GRAMMAR_STR "CLI grammar sandbox\n" + +DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc") + +#define MAXDEPTH 64 + +/** headers **/ +void +grammar_sandbox_init (void); +void +pretty_print_graph (struct vty *vty, struct graph_node *, int, int, struct graph_node **, size_t); +static void +pretty_print_dot (FILE *ofd, unsigned opts, struct graph_node *start, + struct graph_node **stack, size_t stackpos, + struct graph_node **visited, size_t *visitpos); +void +init_cmdgraph (struct vty *, struct graph **); + +/** shim interface commands **/ +struct graph *nodegraph = NULL, *nodegraph_free = NULL; + +DEFUN (grammar_test, + grammar_test_cmd, + "grammar parse LINE...", + GRAMMAR_STR + "parse a command\n" + "command to pass to new parser\n") +{ + int idx_command = 2; + // make a string from tokenized command line + char *command = argv_concat (argv, argc, idx_command); + + // create cmd_element for parser + struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element)); + cmd->string = command; + cmd->doc = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n"; + cmd->func = NULL; + + // parse the command and install it into the command graph + struct graph *graph = graph_new(); + struct cmd_token *token = new_cmd_token (START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + graph_new_node (graph, token, (void (*)(void *)) &del_cmd_token); + + command_parse_format (graph, cmd); + cmd_merge_graphs (nodegraph, graph, +1); + + return CMD_SUCCESS; +} + +DEFUN (grammar_test_complete, + grammar_test_complete_cmd, + "grammar complete COMMAND...", + GRAMMAR_STR + "attempt to complete input on DFA\n" + "command to complete\n") +{ + int idx_command = 2; + char *cmdstr = argv_concat (argv, argc, idx_command); + if (!cmdstr) + return CMD_SUCCESS; + + vector command = cmd_make_strvec (cmdstr); + if (!command) + { + XFREE (MTYPE_TMP, cmdstr); + return CMD_SUCCESS; + } + + // generate completions of user input + struct list *completions; + enum matcher_rv result = command_complete (nodegraph, command, &completions); + + // print completions or relevant error message + if (!MATCHER_ERROR(result)) + { + vector comps = completions_to_vec (completions); + struct cmd_token *tkn; + + // calculate length of longest tkn->text in completions + unsigned int width = 0, i = 0; + for (i = 0; i < vector_active (comps); i++) { + tkn = vector_slot (comps, i); + unsigned int len = strlen (tkn->text); + width = len > width ? len : width; + } + + // print completions + for (i = 0; i < vector_active (comps); i++) { + tkn = vector_slot (comps, i); + vty_out (vty, " %-*s %s%s", width, tkn->text, tkn->desc, VTY_NEWLINE); + } + + for (i = 0; i < vector_active (comps); i++) + del_cmd_token ((struct cmd_token *) vector_slot (comps, i)); + vector_free (comps); + } + else + vty_out (vty, "%% No match%s", VTY_NEWLINE); + + // free resources + list_delete (completions); + cmd_free_strvec (command); + XFREE (MTYPE_TMP, cmdstr); + + 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\n") +{ + int idx_command = 2; + if (argv[2]->arg[0] == '#') + return CMD_SUCCESS; + + char *cmdstr = argv_concat(argv, argc, idx_command); + if (!cmdstr) + return CMD_SUCCESS; + vector command = cmd_make_strvec (cmdstr); + if (!command) + { + XFREE (MTYPE_TMP, cmdstr); + return CMD_SUCCESS; + } + + struct list *argvv = NULL; + const struct cmd_element *element = NULL; + enum matcher_rv result = command_match (nodegraph, command, &argvv, &element); + + // print completions or relevant error message + if (element) + { + vty_out (vty, "Matched: %s%s", element->string, VTY_NEWLINE); + struct listnode *ln; + struct cmd_token *token; + for (ALL_LIST_ELEMENTS_RO(argvv,ln,token)) + vty_out (vty, "%s -- %s%s", token->text, token->arg, VTY_NEWLINE); + + vty_out (vty, "func: %p%s", element->func, VTY_NEWLINE); + + list_delete (argvv); + } + else { + assert(MATCHER_ERROR(result)); + switch (result) { + case MATCHER_NO_MATCH: + vty_out (vty, "%% Unknown command%s", VTY_NEWLINE); + break; + case MATCHER_INCOMPLETE: + vty_out (vty, "%% Incomplete command%s", VTY_NEWLINE); + break; + case MATCHER_AMBIGUOUS: + vty_out (vty, "%% Ambiguous command%s", VTY_NEWLINE); + break; + default: + vty_out (vty, "%% Unknown error%s", VTY_NEWLINE); + break; + } + } + + // free resources + cmd_free_strvec (command); + XFREE (MTYPE_TMP, cmdstr); + + return CMD_SUCCESS; +} + +/** + * Testing shim to test docstrings + */ +DEFUN (grammar_test_doc, + grammar_test_doc_cmd, + "grammar test docstring", + GRAMMAR_STR + "Test function for docstring\n" + "Command end\n") +{ + // create cmd_element with docstring + struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element)); + cmd->string = XSTRDUP (MTYPE_CMD_TOKENS, "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG"); + cmd->doc = XSTRDUP (MTYPE_CMD_TOKENS, + "Test stuff\n" + "docstring thing\n" + "first example\n" + "second example\n" + "follow\n" + "random range\n" + "end thingy\n" + "variable\n" + "optional variable\n" + "optional set\n" + "optional lol\n" + "vararg!\n"); + cmd->func = NULL; + + // parse element + command_parse_format (nodegraph, cmd); + + return CMD_SUCCESS; +} + +/** + * Debugging command to print command graph + */ +DEFUN (grammar_test_show, + grammar_test_show_cmd, + "grammar show [doc]", + GRAMMAR_STR + "print current accumulated DFA\n" + "include docstrings\n") +{ + struct graph_node *stack[MAXDEPTH]; + + if (!nodegraph) + vty_out(vty, "nodegraph uninitialized\r\n"); + else + pretty_print_graph (vty, vector_slot (nodegraph->nodes, 0), 0, argc >= 3, stack, 0); + return CMD_SUCCESS; +} + +DEFUN (grammar_test_dot, + grammar_test_dot_cmd, + "grammar dotfile OUTNAME", + GRAMMAR_STR + "print current graph for dot\n" + ".dot filename\n") +{ + struct graph_node *stack[MAXDEPTH]; + struct graph_node *visited[MAXDEPTH*MAXDEPTH]; + size_t vpos = 0; + + if (!nodegraph) { + vty_out(vty, "nodegraph uninitialized\r\n"); + return CMD_SUCCESS; + } + FILE *ofd = fopen(argv[2]->arg, "w"); + if (!ofd) { + vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno)); + return CMD_SUCCESS; + } + + fprintf(ofd, "digraph {\n graph [ rankdir = LR ];\n node [ fontname = \"Fira Mono\", fontsize = 9 ];\n\n"); + pretty_print_dot (ofd, 0, + vector_slot (nodegraph->nodes, 0), + stack, 0, visited, &vpos); + fprintf(ofd, "}\n"); + fclose(ofd); + return CMD_SUCCESS; +} + +struct cmd_permute_item +{ + char *cmd; + struct cmd_element *el; +}; + +static void +cmd_permute_free (void *arg) +{ + struct cmd_permute_item *i = arg; + XFREE (MTYPE_TMP, i->cmd); + XFREE (MTYPE_TMP, i); +} + +static int +cmd_permute_cmp (void *a, void *b) +{ + struct cmd_permute_item *aa = a, *bb = b; + return strcmp (aa->cmd, bb->cmd); +} + +static void +cmd_graph_permute (struct list *out, struct graph_node **stack, + size_t stackpos, char *cmd) +{ + struct graph_node *gn = stack[stackpos]; + struct cmd_token *tok = gn->data; + char *appendp = cmd + strlen(cmd); + size_t i, j; + + if (tok->type < SPECIAL_TKN) + { + sprintf (appendp, "%s ", tok->text); + appendp += strlen (appendp); + } + else if (tok->type == END_TKN) + { + struct cmd_permute_item *i = XMALLOC (MTYPE_TMP, sizeof (*i)); + i->el = ((struct graph_node *)vector_slot (gn->to, 0))->data; + i->cmd = XSTRDUP (MTYPE_TMP, cmd); + i->cmd[strlen(cmd) - 1] = '\0'; + listnode_add_sort (out, i); + return; + } + + if (++stackpos == MAXDEPTH) + return; + + for (i = 0; i < vector_active (gn->to); i++) + { + struct graph_node *gnext = vector_slot (gn->to, i); + for (j = 0; j < stackpos; j++) + if (stack[j] == gnext) + break; + if (j != stackpos) + continue; + + stack[stackpos] = gnext; + *appendp = '\0'; + cmd_graph_permute (out, stack, stackpos, cmd); + } +} + +static struct list * +cmd_graph_permutations (struct graph *graph) +{ + char accumulate[2048] = ""; + struct graph_node *stack[MAXDEPTH]; + + struct list *rv = list_new (); + rv->cmp = cmd_permute_cmp; + rv->del = cmd_permute_free; + stack[0] = vector_slot (graph->nodes, 0); + cmd_graph_permute (rv, stack, 0, accumulate); + return rv; +} + +extern vector cmdvec; + +DEFUN (grammar_findambig, + grammar_findambig_cmd, + "grammar find-ambiguous [{printall|nodescan}]", + GRAMMAR_STR + "Find ambiguous commands\n" + "Print all permutations\n" + "Scan all nodes\n") +{ + struct list *commands; + struct cmd_permute_item *prev = NULL, *cur = NULL; + struct listnode *ln; + int i, printall, scan, scannode = 0; + int ambig = 0; + + i = 0; + printall = argv_find (argv, argc, "printall", &i); + i = 0; + scan = argv_find (argv, argc, "nodescan", &i); + + if (scan && nodegraph_free) + { + graph_delete_graph (nodegraph_free); + nodegraph_free = NULL; + } + + if (!scan && !nodegraph) + { + vty_out(vty, "nodegraph uninitialized\r\n"); + return CMD_WARNING; + } + + do { + if (scan) + { + struct cmd_node *cnode = vector_slot (cmdvec, scannode++); + if (!cnode) + continue; + nodegraph = cnode->cmdgraph; + if (!nodegraph) + continue; + vty_out (vty, "scanning node %d%s", scannode - 1, VTY_NEWLINE); + } + + commands = cmd_graph_permutations (nodegraph); + prev = NULL; + for (ALL_LIST_ELEMENTS_RO (commands, ln, cur)) + { + int same = prev && !strcmp (prev->cmd, cur->cmd); + if (printall && !same) + vty_out (vty, "'%s'%s", cur->cmd, VTY_NEWLINE); + if (same) + { + vty_out (vty, "'%s' AMBIGUOUS:%s", cur->cmd, VTY_NEWLINE); + vty_out (vty, " %s%s '%s'%s", prev->el->name, VTY_NEWLINE, prev->el->string, VTY_NEWLINE); + vty_out (vty, " %s%s '%s'%s", cur->el->name, VTY_NEWLINE, cur->el->string, VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + ambig++; + } + prev = cur; + } + list_delete (commands); + + vty_out (vty, "%s", VTY_NEWLINE); + } while (scan && scannode < LINK_PARAMS_NODE); + + vty_out (vty, "%d ambiguous commands found.%s", ambig, VTY_NEWLINE); + + if (scan) + nodegraph = NULL; + return ambig == 0 ? CMD_SUCCESS : CMD_WARNING; +} + +DEFUN (grammar_init_graph, + grammar_init_graph_cmd, + "grammar init", + GRAMMAR_STR + "(re)initialize graph\n") +{ + if (nodegraph_free) + graph_delete_graph (nodegraph_free); + nodegraph_free = NULL; + + init_cmdgraph (vty, &nodegraph); + return CMD_SUCCESS; +} + +DEFUN (grammar_access, + grammar_access_cmd, + "grammar access (0-65535)", + GRAMMAR_STR + "access node graph\n" + "node number\n") +{ + if (nodegraph_free) + graph_delete_graph (nodegraph_free); + nodegraph_free = NULL; + + struct cmd_node *cnode; + + cnode = vector_slot (cmdvec, atoi (argv[2]->arg)); + if (!cnode) + { + vty_out (vty, "%% no such node%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out (vty, "node %d%s", (int)cnode->node, VTY_NEWLINE); + nodegraph = cnode->cmdgraph; + return CMD_SUCCESS; +} + +/* this is called in vtysh.c to set up the testing shim */ +void grammar_sandbox_init(void) { + init_cmdgraph (NULL, &nodegraph); + + // install all enable elements + install_element (ENABLE_NODE, &grammar_test_cmd); + install_element (ENABLE_NODE, &grammar_test_show_cmd); + install_element (ENABLE_NODE, &grammar_test_dot_cmd); + install_element (ENABLE_NODE, &grammar_test_match_cmd); + install_element (ENABLE_NODE, &grammar_test_complete_cmd); + install_element (ENABLE_NODE, &grammar_test_doc_cmd); + install_element (ENABLE_NODE, &grammar_findambig_cmd); + install_element (ENABLE_NODE, &grammar_init_graph_cmd); + install_element (ENABLE_NODE, &grammar_access_cmd); +} + +#define item(x) { x, #x } +struct message tokennames[] = { + item(WORD_TKN), // words + item(VARIABLE_TKN), // almost anything + item(RANGE_TKN), // integer range + item(IPV4_TKN), // IPV4 addresses + item(IPV4_PREFIX_TKN), // IPV4 network prefixes + item(IPV6_TKN), // IPV6 prefixes + item(IPV6_PREFIX_TKN), // IPV6 network prefixes + + /* plumbing types */ + item(FORK_TKN), + item(JOIN_TKN), + item(START_TKN), // first token in line + item(END_TKN), // last token in line + { 0, NULL } +}; +size_t tokennames_max = array_size(tokennames); + +/** + * Pretty-prints a graph, assuming it is a tree. + * + * @param start the node to take as the root + * @param level indent level for recursive calls, always pass 0 + */ +void +pretty_print_graph (struct vty *vty, struct graph_node *start, int level, + int desc, struct graph_node **stack, size_t stackpos) +{ + // print this node + char tokennum[32]; + struct cmd_token *tok = start->data; + + snprintf(tokennum, sizeof(tokennum), "%d?", tok->type); + vty_out(vty, "%s", LOOKUP_DEF(tokennames, tok->type, tokennum)); + if (tok->text) + vty_out(vty, ":\"%s\"", tok->text); + if (desc) + vty_out(vty, " ?'%s'", tok->desc); + vty_out(vty, " "); + + if (stackpos == MAXDEPTH) + { + vty_out(vty, " -aborting! (depth limit)%s", VTY_NEWLINE); + return; + } + stack[stackpos++] = start; + + int numto = desc ? 2 : vector_active (start->to); + if (numto) + { + if (numto > 1) + vty_out(vty, "%s", VTY_NEWLINE); + for (unsigned int i = 0; i < vector_active (start->to); i++) + { + struct graph_node *adj = vector_slot (start->to, i); + // if we're listing multiple children, indent! + if (numto > 1) + for (int j = 0; j < level+1; j++) + vty_out(vty, " "); + // if this node is a vararg, just print * + if (adj == start) + vty_out(vty, "*"); + else if (((struct cmd_token *)adj->data)->type == END_TKN) + vty_out(vty, "--END%s", VTY_NEWLINE); + else { + size_t k; + for (k = 0; k < stackpos; k++) + if (stack[k] == adj) { + vty_out(vty, "<<loop@%zu %s", k, VTY_NEWLINE); + break; + } + if (k == stackpos) + pretty_print_graph (vty, adj, numto > 1 ? level+1 : level, desc, stack, stackpos); + } + } + } + else + vty_out(vty, "%s", VTY_NEWLINE); +} + +static void +pretty_print_dot (FILE *ofd, unsigned opts, struct graph_node *start, + struct graph_node **stack, size_t stackpos, + struct graph_node **visited, size_t *visitpos) +{ + // print this node + char tokennum[32]; + struct cmd_token *tok = start->data; + const char *color; + + for (size_t i = 0; i < (*visitpos); i++) + if (visited[i] == start) + return; + visited[(*visitpos)++] = start; + if ((*visitpos) == MAXDEPTH*MAXDEPTH) + return; + + snprintf(tokennum, sizeof(tokennum), "%d?", tok->type); + fprintf(ofd, " n%016llx [ shape=box, label=<", (unsigned long long)start); + + fprintf(ofd, "<b>%s</b>", LOOKUP_DEF(tokennames, tok->type, tokennum)); + if (tok->attr == CMD_ATTR_DEPRECATED) + fprintf(ofd, " (d)"); + else if (tok->attr == CMD_ATTR_HIDDEN) + fprintf(ofd, " (h)"); + if (tok->text) { + if (tok->type == WORD_TKN) + fprintf(ofd, "<br/>\"<font color=\"#0055ff\" point-size=\"11\"><b>%s</b></font>\"", tok->text); + else + fprintf(ofd, "<br/>%s", tok->text); + } +/* if (desc) + fprintf(ofd, " ?'%s'", tok->desc); */ + switch (tok->type) { + case START_TKN: color = "#ccffcc"; break; + case FORK_TKN: color = "#aaddff"; break; + case JOIN_TKN: color = "#ddaaff"; break; + case WORD_TKN: color = "#ffffff"; break; + default: color = "#ffffff"; break; + } + fprintf(ofd, ">, style = filled, fillcolor = \"%s\" ];\n", color); + + if (stackpos == MAXDEPTH) + return; + stack[stackpos++] = start; + + for (unsigned int i = 0; i < vector_active (start->to); i++) + { + struct graph_node *adj = vector_slot (start->to, i); + // if this node is a vararg, just print * + if (adj == start) { + fprintf(ofd, " n%016llx -> n%016llx;\n", + (unsigned long long)start, + (unsigned long long)start); + } else if (((struct cmd_token *)adj->data)->type == END_TKN) { + //struct cmd_token *et = adj->data; + fprintf(ofd, " n%016llx -> end%016llx;\n", + (unsigned long long)start, + (unsigned long long)adj); + fprintf(ofd, " end%016llx [ shape=box, label=<end>, style = filled, fillcolor = \"#ffddaa\" ];\n", + (unsigned long long)adj); + } else { + fprintf(ofd, " n%016llx -> n%016llx;\n", + (unsigned long long)start, + (unsigned long long)adj); + size_t k; + for (k = 0; k < stackpos; k++) + if (stack[k] == adj) + break; + if (k == stackpos) { + pretty_print_dot (ofd, opts, adj, stack, stackpos, visited, visitpos); + } + } + } +} + + +/** stuff that should go in command.c + command.h */ +void +init_cmdgraph (struct vty *vty, struct graph **graph) +{ + // initialize graph, add start noe + *graph = graph_new (); + nodegraph_free = *graph; + struct cmd_token *token = new_cmd_token (START_TKN, 0, NULL, NULL); + graph_new_node (*graph, token, (void (*)(void *)) &del_cmd_token); + if (vty) + vty_out (vty, "initialized graph%s", VTY_NEWLINE); +} diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c new file mode 100644 index 0000000000..681d4da440 --- /dev/null +++ b/lib/grammar_sandbox_main.c @@ -0,0 +1,64 @@ +/* + * Testing shim and API examples for the new CLI backend. + * + * Minimal main() to run grammar_sandbox standalone. + * [split off grammar_sandbox.c 2017-01-23] + * -- + * Copyright (C) 2016 Cumulus Networks, Inc. + * Copyright (C) 2017 David Lamparter for NetDEF, Inc. + * + * This file is part of FreeRangeRouting (FRR). + * + * FRR is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2, or (at your option) any later version. + * + * FRR is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with FRR; see the file COPYING. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "command.h" +#include "memory_vty.h" + +static void vty_do_exit(void) +{ + printf ("\nend.\n"); + exit (0); +} + +struct thread_master *master; + +int main(int argc, char **argv) +{ + struct thread thread; + + master = thread_master_create (); + + openzlog ("grammar_sandbox", "NONE", 0, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + zlog_set_level (ZLOG_DEST_SYSLOG, ZLOG_DISABLED); + zlog_set_level (ZLOG_DEST_STDOUT, LOG_DEBUG); + zlog_set_level (ZLOG_DEST_MONITOR, ZLOG_DISABLED); + + /* Library inits. */ + cmd_init (1); + host.name = strdup ("test"); + + vty_init (master); + memory_init (); + + vty_stdio (vty_do_exit); + + /* Fetch next active thread. */ + while (thread_fetch (master, &thread)) + thread_call (&thread); + + /* Not reached. */ + exit (0); +} diff --git a/lib/graph.c b/lib/graph.c new file mode 100644 index 0000000000..0992059ef1 --- /dev/null +++ b/lib/graph.c @@ -0,0 +1,148 @@ +/* + * Graph data structure. + * + * -- + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include <zebra.h> +#include "graph.h" +#include "memory.h" + +DEFINE_MTYPE_STATIC(LIB, GRAPH, "Graph") +DEFINE_MTYPE_STATIC(LIB, GRAPH_NODE, "Graph Node") +struct graph * +graph_new () +{ + struct graph *graph = XCALLOC (MTYPE_GRAPH, sizeof(struct graph)); + graph->nodes = vector_init (VECTOR_MIN_SIZE); + + return graph; +} + +struct graph_node * +graph_new_node (struct graph *graph, void *data, void (*del) (void*)) +{ + struct graph_node *node = + XCALLOC(MTYPE_GRAPH_NODE, sizeof(struct graph_node)); + + node->from = vector_init (VECTOR_MIN_SIZE); + node->to = vector_init (VECTOR_MIN_SIZE); + node->data = data; + node->del = del; + + vector_set (graph->nodes, node); + + return node; +} + +static void +vector_remove (vector v, unsigned int ix) +{ + if (ix >= v->active) + return; + + /* v->active is guaranteed >= 1 because ix can't be lower than 0 + * and v->active is > ix. */ + v->active--; + /* if ix == v->active--, we set the item to itself, then to NULL... + * still correct, no check neccessary. */ + v->index[ix] = v->index[v->active]; + v->index[v->active] = NULL; +} + +void +graph_delete_node (struct graph *graph, struct graph_node *node) +{ + if (!node) return; + + // an adjacent node + struct graph_node *adj; + + // remove all edges from other nodes to us + for (unsigned int i = vector_active (node->from); i--; /**/) + { + adj = vector_slot (node->from, i); + graph_remove_edge (adj, node); + } + + // remove all edges from us to other nodes + for (unsigned int i = vector_active (node->to); i--; /**/) + { + adj = vector_slot (node->to, i); + graph_remove_edge (node, adj); + } + + // if there is a deletion callback, call it + if (node->del && node->data) + (*node->del) (node->data); + + // free adjacency lists + vector_free (node->to); + vector_free (node->from); + + // remove node from graph->nodes + for (unsigned int i = vector_active (graph->nodes); i--; /**/) + if (vector_slot (graph->nodes, i) == node) + { + vector_remove (graph->nodes, i); + break; + } + + // free the node itself + XFREE (MTYPE_GRAPH_NODE, node); +} + +struct graph_node * +graph_add_edge (struct graph_node *from, struct graph_node *to) +{ + vector_set (from->to, to); + vector_set (to->from, from); + return to; +} + +void +graph_remove_edge (struct graph_node *from, struct graph_node *to) +{ + // remove from from to->from + for (unsigned int i = vector_active (to->from); i--; /**/) + if (vector_slot (to->from, i) == from) + { + vector_remove (to->from, i); + break; + } + // remove to from from->to + for (unsigned int i = vector_active (from->to); i--; /**/) + if (vector_slot (from->to, i) == to) + { + vector_remove (from->to, i); + break; + } +} + +void +graph_delete_graph (struct graph *graph) +{ + // delete each node in the graph + for (unsigned int i = vector_active (graph->nodes); i--; /**/) + graph_delete_node (graph, vector_slot (graph->nodes, i)); + + vector_free (graph->nodes); + XFREE (MTYPE_GRAPH, graph); +} diff --git a/lib/graph.h b/lib/graph.h new file mode 100644 index 0000000000..8d8aa3823b --- /dev/null +++ b/lib/graph.h @@ -0,0 +1,101 @@ +/* + * Graph data structure. + * + * -- + * Copyright (C) 2016 Cumulus Networks, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_COMMAND_GRAPH_H +#define _ZEBRA_COMMAND_GRAPH_H + +#include "vector.h" + +struct graph +{ + vector nodes; +}; + +struct graph_node +{ + vector from; // nodes which have edges to this node + vector to; // nodes which this node has edges to + + void *data; // node data + void (*del) (void *data); // deletion callback +}; + +struct graph * +graph_new (void); + +/** + * Creates a new node. + * + * @struct graph the graph this node exists in + * @param[in] data this node's data + * @param[in] del data deletion callback + * @return the new node + */ +struct graph_node * +graph_new_node (struct graph *graph, void *data, void (*del) (void*)); + +/** + * Deletes a node. + * + * Before deletion, this function removes all edges to and from this node from + * any neighbor nodes. + * + * If *data and *del are non-null, the following call is made: + * (*node->del) (node->data); + * + * @param[in] graph the graph this node belongs to + * @param[out] node pointer to node to delete + */ +void +graph_delete_node (struct graph *graph, struct graph_node *node); + +/** + * Makes a directed edge between two nodes. + * + * @param[in] from + * @param[in] to + * @return to + */ +struct graph_node * +graph_add_edge (struct graph_node *from, struct graph_node *to); + +/** + * Removes a directed edge between two nodes. + * + * @param[in] from + * @param[in] to + */ +void +graph_remove_edge (struct graph_node *from, struct graph_node *to); + +/** + * Deletes a graph. + * Calls graph_delete_node on each node before freeing the graph struct itself. + * + * @param graph the graph to delete + */ +void +graph_delete_graph (struct graph *graph); + +#endif /* _ZEBRA_COMMAND_GRAPH_H */ @@ -374,7 +374,7 @@ if_lookup_exact_address_vrf (void *src, int family, vrf_id_t vrf_id) } else if (family == AF_INET6) { - if (IPV6_ADDR_SAME (&p->u.prefix4, (struct in6_addr *)src)) + if (IPV6_ADDR_SAME (&p->u.prefix6, (struct in6_addr *)src)) return ifp; } } @@ -671,25 +671,23 @@ if_dump_all (void) if_dump (p); } -DEFUN (interface_desc, +DEFUN (interface_desc, interface_desc_cmd, - "description .LINE", + "description LINE...", "Interface specific description\n" "Characters describing this interface\n") { + int idx_line = 1; VTY_DECLVAR_CONTEXT (interface, ifp); - if (argc == 0) - return CMD_SUCCESS; - if (ifp->desc) XFREE (MTYPE_TMP, ifp->desc); - ifp->desc = argv_concat(argv, argc, 0); + ifp->desc = argv_concat(argv, argc, idx_line); return CMD_SUCCESS; } -DEFUN (no_interface_desc, +DEFUN (no_interface_desc, no_interface_desc_cmd, "no description", NO_STR @@ -748,69 +746,72 @@ if_sunwzebra_get (const char *name, size_t nlen, vrf_id_t vrf_id) DEFUN (interface, interface_cmd, - "interface IFNAME", + "interface IFNAME [vrf NAME]", "Select an interface to configure\n" - "Interface's name\n") + "Interface's name\n" + VRF_CMD_HELP_STR) { + int idx_ifname = 1; + int idx_vrf = 3; + const char *ifname = argv[idx_ifname]->arg; + const char *vrfname = (argc > 2) ? argv[idx_vrf]->arg : NULL; + struct interface *ifp; size_t sl; vrf_id_t vrf_id = VRF_DEFAULT; - if ((sl = strlen(argv[0])) > INTERFACE_NAMSIZ) + if ((sl = strlen(ifname)) > INTERFACE_NAMSIZ) { vty_out (vty, "%% Interface name %s is invalid: length exceeds " "%d characters%s", - argv[0], INTERFACE_NAMSIZ, VTY_NEWLINE); + ifname, INTERFACE_NAMSIZ, VTY_NEWLINE); return CMD_WARNING; } /*Pending: need proper vrf name based lookup/(possible creation of VRF) Imagine forward reference of a vrf by name in this interface config */ - if (argc > 1) - VRF_GET_ID (vrf_id, argv[1]); + if (vrfname) + VRF_GET_ID (vrf_id, vrfname); #ifdef SUNOS_5 - ifp = if_sunwzebra_get (argv[0], sl, vrf_id); + ifp = if_sunwzebra_get (ifname, sl, vrf_id); #else - ifp = if_get_by_name_len_vrf (argv[0], sl, vrf_id, 1); + ifp = if_get_by_name_len_vrf (ifname, sl, vrf_id, 1); #endif /* SUNOS_5 */ if (!ifp) { - vty_out (vty, "%% interface %s not in %s%s", argv[0], argv[1], VTY_NEWLINE); + vty_out (vty, "%% interface %s not in %s%s", ifname, vrfname, VTY_NEWLINE); return CMD_WARNING; } - VTY_PUSH_CONTEXT_COMPAT (INTERFACE_NODE, ifp); + VTY_PUSH_CONTEXT (INTERFACE_NODE, ifp); return CMD_SUCCESS; } -ALIAS (interface, - interface_vrf_cmd, - "interface IFNAME " VRF_CMD_STR, - "Select an interface to configure\n" - "Interface's name\n" - VRF_CMD_HELP_STR) - DEFUN_NOSH (no_interface, no_interface_cmd, - "no interface IFNAME", + "no interface IFNAME [vrf NAME]", NO_STR "Delete a pseudo interface's configuration\n" - "Interface's name\n") + "Interface's name\n" + VRF_CMD_HELP_STR) { + const char *ifname = argv[2]->arg; + const char *vrfname = (argc > 3) ? argv[3]->arg : NULL; + // deleting interface struct interface *ifp; vrf_id_t vrf_id = VRF_DEFAULT; - if (argc > 1) - VRF_GET_ID (vrf_id, argv[1]); + if (argc > 3) + VRF_GET_ID (vrf_id, vrfname); - ifp = if_lookup_by_name_vrf (argv[0], vrf_id); + ifp = if_lookup_by_name_vrf (ifname, vrf_id); if (ifp == NULL) { - vty_out (vty, "%% Interface %s does not exist%s", argv[0], VTY_NEWLINE); + vty_out (vty, "%% Interface %s does not exist%s", ifname, VTY_NEWLINE); return CMD_WARNING; } @@ -826,21 +827,27 @@ DEFUN_NOSH (no_interface, return CMD_SUCCESS; } -ALIAS (no_interface, - no_interface_vrf_cmd, - "no interface IFNAME " VRF_CMD_STR, - NO_STR - "Delete a pseudo interface's configuration\n" - "Interface's name\n" - VRF_CMD_HELP_STR) +void +if_cmd_init (void) +{ + install_element (CONFIG_NODE, &interface_cmd); + install_element (CONFIG_NODE, &no_interface_cmd); + + install_default (INTERFACE_NODE); + install_element (INTERFACE_NODE, &interface_desc_cmd); + install_element (INTERFACE_NODE, &no_interface_desc_cmd); +} +#if 0 /* For debug purpose. */ DEFUN (show_address, show_address_cmd, - "show address", + "show address [vrf NAME]", SHOW_STR - "address\n") + "address\n" + VRF_CMD_HELP_STR) { + int idx_vrf = 3; struct listnode *node; struct listnode *node2; struct interface *ifp; @@ -848,8 +855,8 @@ DEFUN (show_address, struct prefix *p; vrf_id_t vrf_id = VRF_DEFAULT; - if (argc > 0) - VRF_GET_ID (vrf_id, argv[0]); + if (argc > 2) + VRF_GET_ID (vrf_id, argv[idx_vrf]->arg); for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) { @@ -865,16 +872,9 @@ DEFUN (show_address, return CMD_SUCCESS; } -ALIAS (show_address, - show_address_vrf_cmd, - "show address " VRF_CMD_STR, - SHOW_STR - "address\n" - VRF_CMD_HELP_STR) - DEFUN (show_address_vrf_all, show_address_vrf_all_cmd, - "show address " VRF_ALL_CMD_STR, + "show address vrf all", SHOW_STR "address\n" VRF_ALL_CMD_HELP_STR) @@ -908,6 +908,7 @@ DEFUN (show_address_vrf_all, } return CMD_SUCCESS; } +#endif /* Allocate connected structure. */ struct connected * @@ -986,7 +987,7 @@ connected_log (struct connected *connected, char *str) strncat (logbuf, inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), BUFSIZ - strlen(logbuf)); } - zlog (NULL, LOG_INFO, "%s", logbuf); + zlog_info("%s", logbuf); } /* Print if_addr structure. */ @@ -1006,7 +1007,7 @@ nbr_connected_log (struct nbr_connected *connected, char *str) inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen); - zlog (NULL, LOG_INFO, "%s", logbuf); + zlog_info("%s", logbuf); } /* If two connected address has same prefix return 1. */ @@ -1018,11 +1019,9 @@ connected_same_prefix (struct prefix *p1, struct prefix *p2) if (p1->family == AF_INET && IPV4_ADDR_SAME (&p1->u.prefix4, &p2->u.prefix4)) return 1; -#ifdef HAVE_IPV6 if (p1->family == AF_INET6 && IPV6_ADDR_SAME (&p1->u.prefix6, &p2->u.prefix6)) return 1; -#endif /* HAVE_IPV6 */ } return 0; } @@ -1301,7 +1300,7 @@ if_link_params_get (struct interface *ifp) sizeof (struct if_link_params)); if (iflp == NULL) return NULL; - /* Set TE metric == standard metric */ + /* Set TE metric equal to standard metric */ iflp->te_metric = ifp->metric; /* Compute default bandwidth based on interface */ @@ -1315,7 +1314,7 @@ if_link_params_get (struct interface *ifp) iflp->unrsv_bw[i] = iflp->default_bw; /* Update Link parameters status */ - iflp->lp_status = LP_TE | LP_MAX_BW | LP_MAX_RSV_BW | LP_UNRSV_BW; + iflp->lp_status = LP_TE_METRIC | LP_MAX_BW | LP_MAX_RSV_BW | LP_UNRSV_BW; /* Finally attach newly created Link Parameters */ ifp->link_params = iflp; @@ -146,9 +146,13 @@ struct if_stats #define MAX_CLASS_TYPE 8 #define MAX_PKT_LOSS 50.331642 -/* Link Parameters Status: 0: unset, 1: set, */ +/* + * Link Parameters Status: + * equal to 0: unset + * different from 0: set + */ #define LP_UNSET 0x0000 -#define LP_TE 0x0001 +#define LP_TE_METRIC 0x0001 #define LP_MAX_BW 0x0002 #define LP_MAX_RSV_BW 0x0004 #define LP_UNRSV_BW 0x0008 @@ -161,7 +165,6 @@ struct if_stats #define LP_RES_BW 0x0400 #define LP_AVA_BW 0x0800 #define LP_USE_BW 0x1000 -#define LP_TE_METRIC 0x2000 #define IS_PARAM_UNSET(lp, st) !(lp->lp_status & st) #define IS_PARAM_SET(lp, st) (lp->lp_status & st) @@ -224,7 +227,7 @@ struct interface uint64_t flags; /* Interface metric */ - int metric; + uint32_t metric; /* Interface MTU. */ unsigned int mtu; /* IPv4 MTU */ @@ -447,6 +450,7 @@ extern int if_is_pointopoint (struct interface *); extern int if_is_multicast (struct interface *); extern void if_add_hook (int, int (*)(struct interface *)); extern void if_init (struct list **); +extern void if_cmd_init (void); extern void if_terminate (struct list **); extern void if_dump_all (void); extern const char *if_flag_dump(unsigned long); @@ -485,19 +489,4 @@ struct nbr_connected *nbr_connected_check (struct interface *, struct prefix *); struct if_link_params *if_link_params_get (struct interface *); void if_link_params_free (struct interface *); -/* Exported variables. */ -extern struct cmd_element interface_desc_cmd; -extern struct cmd_element no_interface_desc_cmd; -extern struct cmd_element interface_cmd; -extern struct cmd_element no_interface_cmd; -extern struct cmd_element interface_vrf_cmd; -extern struct cmd_element no_interface_vrf_cmd; -extern struct cmd_element interface_pseudo_cmd; -extern struct cmd_element no_interface_pseudo_cmd; -extern struct cmd_element show_address_cmd; -extern struct cmd_element show_address_vrf_cmd; -extern struct cmd_element show_address_vrf_all_cmd; -extern struct cmd_element vrf_cmd; -extern struct cmd_element no_vrf_cmd; - #endif /* _ZEBRA_IF_H */ diff --git a/lib/if_rmap.c b/lib/if_rmap.c index 736f2e237d..2afb08c7ca 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -211,18 +211,21 @@ if_rmap_unset (const char *ifname, enum if_rmap_type type, DEFUN (if_rmap, if_rmap_cmd, - "route-map RMAP_NAME (in|out) IFNAME", + "route-map RMAP_NAME <in|out> IFNAME", "Route map set\n" "Route map name\n" "Route map set for input filtering\n" "Route map set for output filtering\n" "Route map interface name\n") { + int idx_rmap_name = 1; + int idx_in_out = 2; + int idx_ifname = 3; enum if_rmap_type type; - if (strncmp (argv[1], "i", 1) == 0) + if (strncmp (argv[idx_in_out]->text, "in", 1) == 0) type = IF_RMAP_IN; - else if (strncmp (argv[1], "o", 1) == 0) + else if (strncmp (argv[idx_in_out]->text, "out", 1) == 0) type = IF_RMAP_OUT; else { @@ -230,23 +233,14 @@ DEFUN (if_rmap, return CMD_WARNING; } - if_rmap_set (argv[2], type, argv[0]); + if_rmap_set (argv[idx_ifname]->arg, type, argv[idx_rmap_name]->arg); return CMD_SUCCESS; -} - -ALIAS (if_rmap, - if_ipv6_rmap_cmd, - "route-map RMAP_NAME (in|out) IFNAME", - "Route map set\n" - "Route map name\n" - "Route map set for input filtering\n" - "Route map set for output filtering\n" - "Route map interface name\n") +} DEFUN (no_if_rmap, no_if_rmap_cmd, - "no route-map ROUTEMAP_NAME (in|out) IFNAME", + "no route-map ROUTEMAP_NAME <in|out> IFNAME", NO_STR "Route map unset\n" "Route map name\n" @@ -254,12 +248,15 @@ DEFUN (no_if_rmap, "Route map for output filtering\n" "Route map interface name\n") { + int idx_routemap_name = 2; + int idx_in_out = 3; + int idx_ifname = 4; int ret; enum if_rmap_type type; - if (strncmp (argv[1], "i", 1) == 0) + if (strncmp (argv[idx_in_out]->arg, "i", 1) == 0) type = IF_RMAP_IN; - else if (strncmp (argv[1], "o", 1) == 0) + else if (strncmp (argv[idx_in_out]->arg, "o", 1) == 0) type = IF_RMAP_OUT; else { @@ -267,24 +264,15 @@ DEFUN (no_if_rmap, return CMD_WARNING; } - ret = if_rmap_unset (argv[2], type, argv[0]); + ret = if_rmap_unset (argv[idx_ifname]->arg, type, argv[idx_routemap_name]->arg); if (! ret) { vty_out (vty, "route-map doesn't exist%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; -} +} -ALIAS (no_if_rmap, - no_if_ipv6_rmap_cmd, - "no route-map ROUTEMAP_NAME (in|out) IFNAME", - NO_STR - "Route map unset\n" - "Route map name\n" - "Route map for input filtering\n" - "Route map for output filtering\n" - "Route map interface name\n") /* Configuration write function. */ int @@ -333,8 +321,6 @@ if_rmap_init (int node) { ifrmaphash = hash_create (if_rmap_hash_make, if_rmap_hash_cmp); if (node == RIPNG_NODE) { - install_element (RIPNG_NODE, &if_ipv6_rmap_cmd); - install_element (RIPNG_NODE, &no_if_ipv6_rmap_cmd); } else if (node == RIP_NODE) { install_element (RIP_NODE, &if_rmap_cmd); install_element (RIP_NODE, &no_if_rmap_cmd); diff --git a/lib/imsg.c b/lib/imsg.c index 246430cdd5..fc62c13734 100644 --- a/lib/imsg.c +++ b/lib/imsg.c @@ -182,7 +182,8 @@ imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) else imsg->fd = -1; - memcpy(imsg->data, ibuf->r.rptr, datalen); + if (imsg->data) + memcpy(imsg->data, ibuf->r.rptr, datalen); if (imsg->hdr.len < av) { left = av - imsg->hdr.len; diff --git a/lib/json.c b/lib/json.c index c49a4f9074..ccbecb726a 100644 --- a/lib/json.c +++ b/lib/json.c @@ -21,6 +21,7 @@ #include <zebra.h> +#include "command.h" #include "lib/json.h" /* @@ -29,12 +30,12 @@ * what. */ int -use_json (const int argc, const char *argv[]) +use_json (const int argc, struct cmd_token *argv[]) { if (argc == 0) return 0; - if (argv[argc-1] && strcmp(argv[argc-1], "json") == 0) + if (argv[argc-1]->arg && strcmp(argv[argc-1]->arg, "json") == 0) return 1; return 0; @@ -86,3 +87,18 @@ json_object_free(struct json_object *obj) { json_object_put(obj); } + +#if !defined(HAVE_JSON_C_JSON_H) +int +json_object_object_get_ex(struct json_object *obj, + const char *key, + struct json_object **value) +{ + *value = json_object_object_get(obj, key); + + if (*value) + return 1; + + return 0; +} +#endif diff --git a/lib/json.h b/lib/json.h index b217df0a7b..7e98614280 100644 --- a/lib/json.h +++ b/lib/json.h @@ -32,9 +32,15 @@ * so let's just turn it back to the original usage. */ #define json_object_to_json_string_ext(A, B) json_object_to_json_string (A) + +extern int json_object_object_get_ex(struct json_object *obj, + const char *key, + struct json_object **value); #endif -extern int use_json(const int argc, const char *argv[]); +#include "command.h" + +extern int use_json(const int argc, struct cmd_token *argv[]); extern void json_object_string_add(struct json_object* obj, const char *key, const char *s); extern void json_object_int_add(struct json_object* obj, const char *key, diff --git a/lib/keychain.c b/lib/keychain.c index c090956487..cd8039b95b 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -247,10 +247,11 @@ DEFUN (key_chain, "Key-chain management\n" "Key-chain name\n") { + int idx_word = 2; struct keychain *keychain; - keychain = keychain_get (argv[0]); - VTY_PUSH_CONTEXT_COMPAT (KEYCHAIN_NODE, keychain); + keychain = keychain_get (argv[idx_word]->arg); + VTY_PUSH_CONTEXT (KEYCHAIN_NODE, keychain); return CMD_SUCCESS; } @@ -263,13 +264,14 @@ DEFUN (no_key_chain, "Key-chain management\n" "Key-chain name\n") { + int idx_word = 3; struct keychain *keychain; - keychain = keychain_lookup (argv[0]); + keychain = keychain_lookup (argv[idx_word]->arg); if (! keychain) { - vty_out (vty, "Can't find keychain %s%s", argv[0], VTY_NEWLINE); + vty_out (vty, "Can't find keychain %s%s", argv[idx_word]->arg, VTY_NEWLINE); return CMD_WARNING; } @@ -280,33 +282,35 @@ DEFUN (no_key_chain, DEFUN (key, key_cmd, - "key <0-2147483647>", + "key (0-2147483647)", "Configure a key\n" "Key identifier number\n") { + int idx_number = 1; VTY_DECLVAR_CONTEXT (keychain, keychain); struct key *key; u_int32_t index; - VTY_GET_INTEGER ("key identifier", index, argv[0]); + VTY_GET_INTEGER ("key identifier", index, argv[idx_number]->arg); key = key_get (keychain, index); VTY_PUSH_CONTEXT_SUB (KEYCHAIN_KEY_NODE, key); - + return CMD_SUCCESS; } DEFUN (no_key, no_key_cmd, - "no key <0-2147483647>", + "no key (0-2147483647)", NO_STR "Delete a key\n" "Key identifier number\n") { + int idx_number = 2; VTY_DECLVAR_CONTEXT (keychain, keychain); struct key *key; u_int32_t index; - - VTY_GET_INTEGER ("key identifier", index, argv[0]); + + VTY_GET_INTEGER ("key identifier", index, argv[idx_number]->arg); key = key_lookup (keychain, index); if (! key) { @@ -327,11 +331,12 @@ DEFUN (key_string, "Set key string\n" "The key\n") { + int idx_line = 1; VTY_DECLVAR_CONTEXT_SUB (key, key); if (key->string) XFREE(MTYPE_KEY, key->string); - key->string = XSTRDUP(MTYPE_KEY, argv[0]); + key->string = XSTRDUP(MTYPE_KEY, argv[idx_line]->arg); return CMD_SUCCESS; } @@ -541,7 +546,7 @@ key_lifetime_infinite_set (struct vty *vty, struct key_range *krange, DEFUN (accept_lifetime_day_month_day_month, accept_lifetime_day_month_day_month_cmd, - "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", + "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", "Set accept lifetime of the key\n" "Time to start\n" "Day of th month to start\n" @@ -552,15 +557,23 @@ DEFUN (accept_lifetime_day_month_day_month, "Month of the year to expire\n" "Year to expire\n") { + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_number_3 = 6; + int idx_month_2 = 7; + int idx_number_4 = 8; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_set (vty, &key->accept, argv[0], argv[1], argv[2], - argv[3], argv[4], argv[5], argv[6], argv[7]); + return key_lifetime_set (vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, argv[idx_month_2]->arg, argv[idx_number_4]->arg); } DEFUN (accept_lifetime_day_month_month_day, accept_lifetime_day_month_month_day_cmd, - "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", "Set accept lifetime of the key\n" "Time to start\n" "Day of th month to start\n" @@ -571,15 +584,23 @@ DEFUN (accept_lifetime_day_month_month_day, "Day of th month to expire\n" "Year to expire\n") { + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_month_2 = 6; + int idx_number_3 = 7; + int idx_number_4 = 8; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_set (vty, &key->accept, argv[0], argv[1], argv[2], - argv[3], argv[4], argv[6], argv[5], argv[7]); + return key_lifetime_set (vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, argv[idx_month_2]->arg, argv[idx_number_4]->arg); } DEFUN (accept_lifetime_month_day_day_month, accept_lifetime_month_day_day_month_cmd, - "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", + "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", "Set accept lifetime of the key\n" "Time to start\n" "Month of the year to start\n" @@ -590,15 +611,23 @@ DEFUN (accept_lifetime_month_day_day_month, "Month of the year to expire\n" "Year to expire\n") { + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_number_3 = 6; + int idx_month_2 = 7; + int idx_number_4 = 8; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_set (vty, &key->accept, argv[0], argv[2], argv[1], - argv[3], argv[4], argv[5], argv[6], argv[7]); + return key_lifetime_set (vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, argv[idx_month_2]->arg, argv[idx_number_4]->arg); } DEFUN (accept_lifetime_month_day_month_day, accept_lifetime_month_day_month_day_cmd, - "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", "Set accept lifetime of the key\n" "Time to start\n" "Month of the year to start\n" @@ -609,15 +638,23 @@ DEFUN (accept_lifetime_month_day_month_day, "Day of th month to expire\n" "Year to expire\n") { + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_month_2 = 6; + int idx_number_3 = 7; + int idx_number_4 = 8; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_set (vty, &key->accept, argv[0], argv[2], argv[1], - argv[3], argv[4], argv[6], argv[5], argv[7]); + return key_lifetime_set (vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, argv[idx_month_2]->arg, argv[idx_number_4]->arg); } DEFUN (accept_lifetime_infinite_day_month, accept_lifetime_infinite_day_month_cmd, - "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> infinite", + "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) infinite", "Set accept lifetime of the key\n" "Time to start\n" "Day of th month to start\n" @@ -625,15 +662,19 @@ DEFUN (accept_lifetime_infinite_day_month, "Year to start\n" "Never expires") { + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_infinite_set (vty, &key->accept, argv[0], argv[1], - argv[2], argv[3]); + return key_lifetime_infinite_set (vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, + argv[idx_month]->arg, argv[idx_number_2]->arg); } DEFUN (accept_lifetime_infinite_month_day, accept_lifetime_infinite_month_day_cmd, - "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> infinite", + "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) infinite", "Set accept lifetime of the key\n" "Time to start\n" "Month of the year to start\n" @@ -641,15 +682,19 @@ DEFUN (accept_lifetime_infinite_month_day, "Year to start\n" "Never expires") { + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_infinite_set (vty, &key->accept, argv[0], argv[2], - argv[1], argv[3]); + return key_lifetime_infinite_set (vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, + argv[idx_month]->arg, argv[idx_number_2]->arg); } DEFUN (accept_lifetime_duration_day_month, accept_lifetime_duration_day_month_cmd, - "accept-lifetime HH:MM:SS <1-31> MONTH <1993-2035> duration <1-2147483646>", + "accept-lifetime HH:MM:SS (1-31) MONTH (1993-2035) duration (1-2147483646)", "Set accept lifetime of the key\n" "Time to start\n" "Day of th month to start\n" @@ -658,15 +703,20 @@ DEFUN (accept_lifetime_duration_day_month, "Duration of the key\n" "Duration seconds\n") { + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_number_3 = 6; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[1], - argv[2], argv[3], argv[4]); + return key_lifetime_duration_set (vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, + argv[idx_month]->arg, argv[idx_number_2]->arg, argv[idx_number_3]->arg); } DEFUN (accept_lifetime_duration_month_day, accept_lifetime_duration_month_day_cmd, - "accept-lifetime HH:MM:SS MONTH <1-31> <1993-2035> duration <1-2147483646>", + "accept-lifetime HH:MM:SS MONTH (1-31) (1993-2035) duration (1-2147483646)", "Set accept lifetime of the key\n" "Time to start\n" "Month of the year to start\n" @@ -675,15 +725,20 @@ DEFUN (accept_lifetime_duration_month_day, "Duration of the key\n" "Duration seconds\n") { + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_number_3 = 6; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_duration_set (vty, &key->accept, argv[0], argv[2], - argv[1], argv[3], argv[4]); + return key_lifetime_duration_set (vty, &key->accept, argv[idx_hhmmss]->arg, argv[idx_number]->arg, + argv[idx_month]->arg, argv[idx_number_2]->arg, argv[idx_number_3]->arg); } DEFUN (send_lifetime_day_month_day_month, send_lifetime_day_month_day_month_cmd, - "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", + "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", "Set send lifetime of the key\n" "Time to start\n" "Day of th month to start\n" @@ -694,15 +749,23 @@ DEFUN (send_lifetime_day_month_day_month, "Month of the year to expire\n" "Year to expire\n") { + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_number_3 = 6; + int idx_month_2 = 7; + int idx_number_4 = 8; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3], - argv[4], argv[5], argv[6], argv[7]); + return key_lifetime_set (vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, argv[idx_number_2]->arg, + argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, argv[idx_month_2]->arg, argv[idx_number_4]->arg); } DEFUN (send_lifetime_day_month_month_day, send_lifetime_day_month_month_day_cmd, - "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", "Set send lifetime of the key\n" "Time to start\n" "Day of th month to start\n" @@ -713,15 +776,23 @@ DEFUN (send_lifetime_day_month_month_day, "Day of th month to expire\n" "Year to expire\n") { + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_month_2 = 6; + int idx_number_3 = 7; + int idx_number_4 = 8; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_set (vty, &key->send, argv[0], argv[1], argv[2], argv[3], - argv[4], argv[6], argv[5], argv[7]); + return key_lifetime_set (vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, argv[idx_number_2]->arg, + argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, argv[idx_month_2]->arg, argv[idx_number_4]->arg); } DEFUN (send_lifetime_month_day_day_month, send_lifetime_month_day_day_month_cmd, - "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS <1-31> MONTH <1993-2035>", + "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS (1-31) MONTH (1993-2035)", "Set send lifetime of the key\n" "Time to start\n" "Month of the year to start\n" @@ -732,15 +803,23 @@ DEFUN (send_lifetime_month_day_day_month, "Month of the year to expire\n" "Year to expire\n") { + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_number_3 = 6; + int idx_month_2 = 7; + int idx_number_4 = 8; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], - argv[4], argv[5], argv[6], argv[7]); + return key_lifetime_set (vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, argv[idx_number_2]->arg, + argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, argv[idx_month_2]->arg, argv[idx_number_4]->arg); } DEFUN (send_lifetime_month_day_month_day, send_lifetime_month_day_month_day_cmd, - "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> HH:MM:SS MONTH <1-31> <1993-2035>", + "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) HH:MM:SS MONTH (1-31) (1993-2035)", "Set send lifetime of the key\n" "Time to start\n" "Month of the year to start\n" @@ -751,15 +830,23 @@ DEFUN (send_lifetime_month_day_month_day, "Day of th month to expire\n" "Year to expire\n") { + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_hhmmss_2 = 5; + int idx_month_2 = 6; + int idx_number_3 = 7; + int idx_number_4 = 8; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_set (vty, &key->send, argv[0], argv[2], argv[1], argv[3], - argv[4], argv[6], argv[5], argv[7]); + return key_lifetime_set (vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, argv[idx_number_2]->arg, + argv[idx_hhmmss_2]->arg, argv[idx_number_3]->arg, argv[idx_month_2]->arg, argv[idx_number_4]->arg); } DEFUN (send_lifetime_infinite_day_month, send_lifetime_infinite_day_month_cmd, - "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> infinite", + "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) infinite", "Set send lifetime of the key\n" "Time to start\n" "Day of th month to start\n" @@ -767,15 +854,19 @@ DEFUN (send_lifetime_infinite_day_month, "Year to start\n" "Never expires") { + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_infinite_set (vty, &key->send, argv[0], argv[1], argv[2], - argv[3]); + return key_lifetime_infinite_set (vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg); } DEFUN (send_lifetime_infinite_month_day, send_lifetime_infinite_month_day_cmd, - "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> infinite", + "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) infinite", "Set send lifetime of the key\n" "Time to start\n" "Month of the year to start\n" @@ -783,15 +874,19 @@ DEFUN (send_lifetime_infinite_month_day, "Year to start\n" "Never expires") { + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_infinite_set (vty, &key->send, argv[0], argv[2], argv[1], - argv[3]); + return key_lifetime_infinite_set (vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg); } DEFUN (send_lifetime_duration_day_month, send_lifetime_duration_day_month_cmd, - "send-lifetime HH:MM:SS <1-31> MONTH <1993-2035> duration <1-2147483646>", + "send-lifetime HH:MM:SS (1-31) MONTH (1993-2035) duration (1-2147483646)", "Set send lifetime of the key\n" "Time to start\n" "Day of th month to start\n" @@ -800,15 +895,20 @@ DEFUN (send_lifetime_duration_day_month, "Duration of the key\n" "Duration seconds\n") { + int idx_hhmmss = 1; + int idx_number = 2; + int idx_month = 3; + int idx_number_2 = 4; + int idx_number_3 = 6; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_duration_set (vty, &key->send, argv[0], argv[1], argv[2], - argv[3], argv[4]); + return key_lifetime_duration_set (vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_number_3]->arg); } DEFUN (send_lifetime_duration_month_day, send_lifetime_duration_month_day_cmd, - "send-lifetime HH:MM:SS MONTH <1-31> <1993-2035> duration <1-2147483646>", + "send-lifetime HH:MM:SS MONTH (1-31) (1993-2035) duration (1-2147483646)", "Set send lifetime of the key\n" "Time to start\n" "Month of the year to start\n" @@ -817,10 +917,15 @@ DEFUN (send_lifetime_duration_month_day, "Duration of the key\n" "Duration seconds\n") { + int idx_hhmmss = 1; + int idx_month = 2; + int idx_number = 3; + int idx_number_2 = 4; + int idx_number_3 = 6; VTY_DECLVAR_CONTEXT_SUB (key, key); - return key_lifetime_duration_set (vty, &key->send, argv[0], argv[2], argv[1], - argv[3], argv[4]); + return key_lifetime_duration_set (vty, &key->send, argv[idx_hhmmss]->arg, argv[idx_number]->arg, argv[idx_month]->arg, + argv[idx_number_2]->arg, argv[idx_number_3]->arg); } static struct cmd_node keychain_node = diff --git a/lib/libfrr.c b/lib/libfrr.c new file mode 100644 index 0000000000..f9ac3158a3 --- /dev/null +++ b/lib/libfrr.c @@ -0,0 +1,392 @@ +/* + * libfrr overall management functions + * + * Copyright (C) 2016 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <zebra.h> + +#include "libfrr.h" +#include "getopt.h" +#include "vty.h" +#include "command.h" +#include "version.h" +#include "memory_vty.h" +#include "zclient.h" + +const char frr_sysconfdir[] = SYSCONFDIR; +const char frr_vtydir[] = DAEMON_VTY_DIR; + +char config_default[256]; +static char pidfile_default[256]; +static char vtypath_default[256]; + +static char comb_optstr[256]; +static struct option comb_lo[64]; +static struct option *comb_next_lo = &comb_lo[0]; +static char comb_helpstr[4096]; + +struct optspec { + const char *optstr; + const char *helpstr; + const struct option *longopts; +}; + +static void opt_extend(const struct optspec *os) +{ + const struct option *lo; + + strcat(comb_optstr, os->optstr); + strcat(comb_helpstr, os->helpstr); + for (lo = os->longopts; lo->name; lo++) + memcpy(comb_next_lo++, lo, sizeof(*lo)); +} + + +#define OPTION_VTYSOCK 1000 + +static const struct option lo_always[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "daemon", no_argument, NULL, 'd' }, + { "vty_socket", required_argument, NULL, OPTION_VTYSOCK }, + { NULL } +}; +static const struct optspec os_always = { + "hvdi:", + " -h, --help Display this help and exit\n" + " -v, --version Print program version\n" + " -d, --daemon Runs in daemon mode\n" + " --vty_socket Override vty socket path\n", + lo_always +}; + + +static const struct option lo_cfg_pid_dry[] = { + { "pid_file", required_argument, NULL, 'i' }, + { "config_file", required_argument, NULL, 'f' }, + { "dryrun", no_argument, NULL, 'C' }, + { NULL } +}; +static const struct optspec os_cfg_pid_dry = { + "f:i:C", + " -f, --config_file Set configuration file name\n" + " -i, --pid_file Set process identifier file name\n" + " -C, --dryrun Check configuration for validity and exit\n", + lo_cfg_pid_dry +}; + + +static const struct option lo_zclient[] = { + { "socket", required_argument, NULL, 'z' }, + { NULL } +}; +static const struct optspec os_zclient = { + "z:", + " -z, --socket Set path of zebra socket\n", + lo_zclient +}; + + +static const struct option lo_vty[] = { + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { NULL } +}; +static const struct optspec os_vty = { + "A:P:", + " -A, --vty_addr Set vty's bind address\n" + " -P, --vty_port Set vty's port number\n", + lo_vty +}; + + +static const struct option lo_user[] = { + { "user", required_argument, NULL, 'u'}, + { "group", required_argument, NULL, 'g'}, + { NULL } +}; +static const struct optspec os_user = { + "u:g:", + " -u, --user User to run as\n" + " -g, --group Group to run as\n", + lo_user +}; + + +static struct frr_daemon_info *di = NULL; + +void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) +{ + di = daemon; + + /* basename(), opencoded. */ + char *p = strrchr(argv[0], '/'); + di->progname = p ? p + 1 : argv[0]; + + umask(0027); + + opt_extend(&os_always); + if (!(di->flags & FRR_NO_CFG_PID_DRY)) + opt_extend(&os_cfg_pid_dry); + if (!(di->flags & FRR_NO_PRIVSEP)) + opt_extend(&os_user); + if (!(di->flags & FRR_NO_ZCLIENT)) + opt_extend(&os_zclient); + if (!(di->flags & FRR_NO_TCPVTY)) + opt_extend(&os_vty); + + snprintf(config_default, sizeof(config_default), "%s/%s.conf", + frr_sysconfdir, di->name); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s.pid", + frr_vtydir, di->name); +} + +void frr_opt_add(const char *optstr, const struct option *longopts, + const char *helpstr) +{ + const struct optspec main_opts = { optstr, helpstr, longopts }; + opt_extend(&main_opts); +} + +void frr_help_exit(int status) +{ + FILE *target = status ? stderr : stdout; + + if (status != 0) + fprintf(stderr, "Invalid options.\n\n"); + + if (di->printhelp) + di->printhelp(target); + else + fprintf(target, "Usage: %s [OPTION...]\n\n%s%s%s\n\n%s", + di->progname, + di->proghelp, + di->copyright ? "\n\n" : "", + di->copyright ? di->copyright : "", + comb_helpstr); + fprintf(target, "\nReport bugs to %s\n", FRR_BUG_ADDRESS); + exit(status); +} + +static int errors = 0; + +static int frr_opt(int opt) +{ + static int vty_port_set = 0; + static int vty_addr_set = 0; + char *err; + + switch (opt) { + case 'h': + frr_help_exit(0); + break; + case 'v': + print_version(di->progname); + exit(0); + break; + case 'd': + di->daemon_mode = 1; + break; + case 'i': + if (di->flags & FRR_NO_CFG_PID_DRY) + return 1; + di->pid_file = optarg; + break; + case 'f': + if (di->flags & FRR_NO_CFG_PID_DRY) + return 1; + di->config_file = optarg; + break; + case 'C': + if (di->flags & FRR_NO_CFG_PID_DRY) + return 1; + di->dryrun = 1; + break; + case 'z': + if (di->flags & FRR_NO_ZCLIENT) + return 1; + zclient_serv_path_set(optarg); + break; + case 'A': + if (di->flags & FRR_NO_TCPVTY) + return 1; + if (vty_addr_set) { + fprintf(stderr, "-A option specified more than once!\n"); + errors++; + break; + } + vty_addr_set = 1; + di->vty_addr = optarg; + break; + case 'P': + if (di->flags & FRR_NO_TCPVTY) + return 1; + if (vty_port_set) { + fprintf(stderr, "-P option specified more than once!\n"); + errors++; + break; + } + vty_port_set = 1; + di->vty_port = strtoul(optarg, &err, 0); + if (*err || !*optarg) { + fprintf(stderr, "invalid port number \"%s\" for -P option\n", + optarg); + errors++; + break; + } + break; + case OPTION_VTYSOCK: + if (di->vty_sock_path) { + fprintf(stderr, "--vty_socket option specified more than once!\n"); + errors++; + break; + } + di->vty_sock_path = optarg; + break; + case 'u': + if (di->flags & FRR_NO_PRIVSEP) + return 1; + di->privs->user = optarg; + break; + case 'g': + if (di->flags & FRR_NO_PRIVSEP) + return 1; + di->privs->group = optarg; + break; + default: + return 1; + } + return 0; +} + +int frr_getopt(int argc, char * const argv[], int *longindex) +{ + int opt; + int lidx; + + comb_next_lo->name = NULL; + + do { + opt = getopt_long(argc, argv, comb_optstr, comb_lo, &lidx); + if (frr_opt(opt)) + break; + } while (opt != -1); + + if (opt == -1 && errors) + frr_help_exit(1); + if (longindex) + *longindex = lidx; + return opt; +} + +struct thread_master *frr_init(void) +{ + struct thread_master *master; + + srandom(time(NULL)); + + openzlog (di->progname, di->logname, di->instance, + LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); +#if defined(HAVE_CUMULUS) + zlog_set_level (NULL, ZLOG_DEST_SYSLOG, zlog_default->default_lvl); +#endif + + zprivs_init(di->privs); + + master = thread_master_create(); + signal_init(master, di->n_signals, di->signals); + + if (di->flags & FRR_LIMITED_CLI) + cmd_init(-1); + else + cmd_init(1); + vty_init(master); + memory_init(); + + return master; +} + +void frr_config_fork(void) +{ + if (di->instance) { + snprintf(config_default, sizeof(config_default), "%s/%s-%d.conf", + frr_sysconfdir, di->name, di->instance); + snprintf(pidfile_default, sizeof(pidfile_default), "%s/%s-%d.pid", + frr_vtydir, di->name, di->instance); + } + + vty_read_config(di->config_file, config_default); + + /* Don't start execution if we are in dry-run mode */ + if (di->dryrun) + exit(0); + + /* Daemonize. */ + if (di->daemon_mode && daemon (0, 0) < 0) { + zlog_err("Zebra daemon failed: %s", strerror(errno)); + exit(1); + } + + if (!di->pid_file) + di->pid_file = pidfile_default; + pid_output (di->pid_file); +} + +void frr_vty_serv(void) +{ + /* allow explicit override of vty_path in the future + * (not currently set anywhere) */ + if (!di->vty_path) { + const char *dir; + dir = di->vty_sock_path ? di->vty_sock_path : frr_vtydir; + + if (di->instance) + snprintf(vtypath_default, sizeof(vtypath_default), + "%s/%s-%d.vty", + dir, di->name, di->instance); + else + snprintf(vtypath_default, sizeof(vtypath_default), + "%s/%s.vty", dir, di->name); + + di->vty_path = vtypath_default; + } + + vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path); +} + +void frr_run(struct thread_master *master) +{ + char instanceinfo[64] = ""; + + frr_vty_serv(); + + if (di->instance) + snprintf(instanceinfo, sizeof(instanceinfo), "instance %u ", + di->instance); + + zlog_notice("%s %s starting: %svty@%d%s", + di->name, + FRR_VERSION, + instanceinfo, + di->vty_port, + di->startinfo); + + struct thread thread; + while (thread_fetch(master, &thread)) + thread_call(&thread); +} diff --git a/lib/libfrr.h b/lib/libfrr.h new file mode 100644 index 0000000000..d37f406f5b --- /dev/null +++ b/lib/libfrr.h @@ -0,0 +1,100 @@ +/* + * libfrr overall management functions + * + * Copyright (C) 2016 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZEBRA_FRR_H +#define _ZEBRA_FRR_H + +#include "sigevent.h" +#include "privs.h" +#include "thread.h" +#include "log.h" +#include "getopt.h" + +#define FRR_NO_PRIVSEP (1 << 0) +#define FRR_NO_TCPVTY (1 << 1) +#define FRR_LIMITED_CLI (1 << 2) +#define FRR_NO_CFG_PID_DRY (1 << 3) +#define FRR_NO_ZCLIENT (1 << 4) + +struct frr_daemon_info { + unsigned flags; + + const char *progname; + const char *name; + const char *logname; + unsigned short instance; + + char *vty_addr; + int vty_port; + char *vty_sock_path; + bool dryrun; + bool daemon_mode; + const char *config_file; + const char *pid_file; + const char *vty_path; + + const char *proghelp; + void (*printhelp)(FILE *target); + const char *copyright; + char startinfo[128]; + + struct quagga_signal_t *signals; + size_t n_signals; + + struct zebra_privs_t *privs; +}; + +/* execname is the daemon's executable (and pidfile and configfile) name, + * i.e. "zebra" or "bgpd" + * constname is the daemons source-level name, primarily for the logging ID, + * i.e. "ZEBRA" or "BGP" + * + * note that this macro is also a latch-on point for other changes (e.g. + * upcoming plugin support) that need to place some per-daemon things. Each + * daemon should have one of these. + */ +#define FRR_DAEMON_INFO(execname, constname, ...) \ + static struct frr_daemon_info execname ##_di = { \ + .name = # execname, \ + .logname = # constname, \ + __VA_ARGS__ \ + }; + +extern void frr_preinit(struct frr_daemon_info *daemon, + int argc, char **argv); +extern void frr_opt_add(const char *optstr, + const struct option *longopts, const char *helpstr); +extern int frr_getopt(int argc, char * const argv[], int *longindex); +extern void frr_help_exit(int status); + +extern struct thread_master *frr_init(void); + +extern void frr_config_fork(void); + +extern void frr_vty_serv(void); + +/* note: contains call to frr_vty_serv() */ +extern void frr_run(struct thread_master *master); + +extern char config_default[256]; +extern const char frr_sysconfdir[]; +extern const char frr_vtydir[]; + +#endif /* _ZEBRA_FRR_H */ @@ -24,7 +24,9 @@ #include <zebra.h> +#include "zclient.h" #include "log.h" +#include "log_int.h" #include "memory.h" #include "command.h" #ifndef SUNOS_5 @@ -41,28 +43,6 @@ static int logfile_fd = -1; /* Used in signal handler. */ struct zlog *zlog_default = NULL; -/* - * This must be kept in the same order as the - * zlog_proto_t enum - */ -const char *zlog_proto_names[] = -{ - "NONE", - "DEFAULT", - "ZEBRA", - "RIP", - "BGP", - "OSPF", - "RIPNG", - "OSPF6", - "LDP", - "ISIS", - "PIM", - "RFP", - "WATCHFRR", - NULL, -}; - const char *zlog_priority[] = { "emergencies", @@ -118,7 +98,6 @@ quagga_timestamp(int timestamp_precision, char *buf, size_t buflen) } cache; struct timeval clock; - /* would it be sufficient to use global 'recent_time' here? I fear not... */ gettimeofday(&clock, NULL); /* first, we update the cache if the time has changed */ @@ -184,16 +163,13 @@ time_print(FILE *fp, struct timestamp_control *ctl) /* va_list version of zlog. */ void -vzlog (struct zlog *zl, int priority, const char *format, va_list args) +vzlog (int priority, const char *format, va_list args) { char proto_str[32]; int original_errno = errno; struct timestamp_control tsctl; tsctl.already_rendered = 0; - - /* If zlog is not specified, use default one. */ - if (zl == NULL) - zl = zlog_default; + struct zlog *zl = zlog_default; /* When zlog_default is also NULL, use stderr for logging. */ if (zl == NULL) @@ -221,9 +197,9 @@ vzlog (struct zlog *zl, int priority, const char *format, va_list args) } if (zl->instance) - sprintf (proto_str, "%s[%d]: ", zlog_proto_names[zl->protocol], zl->instance); + sprintf (proto_str, "%s[%d]: ", zl->protoname, zl->instance); else - sprintf (proto_str, "%s: ", zlog_proto_names[zl->protocol]); + sprintf (proto_str, "%s: ", zl->protoname); /* File output. */ if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) @@ -264,11 +240,9 @@ vzlog (struct zlog *zl, int priority, const char *format, va_list args) } int -vzlog_test (struct zlog *zl, int priority) +vzlog_test (int priority) { - /* If zlog is not specified, use default one. */ - if (zl == NULL) - zl = zlog_default; + struct zlog *zl = zlog_default; /* When zlog_default is also NULL, use stderr for logging. */ if (zl == NULL) @@ -455,7 +429,7 @@ zlog_signal(int signo, const char *action time(&now); if (zlog_default) { - s = str_append(LOC,zlog_proto_names[zlog_default->protocol]); + s = str_append(LOC,zlog_default->protoname); *s++ = ':'; *s++ = ' '; msgstart = s; @@ -638,7 +612,7 @@ void zlog_backtrace(int priority) { #ifndef HAVE_GLIBC_BACKTRACE - zlog(NULL, priority, "No backtrace available on this platform."); + zlog(priority, "No backtrace available on this platform."); #else void *array[20]; int size, i; @@ -652,29 +626,29 @@ zlog_backtrace(int priority) size, (unsigned long)(array_size(array))); return; } - zlog(NULL, priority, "Backtrace for %d stack frames:", size); + zlog(priority, "Backtrace for %d stack frames:", size); if (!(strings = backtrace_symbols(array, size))) { zlog_err("Cannot get backtrace symbols (out of memory?)"); for (i = 0; i < size; i++) - zlog(NULL, priority, "[bt %d] %p",i,array[i]); + zlog(priority, "[bt %d] %p",i,array[i]); } else { for (i = 0; i < size; i++) - zlog(NULL, priority, "[bt %d] %s",i,strings[i]); + zlog(priority, "[bt %d] %s",i,strings[i]); free(strings); } #endif /* HAVE_GLIBC_BACKTRACE */ } void -zlog (struct zlog *zl, int priority, const char *format, ...) +zlog (int priority, const char *format, ...) { va_list args; va_start(args, format); - vzlog (zl, priority, format, args); + vzlog (priority, format, args); va_end (args); } @@ -684,7 +658,7 @@ FUNCNAME(const char *format, ...) \ { \ va_list args; \ va_start(args, format); \ - vzlog (NULL, PRIORITY, format, args); \ + vzlog (PRIORITY, format, args); \ va_end(args); \ } @@ -703,11 +677,11 @@ ZLOG_FUNC(zlog_debug, LOG_DEBUG) void zlog_thread_info (int log_level) { if (thread_current) - zlog(NULL, log_level, "Current thread function %s, scheduled from " + zlog(log_level, "Current thread function %s, scheduled from " "file %s, line %u", thread_current->funcname, thread_current->schedfrom, thread_current->schedfrom_line); else - zlog(NULL, log_level, "Current thread not known/applicable"); + zlog(log_level, "Current thread not known/applicable"); } void @@ -719,7 +693,7 @@ _zlog_assert_failed (const char *assertion, const char *file, ((logfile_fd = open_crashlog()) >= 0) && ((zlog_default->fp = fdopen(logfile_fd, "w")) != NULL)) zlog_default->maxlvl[ZLOG_DEST_FILE] = LOG_ERR; - zlog(NULL, LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s", + zlog(LOG_CRIT, "Assertion `%s' failed in file %s, line %u, function %s", assertion,file,line,(function ? function : "?")); zlog_backtrace(LOG_CRIT); zlog_thread_info(LOG_CRIT); @@ -737,8 +711,8 @@ memory_oom (size_t size, const char *name) } /* Open log stream */ -struct zlog * -openzlog (const char *progname, zlog_proto_t protocol, u_short instance, +void +openzlog (const char *progname, const char *protoname, u_short instance, int syslog_flags, int syslog_facility) { struct zlog *zl; @@ -747,7 +721,7 @@ openzlog (const char *progname, zlog_proto_t protocol, u_short instance, zl = XCALLOC(MTYPE_ZLOG, sizeof (struct zlog)); zl->ident = progname; - zl->protocol = protocol; + zl->protoname = protoname; zl->instance = instance; zl->facility = syslog_facility; zl->syslog_options = syslog_flags; @@ -759,13 +733,14 @@ openzlog (const char *progname, zlog_proto_t protocol, u_short instance, zl->default_lvl = LOG_DEBUG; openlog (progname, syslog_flags, zl->facility); - - return zl; + zlog_default = zl; } void -closezlog (struct zlog *zl) +closezlog (void) { + struct zlog *zl = zlog_default; + closelog(); if (zl->fp != NULL) @@ -775,30 +750,31 @@ closezlog (struct zlog *zl) XFREE(MTYPE_ZLOG, zl->filename); XFREE (MTYPE_ZLOG, zl); + zlog_default = NULL; +} + +const char * +zlog_protoname (void) +{ + return zlog_default ? zlog_default->protoname : "NONE"; } /* Called from command.c. */ void -zlog_set_level (struct zlog *zl, zlog_dest_t dest, int log_level) +zlog_set_level (zlog_dest_t dest, int log_level) { - if (zl == NULL) - zl = zlog_default; - - zl->maxlvl[dest] = log_level; + zlog_default->maxlvl[dest] = log_level; } int -zlog_set_file (struct zlog *zl, const char *filename, int log_level) +zlog_set_file (const char *filename, int log_level) { + struct zlog *zl = zlog_default; FILE *fp; mode_t oldumask; /* There is opend file. */ - zlog_reset_file (zl); - - /* Set default zl. */ - if (zl == NULL) - zl = zlog_default; + zlog_reset_file (); /* Open file. */ oldumask = umask (0777 & ~LOGFILE_MASK); @@ -818,10 +794,9 @@ zlog_set_file (struct zlog *zl, const char *filename, int log_level) /* Reset opend file. */ int -zlog_reset_file (struct zlog *zl) +zlog_reset_file (void) { - if (zl == NULL) - zl = zlog_default; + struct zlog *zl = zlog_default; if (zl->fp) fclose (zl->fp); @@ -838,13 +813,11 @@ zlog_reset_file (struct zlog *zl) /* Reopen log file. */ int -zlog_rotate (struct zlog *zl) +zlog_rotate (void) { + struct zlog *zl = zlog_default; int level; - if (zl == NULL) - zl = zlog_default; - if (zl->fp) fclose (zl->fp); zl->fp = NULL; @@ -990,6 +963,7 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY (ZEBRA_IPV4_NEXTHOP_DELETE), DESC_ENTRY (ZEBRA_IPV6_NEXTHOP_ADD), DESC_ENTRY (ZEBRA_IPV6_NEXTHOP_DELETE), + DESC_ENTRY (ZEBRA_IPMR_ROUTE_STATS), }; #undef DESC_ENTRY @@ -1062,49 +1036,53 @@ proto_redistnum(int afi, const char *s) if (afi == AFI_IP) { - if (strncmp (s, "k", 1) == 0) + if (strmatch (s, "kernel")) return ZEBRA_ROUTE_KERNEL; - else if (strncmp (s, "c", 1) == 0) + else if (strmatch (s, "connected")) return ZEBRA_ROUTE_CONNECT; - else if (strncmp (s, "s", 1) == 0) + else if (strmatch (s, "static")) return ZEBRA_ROUTE_STATIC; - else if (strncmp (s, "r", 1) == 0) + else if (strmatch (s, "rip")) return ZEBRA_ROUTE_RIP; - else if (strncmp (s, "o", 1) == 0) + else if (strmatch (s, "ospf")) return ZEBRA_ROUTE_OSPF; - else if (strncmp (s, "i", 1) == 0) + else if (strmatch (s, "isis")) return ZEBRA_ROUTE_ISIS; - else if (strncmp (s, "bg", 2) == 0) + else if (strmatch (s, "bgp")) return ZEBRA_ROUTE_BGP; - else if (strncmp (s, "ta", 2) == 0) + else if (strmatch (s, "table")) return ZEBRA_ROUTE_TABLE; - else if (strcmp (s, "vnc-direct") == 0) - return ZEBRA_ROUTE_VNC_DIRECT; - else if (strcmp (s, "vnc") == 0) + else if (strmatch (s, "vnc")) return ZEBRA_ROUTE_VNC; + else if (strmatch (s, "vnc-direct")) + return ZEBRA_ROUTE_VNC_DIRECT; + else if (strncmp (s, "n", 1) == 0) + return ZEBRA_ROUTE_NHRP; } if (afi == AFI_IP6) { - if (strncmp (s, "k", 1) == 0) + if (strmatch (s, "kernel")) return ZEBRA_ROUTE_KERNEL; - else if (strncmp (s, "c", 1) == 0) + else if (strmatch (s, "connected")) return ZEBRA_ROUTE_CONNECT; - else if (strncmp (s, "s", 1) == 0) + else if (strmatch (s, "static")) return ZEBRA_ROUTE_STATIC; - else if (strncmp (s, "r", 1) == 0) + else if (strmatch (s, "ripng")) return ZEBRA_ROUTE_RIPNG; - else if (strncmp (s, "o", 1) == 0) + else if (strmatch (s, "ospf6")) return ZEBRA_ROUTE_OSPF6; - else if (strncmp (s, "i", 1) == 0) + else if (strmatch (s, "isis")) return ZEBRA_ROUTE_ISIS; - else if (strncmp (s, "bg", 2) == 0) + else if (strmatch (s, "bgp")) return ZEBRA_ROUTE_BGP; - else if (strncmp (s, "ta", 2) == 0) + else if (strmatch (s, "table")) return ZEBRA_ROUTE_TABLE; - else if (strcmp (s, "vnc-direct") == 0) - return ZEBRA_ROUTE_VNC_DIRECT; - else if (strcmp (s, "vnc") == 0) + else if (strmatch (s, "vnc")) return ZEBRA_ROUTE_VNC; + else if (strmatch (s, "vnc-direct")) + return ZEBRA_ROUTE_VNC_DIRECT; + else if (strncmp (s, "n", 1) == 0) + return ZEBRA_ROUTE_NHRP; } return -1; } @@ -1150,3 +1128,33 @@ zlog_hexdump (const void *mem, unsigned int len) { } zlog_debug("\n%s", buf); } + +const char * +zlog_sanitize (char *buf, size_t bufsz, const void *in, size_t inlen) +{ + const char *inbuf = in; + char *pos = buf, *end = buf + bufsz; + const char *iend = inbuf + inlen; + + memset (buf, 0, bufsz); + for (; inbuf < iend; inbuf++) + { + /* don't write partial escape sequence */ + if (end - pos < 5) + break; + + if (*inbuf == '\n') + snprintf (pos, end - pos, "\\n"); + else if (*inbuf == '\r') + snprintf (pos, end - pos, "\\r"); + else if (*inbuf == '\t') + snprintf (pos, end - pos, "\\t"); + else if (*inbuf < ' ' || *inbuf == '"' || *inbuf >= 127) + snprintf (pos, end - pos, "\\x%02hhx", *inbuf); + else + *pos = *inbuf; + + pos += strlen (pos); + } + return buf; +} @@ -24,6 +24,7 @@ #define _ZEBRA_LOG_H #include <syslog.h> +#include <stdio.h> /* Here is some guidance on logging levels to use: * @@ -41,27 +42,6 @@ * please use LOG_ERR instead. */ -/* - * This must be kept in the same order as - * zlog_proto_names[] - */ -typedef enum -{ - ZLOG_NONE, - ZLOG_DEFAULT, - ZLOG_ZEBRA, - ZLOG_RIP, - ZLOG_BGP, - ZLOG_OSPF, - ZLOG_RIPNG, - ZLOG_OSPF6, - ZLOG_LDP, - ZLOG_ISIS, - ZLOG_PIM, - ZLOG_RFP, - ZLOG_WATCHFRR, -} zlog_proto_t; - /* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent to that logging destination. */ #define ZLOG_DISABLED (LOG_EMERG-1) @@ -75,23 +55,6 @@ typedef enum } zlog_dest_t; #define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1) -struct zlog -{ - const char *ident; /* daemon name (first arg to openlog) */ - zlog_proto_t protocol; - u_short instance; - int maxlvl[ZLOG_NUM_DESTS]; /* maximum priority to send to associated - logging destination */ - int default_lvl; /* maxlvl to use if none is specified */ - FILE *fp; - char *filename; - int facility; /* as per syslog facility */ - int record_priority; /* should messages logged through stdio include the - priority of the message? */ - int syslog_options; /* 2nd arg to openlog */ - int timestamp_precision; /* # of digits of subsecond precision */ -}; - /* Message structure. */ struct message { @@ -99,15 +62,14 @@ struct message const char *str; }; -/* Default logging strucutre. */ -extern struct zlog *zlog_default; - /* Open zlog function */ -extern struct zlog *openzlog (const char *progname, zlog_proto_t protocol, - u_short instance, int syslog_options, int syslog_facility); +extern void openzlog (const char *progname, const char *protoname, + u_short instance, int syslog_options, int syslog_facility); /* Close zlog function. */ -extern void closezlog (struct zlog *zl); +extern void closezlog (void); + +extern const char *zlog_protoname (void); /* GCC have printf type attribute check. */ #ifdef __GNUC__ @@ -116,35 +78,28 @@ extern void closezlog (struct zlog *zl); #define PRINTF_ATTRIBUTE(a,b) #endif /* __GNUC__ */ -/* Generic function for zlog. */ -extern void zlog (struct zlog *zl, int priority, const char *format, ...) - PRINTF_ATTRIBUTE(3, 4); - /* Handy zlog functions. */ -extern void vzlog (struct zlog *zl, int priority, const char *format, va_list args); extern void zlog_err (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_warn (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_info (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_notice (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); extern void zlog_debug (const char *format, ...) PRINTF_ATTRIBUTE(1, 2); -extern void vzlog (struct zlog *, int , const char *, va_list ); - extern void zlog_thread_info (int log_level); /* Set logging level for the given destination. If the log_level argument is ZLOG_DISABLED, then the destination is disabled. This function should not be used for file logging (use zlog_set_file or zlog_reset_file instead). */ -extern void zlog_set_level (struct zlog *zl, zlog_dest_t, int log_level); +extern void zlog_set_level (zlog_dest_t, int log_level); /* Set logging to the given filename at the specified level. */ -extern int zlog_set_file (struct zlog *zl, const char *filename, int log_level); +extern int zlog_set_file (const char *filename, int log_level); /* Disable file logging. */ -extern int zlog_reset_file (struct zlog *zl); +extern int zlog_reset_file (void); /* Rotate log. */ -extern int zlog_rotate (struct zlog *); +extern int zlog_rotate (void); /* For hackey message lookup and check */ #define LOOKUP_DEF(x, y, def) mes_lookup(x, x ## _max, y, def, #x) @@ -155,9 +110,6 @@ extern const char *mes_lookup (const struct message *meslist, int max, int index, const char *no_item, const char *mesname); -extern const char *zlog_priority[]; -extern const char *zlog_proto_names[]; - /* Safe version of strerror -- never returns NULL. */ extern const char *safe_strerror(int errnum); @@ -188,10 +140,10 @@ extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */, char *buf, size_t buflen); extern void zlog_hexdump(const void *mem, unsigned int len); +extern const char *zlog_sanitize(char *buf, size_t bufsz, const void *in, size_t inlen); -extern int -vzlog_test (struct zlog *zl, int priority); +extern int vzlog_test (int priority); /* structure useful for avoiding repeated rendering of the same timestamp */ struct timestamp_control { @@ -201,12 +153,8 @@ struct timestamp_control { char buf[QUAGGA_TIMESTAMP_LEN]; /* will contain the rendered timestamp */ }; -#define LOG_DEFAULT_FILENAME "/var/log/quagga/Quagga.log" - /* Defines for use in command construction: */ -#define LOG_LEVELS "(emergencies|alerts|critical|errors|warnings|notifications|informational|debugging)" - #define LOG_LEVEL_DESC \ "System is unusable\n" \ "Immediate action needed\n" \ @@ -217,8 +165,6 @@ struct timestamp_control { "Informational messages\n" \ "Debugging messages\n" -#define LOG_FACILITIES "(kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7)" - #define LOG_FACILITY_DESC \ "Kernel\n" \ "User process\n" \ diff --git a/lib/log_int.h b/lib/log_int.h new file mode 100644 index 0000000000..c21d723ac6 --- /dev/null +++ b/lib/log_int.h @@ -0,0 +1,57 @@ +/* + * Zebra logging funcions. + * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_LOG_PRIVATE_H +#define _ZEBRA_LOG_PRIVATE_H + +#include "log.h" + +struct zlog +{ + const char *ident; /* daemon name (first arg to openlog) */ + const char *protoname; + u_short instance; + int maxlvl[ZLOG_NUM_DESTS]; /* maximum priority to send to associated + logging destination */ + int default_lvl; /* maxlvl to use if none is specified */ + FILE *fp; + char *filename; + int facility; /* as per syslog facility */ + int record_priority; /* should messages logged through stdio include the + priority of the message? */ + int syslog_options; /* 2nd arg to openlog */ + int timestamp_precision; /* # of digits of subsecond precision */ +}; + +/* Default logging strucutre. */ +extern struct zlog *zlog_default; + +extern const char *zlog_priority[]; + +/* Generic function for zlog. */ +extern void vzlog (int priority, const char *format, va_list args); +extern void zlog (int priority, const char *format, ...) + PRINTF_ATTRIBUTE(2, 3); + +#endif /* _ZEBRA_LOG_PRIVATE_H */ + + diff --git a/lib/memory.c b/lib/memory.c index 9ec564151a..ad55366f64 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -41,11 +41,7 @@ mt_count_alloc (struct memtype *mt, size_t size) static inline void mt_count_free (struct memtype *mt) { - if (mt->n_alloc == 0) - { - zlog_err ("memory allocation count underflow for \"%s\"", mt->name); - zlog_backtrace (LOG_ERR); - } + assert(mt->n_alloc); mt->n_alloc--; } diff --git a/lib/monotime.h b/lib/monotime.h new file mode 100644 index 0000000000..ad44bf511a --- /dev/null +++ b/lib/monotime.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_MONOTIME_H +#define _FRR_MONOTIME_H + +#include <stdint.h> +#include <time.h> +#include <sys/time.h> + +#ifndef TIMESPEC_TO_TIMEVAL +/* should be in sys/time.h on BSD & Linux libcs */ +#define TIMESPEC_TO_TIMEVAL(tv, ts) do { \ + (tv)->tv_sec = (ts)->tv_sec; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000; \ + } while (0) +#endif +#ifndef TIMEVAL_TO_TIMESPEC +/* should be in sys/time.h on BSD & Linux libcs */ +#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ + } while (0) +#endif + +static inline time_t monotime(struct timeval *tvo) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + if (tvo) { + TIMESPEC_TO_TIMEVAL(tvo, &ts); + } + return ts.tv_sec; +} + +/* the following two return microseconds, not time_t! + * + * also, they're negative forms of each other, but having both makes the + * code more readable + */ +static inline int64_t monotime_since(const struct timeval *ref, + struct timeval *out) +{ + struct timeval tv; + monotime(&tv); + timersub(&tv, ref, &tv); + if (out) + *out = tv; + return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; +} + +static inline int64_t monotime_until(const struct timeval *ref, + struct timeval *out) +{ + struct timeval tv; + monotime(&tv); + timersub(ref, &tv, &tv); + if (out) + *out = tv; + return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; +} + +#endif /* _FRR_MONOTIME_H */ diff --git a/lib/mpls.h b/lib/mpls.h index 1f77aaa536..b5c1a653b1 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -123,59 +123,40 @@ mpls_lse_decode (mpls_lse_t lse, mpls_label_t *label, /* Printable string for labels (with consideration for reserved values). */ static inline char * -label2str (mpls_label_t label, char *buf, int len) +label2str (mpls_label_t label, char *buf, size_t len) { switch(label) { case MPLS_V4_EXP_NULL_LABEL: - strncpy(buf, "IPv4 Explicit Null", len); + strlcpy(buf, "IPv4 Explicit Null", len); return(buf); - break; case MPLS_RA_LABEL: - strncpy(buf, "Router Alert", len); + strlcpy(buf, "Router Alert", len); return(buf); - break; case MPLS_V6_EXP_NULL_LABEL: - strncpy(buf, "IPv6 Explict Null", len); + strlcpy(buf, "IPv6 Explict Null", len); return(buf); - break; case MPLS_IMP_NULL_LABEL: - strncpy(buf, "implicit-null", len); + strlcpy(buf, "implicit-null", len); return(buf); - break; case MPLS_ENTROPY_LABEL_INDICATOR: - strncpy(buf, "Entropy Label Indicator", len); + strlcpy(buf, "Entropy Label Indicator", len); return(buf); - break; case MPLS_GAL_LABEL: - strncpy(buf, "Generic Associated Channel", len); + strlcpy(buf, "Generic Associated Channel", len); return(buf); - break; case MPLS_OAM_ALERT_LABEL: - strncpy(buf, "OAM Alert", len); + strlcpy(buf, "OAM Alert", len); return(buf); - break; case MPLS_EXTENSION_LABEL: - strncpy(buf, "Extension", len); + strlcpy(buf, "Extension", len); return(buf); - break; - case 4: - case 5: - case 6: - case 8: - case 9: - case 10: - case 11: - case 12: - strncpy(buf, "Reserved", len); - return(buf); - break; default: - sprintf(buf, "%u", label); + if (label < 16) + snprintf(buf, len, "Reserved (%u)", label); + else + snprintf(buf, len, "%u", label); return(buf); } - - strncpy(buf, "Error", len); - return(buf); } /* constants used by ldpd */ diff --git a/lib/network.c b/lib/network.c index 506e019136..2b6f2fbab5 100644 --- a/lib/network.c +++ b/lib/network.c @@ -62,8 +62,13 @@ writen(int fd, const u_char *ptr, int nbytes) while (nleft > 0) { nwritten = write(fd, ptr, nleft); - - if (nwritten <= 0) + + if (nwritten < 0) + { + if (!ERRNO_IO_RETRY(errno)) + return nwritten; + } + if (nwritten == 0) return (nwritten); nleft -= nwritten; @@ -306,20 +306,22 @@ ns_netns_pathname (struct vty *vty, const char *name) DEFUN (ns_netns, ns_netns_cmd, - "logical-router <1-65535> ns NAME", + "logical-router (1-65535) ns NAME", "Enable a logical-router\n" "Specify the logical-router indentifier\n" "The Name Space\n" "The file name in " NS_RUN_DIR ", or a full pathname\n") { + int idx_number = 1; + int idx_name = 3; ns_id_t ns_id = NS_DEFAULT; struct ns *ns = NULL; - char *pathname = ns_netns_pathname (vty, argv[1]); + char *pathname = ns_netns_pathname (vty, argv[idx_name]->arg); if (!pathname) return CMD_WARNING; - VTY_GET_INTEGER ("NS ID", ns_id, argv[0]); + VTY_GET_INTEGER ("NS ID", ns_id, argv[idx_number]->arg); ns = ns_get (ns_id); if (ns->name && strcmp (ns->name, pathname) != 0) @@ -344,21 +346,23 @@ DEFUN (ns_netns, DEFUN (no_ns_netns, no_ns_netns_cmd, - "no logical-router <1-65535> ns NAME", + "no logical-router (1-65535) ns NAME", NO_STR "Enable a Logical-Router\n" "Specify the Logical-Router identifier\n" "The Name Space\n" "The file name in " NS_RUN_DIR ", or a full pathname\n") { + int idx_number = 2; + int idx_name = 4; ns_id_t ns_id = NS_DEFAULT; struct ns *ns = NULL; - char *pathname = ns_netns_pathname (vty, argv[1]); + char *pathname = ns_netns_pathname (vty, argv[idx_name]->arg); if (!pathname) return CMD_WARNING; - VTY_GET_INTEGER ("NS ID", ns_id, argv[0]); + VTY_GET_INTEGER ("NS ID", ns_id, argv[idx_number]->arg); ns = ns_lookup (ns_id); if (!ns) diff --git a/lib/plist.c b/lib/plist.c index 6f19565688..3714969696 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -20,7 +20,6 @@ */ #include <zebra.h> -#include "lib/json.h" #include "prefix.h" #include "command.h" @@ -28,9 +27,9 @@ #include "plist.h" #include "sockunion.h" #include "buffer.h" -#include "stream.h" #include "log.h" #include "routemap.h" +#include "lib/json.h" #include "plist_int.h" @@ -103,7 +102,6 @@ static struct prefix_master prefix_master_ipv4 = PLC_MAXLEVELV4, }; -#ifdef HAVE_IPV6 /* Static structure of IPv6 prefix-list's master. */ static struct prefix_master prefix_master_ipv6 = { @@ -115,7 +113,6 @@ static struct prefix_master prefix_master_ipv6 = NULL, PLC_MAXLEVELV6, }; -#endif /* HAVE_IPV6*/ /* Static structure of BGP ORF prefix_list's master. */ static struct prefix_master prefix_master_orf_v4 = @@ -412,9 +409,7 @@ void prefix_list_add_hook (void (*func) (struct prefix_list *plist)) { prefix_master_ipv4.add_hook = func; -#ifdef HAVE_IPV6 prefix_master_ipv6.add_hook = func; -#endif /* HAVE_IPV6 */ } /* Delete hook function. */ @@ -422,9 +417,7 @@ void prefix_list_delete_hook (void (*func) (struct prefix_list *plist)) { prefix_master_ipv4.delete_hook = func; -#ifdef HAVE_IPV6 prefix_master_ipv6.delete_hook = func; -#endif /* HAVE_IPVt6 */ } /* Calculate new sequential number. */ @@ -563,10 +556,11 @@ prefix_list_entry_delete (struct prefix_list *plist, struct prefix_list_entry *pentry, int update_list) { - prefix_list_trie_del (plist, pentry); - if (plist == NULL || pentry == NULL) return; + + prefix_list_trie_del (plist, pentry); + if (pentry->prev) pentry->prev->next = pentry->next; else @@ -977,7 +971,7 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, apply_mask_ipv6 ((struct prefix_ipv6 *) &p_tmp); break; - case AFI_ETHER: + case AFI_L2VPN: default: vty_out (vty, "%% Unrecognized AFI (%d)%s", afi, VTY_NEWLINE); return CMD_WARNING; @@ -1106,7 +1100,6 @@ vty_prefix_list_uninstall (struct vty *vty, afi_t afi, const char *name, return CMD_WARNING; } } -#ifdef HAVE_IPV6 else if (afi == AFI_IP6) { if (strncmp ("any", prefix, strlen (prefix)) == 0) @@ -1124,7 +1117,6 @@ vty_prefix_list_uninstall (struct vty *vty, afi_t afi, const char *name, return CMD_WARNING; } } -#endif /* HAVE_IPV6 */ /* Lookup prefix entry. */ pentry = prefix_list_entry_lookup(plist, &p, type, seqnum, lenum, genum); @@ -1183,8 +1175,7 @@ vty_show_prefix_entry (struct vty *vty, afi_t afi, struct prefix_list *plist, struct prefix_list_entry *pentry; /* Print the name of the protocol */ - if (zlog_default) - vty_out (vty, "%s: ", zlog_proto_names[zlog_default->protocol]); + vty_out(vty, "%s: ", zlog_protoname()); if (dtype == normal_display) { @@ -1424,7 +1415,7 @@ vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name, DEFUN (ip_prefix_list, ip_prefix_list_cmd, - "ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)", + "ip prefix-list WORD <deny|permit> <A.B.C.D/M|any>", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -1433,13 +1424,16 @@ DEFUN (ip_prefix_list, "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") { - return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, - argv[1], argv[2], NULL, NULL); + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv4_any = 4; + return vty_prefix_list_install (vty, AFI_IP, argv[idx_word]->arg, NULL, + argv[idx_permit_deny]->arg, argv[idx_ipv4_any]->arg, NULL, NULL); } DEFUN (ip_prefix_list_ge, ip_prefix_list_ge_cmd, - "ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32>", + "ip prefix-list WORD <deny|permit> A.B.C.D/M ge (0-32)", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -1449,13 +1443,17 @@ DEFUN (ip_prefix_list_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], - argv[2], argv[3], NULL); + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv4_prefixlen = 4; + int idx_number = 6; + return vty_prefix_list_install (vty, AFI_IP, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number]->arg, NULL); } DEFUN (ip_prefix_list_ge_le, ip_prefix_list_ge_le_cmd, - "ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + "ip prefix-list WORD <deny|permit> A.B.C.D/M ge (0-32) le (0-32)", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -1467,13 +1465,18 @@ DEFUN (ip_prefix_list_ge_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], - argv[2], argv[3], argv[4]); + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv4_prefixlen = 4; + int idx_number = 6; + int idx_number_2 = 8; + return vty_prefix_list_install (vty, AFI_IP, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number]->arg, argv[idx_number_2]->arg); } DEFUN (ip_prefix_list_le, ip_prefix_list_le_cmd, - "ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32>", + "ip prefix-list WORD <deny|permit> A.B.C.D/M le (0-32)", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -1483,13 +1486,17 @@ DEFUN (ip_prefix_list_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], - argv[2], NULL, argv[3]); + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv4_prefixlen = 4; + int idx_number = 6; + return vty_prefix_list_install (vty, AFI_IP, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, NULL, argv[idx_number]->arg); } DEFUN (ip_prefix_list_le_ge, ip_prefix_list_le_ge_cmd, - "ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + "ip prefix-list WORD <deny|permit> A.B.C.D/M le (0-32) ge (0-32)", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -1501,13 +1508,18 @@ DEFUN (ip_prefix_list_le_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP, argv[0], NULL, argv[1], - argv[2], argv[4], argv[3]); + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv4_prefixlen = 4; + int idx_number = 6; + int idx_number_2 = 8; + return vty_prefix_list_install (vty, AFI_IP, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number_2]->arg, argv[idx_number]->arg); } DEFUN (ip_prefix_list_seq, ip_prefix_list_seq_cmd, - "ip prefix-list WORD seq <1-4294967295> (deny|permit) (A.B.C.D/M|any)", + "ip prefix-list WORD seq (1-4294967295) <deny|permit> <A.B.C.D/M|any>", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -1518,13 +1530,17 @@ DEFUN (ip_prefix_list_seq, "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") { - return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], - argv[3], NULL, NULL); + int idx_word = 2; + int idx_number = 4; + int idx_permit_deny = 5; + int idx_ipv4_any = 6; + return vty_prefix_list_install (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv4_any]->arg, NULL, NULL); } DEFUN (ip_prefix_list_seq_ge, ip_prefix_list_seq_ge_cmd, - "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32>", + "ip prefix-list WORD seq (1-4294967295) <deny|permit> A.B.C.D/M ge (0-32)", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -1536,13 +1552,18 @@ DEFUN (ip_prefix_list_seq_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], - argv[3], argv[4], NULL); + int idx_word = 2; + int idx_number = 4; + int idx_permit_deny = 5; + int idx_ipv4_prefixlen = 6; + int idx_number_2 = 8; + return vty_prefix_list_install (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number_2]->arg, NULL); } DEFUN (ip_prefix_list_seq_ge_le, ip_prefix_list_seq_ge_le_cmd, - "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + "ip prefix-list WORD seq (1-4294967295) <deny|permit> A.B.C.D/M ge (0-32) le (0-32)", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -1556,13 +1577,19 @@ DEFUN (ip_prefix_list_seq_ge_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], - argv[3], argv[4], argv[5]); + int idx_word = 2; + int idx_number = 4; + int idx_permit_deny = 5; + int idx_ipv4_prefixlen = 6; + int idx_number_2 = 8; + int idx_number_3 = 10; + return vty_prefix_list_install (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number_2]->arg, argv[idx_number_3]->arg); } DEFUN (ip_prefix_list_seq_le, ip_prefix_list_seq_le_cmd, - "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32>", + "ip prefix-list WORD seq (1-4294967295) <deny|permit> A.B.C.D/M le (0-32)", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -1574,13 +1601,18 @@ DEFUN (ip_prefix_list_seq_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], - argv[3], NULL, argv[4]); + int idx_word = 2; + int idx_number = 4; + int idx_permit_deny = 5; + int idx_ipv4_prefixlen = 6; + int idx_number_2 = 8; + return vty_prefix_list_install (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, NULL, argv[idx_number_2]->arg); } DEFUN (ip_prefix_list_seq_le_ge, ip_prefix_list_seq_le_ge_cmd, - "ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + "ip prefix-list WORD seq (1-4294967295) <deny|permit> A.B.C.D/M le (0-32) ge (0-32)", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -1594,8 +1626,14 @@ DEFUN (ip_prefix_list_seq_le_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP, argv[0], argv[1], argv[2], - argv[3], argv[5], argv[4]); + int idx_word = 2; + int idx_number = 4; + int idx_permit_deny = 5; + int idx_ipv4_prefixlen = 6; + int idx_number_2 = 8; + int idx_number_3 = 10; + return vty_prefix_list_install (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number_3]->arg, argv[idx_number_2]->arg); } DEFUN (no_ip_prefix_list, @@ -1606,13 +1644,14 @@ DEFUN (no_ip_prefix_list, PREFIX_LIST_STR "Name of a prefix list\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, NULL, + int idx_word = 3; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, NULL, NULL, NULL, NULL, NULL); } DEFUN (no_ip_prefix_list_prefix, no_ip_prefix_list_prefix_cmd, - "no ip prefix-list WORD (deny|permit) (A.B.C.D/M|any)", + "no ip prefix-list WORD <deny|permit> <A.B.C.D/M|any>", NO_STR IP_STR PREFIX_LIST_STR @@ -1622,13 +1661,16 @@ DEFUN (no_ip_prefix_list_prefix, "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], - argv[2], NULL, NULL); + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv4_any = 5; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv4_any]->arg, NULL, NULL); } DEFUN (no_ip_prefix_list_ge, no_ip_prefix_list_ge_cmd, - "no ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32>", + "no ip prefix-list WORD <deny|permit> A.B.C.D/M ge (0-32)", NO_STR IP_STR PREFIX_LIST_STR @@ -1639,13 +1681,17 @@ DEFUN (no_ip_prefix_list_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], - argv[2], argv[3], NULL); + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv4_prefixlen = 5; + int idx_number = 7; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number]->arg, NULL); } DEFUN (no_ip_prefix_list_ge_le, no_ip_prefix_list_ge_le_cmd, - "no ip prefix-list WORD (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + "no ip prefix-list WORD <deny|permit> A.B.C.D/M ge (0-32) le (0-32)", NO_STR IP_STR PREFIX_LIST_STR @@ -1658,13 +1704,18 @@ DEFUN (no_ip_prefix_list_ge_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], - argv[2], argv[3], argv[4]); + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv4_prefixlen = 5; + int idx_number = 7; + int idx_number_2 = 9; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number]->arg, argv[idx_number_2]->arg); } DEFUN (no_ip_prefix_list_le, no_ip_prefix_list_le_cmd, - "no ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32>", + "no ip prefix-list WORD <deny|permit> A.B.C.D/M le (0-32)", NO_STR IP_STR PREFIX_LIST_STR @@ -1675,13 +1726,17 @@ DEFUN (no_ip_prefix_list_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], - argv[2], NULL, argv[3]); + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv4_prefixlen = 5; + int idx_number = 7; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, NULL, argv[idx_number]->arg); } DEFUN (no_ip_prefix_list_le_ge, no_ip_prefix_list_le_ge_cmd, - "no ip prefix-list WORD (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + "no ip prefix-list WORD <deny|permit> A.B.C.D/M le (0-32) ge (0-32)", NO_STR IP_STR PREFIX_LIST_STR @@ -1694,13 +1749,18 @@ DEFUN (no_ip_prefix_list_le_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], NULL, argv[1], - argv[2], argv[4], argv[3]); + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv4_prefixlen = 5; + int idx_number = 7; + int idx_number_2 = 9; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number_2]->arg, argv[idx_number]->arg); } DEFUN (no_ip_prefix_list_seq, no_ip_prefix_list_seq_cmd, - "no ip prefix-list WORD seq <1-4294967295> (deny|permit) (A.B.C.D/M|any)", + "no ip prefix-list WORD seq (1-4294967295) <deny|permit> <A.B.C.D/M|any>", NO_STR IP_STR PREFIX_LIST_STR @@ -1712,13 +1772,17 @@ DEFUN (no_ip_prefix_list_seq, "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" "Any prefix match. Same as \"0.0.0.0/0 le 32\"\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], - argv[3], NULL, NULL); + int idx_word = 3; + int idx_number = 5; + int idx_permit_deny = 6; + int idx_ipv4_any = 7; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv4_any]->arg, NULL, NULL); } DEFUN (no_ip_prefix_list_seq_ge, no_ip_prefix_list_seq_ge_cmd, - "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32>", + "no ip prefix-list WORD seq (1-4294967295) <deny|permit> A.B.C.D/M ge (0-32)", NO_STR IP_STR PREFIX_LIST_STR @@ -1731,13 +1795,18 @@ DEFUN (no_ip_prefix_list_seq_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], - argv[3], argv[4], NULL); + int idx_word = 3; + int idx_number = 5; + int idx_permit_deny = 6; + int idx_ipv4_prefixlen = 7; + int idx_number_2 = 9; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number_2]->arg, NULL); } DEFUN (no_ip_prefix_list_seq_ge_le, no_ip_prefix_list_seq_ge_le_cmd, - "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M ge <0-32> le <0-32>", + "no ip prefix-list WORD seq (1-4294967295) <deny|permit> A.B.C.D/M ge (0-32) le (0-32)", NO_STR IP_STR PREFIX_LIST_STR @@ -1752,13 +1821,19 @@ DEFUN (no_ip_prefix_list_seq_ge_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], - argv[3], argv[4], argv[5]); + int idx_word = 3; + int idx_number = 5; + int idx_permit_deny = 6; + int idx_ipv4_prefixlen = 7; + int idx_number_2 = 9; + int idx_number_3 = 11; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number_2]->arg, argv[idx_number_3]->arg); } DEFUN (no_ip_prefix_list_seq_le, no_ip_prefix_list_seq_le_cmd, - "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32>", + "no ip prefix-list WORD seq (1-4294967295) <deny|permit> A.B.C.D/M le (0-32)", NO_STR IP_STR PREFIX_LIST_STR @@ -1771,13 +1846,18 @@ DEFUN (no_ip_prefix_list_seq_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], - argv[3], NULL, argv[4]); + int idx_word = 3; + int idx_number = 5; + int idx_permit_deny = 6; + int idx_ipv4_prefixlen = 7; + int idx_number_2 = 9; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, NULL, argv[idx_number_2]->arg); } DEFUN (no_ip_prefix_list_seq_le_ge, no_ip_prefix_list_seq_le_ge_cmd, - "no ip prefix-list WORD seq <1-4294967295> (deny|permit) A.B.C.D/M le <0-32> ge <0-32>", + "no ip prefix-list WORD seq (1-4294967295) <deny|permit> A.B.C.D/M le (0-32) ge (0-32)", NO_STR IP_STR PREFIX_LIST_STR @@ -1792,8 +1872,14 @@ DEFUN (no_ip_prefix_list_seq_le_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP, argv[0], argv[1], argv[2], - argv[3], argv[5], argv[4]); + int idx_word = 3; + int idx_number = 5; + int idx_permit_deny = 6; + int idx_ipv4_prefixlen = 7; + int idx_number_2 = 9; + int idx_number_3 = 11; + return vty_prefix_list_uninstall (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv4_prefixlen]->arg, argv[idx_number_3]->arg, argv[idx_number_2]->arg); } DEFUN (ip_prefix_list_sequence_number, @@ -1821,26 +1907,28 @@ DEFUN (no_ip_prefix_list_sequence_number, DEFUN (ip_prefix_list_description, ip_prefix_list_description_cmd, - "ip prefix-list WORD description .LINE", + "ip prefix-list WORD description LINE...", IP_STR PREFIX_LIST_STR "Name of a prefix list\n" "Prefix-list specific description\n" "Up to 80 characters describing this prefix-list\n") { + int idx_word = 2; + int idx_line = 4; struct prefix_list *plist; - plist = prefix_list_get (AFI_IP, 0, argv[0]); + plist = prefix_list_get (AFI_IP, 0, argv[idx_word]->arg); if (plist->desc) { XFREE (MTYPE_TMP, plist->desc); plist->desc = NULL; } - plist->desc = argv_concat(argv, argc, 1); + plist->desc = argv_concat(argv, argc, idx_line); return CMD_SUCCESS; -} +} DEFUN (no_ip_prefix_list_description, no_ip_prefix_list_description_cmd, @@ -1851,18 +1939,23 @@ DEFUN (no_ip_prefix_list_description, "Name of a prefix list\n" "Prefix-list specific description\n") { - return vty_prefix_list_desc_unset (vty, AFI_IP, argv[0]); + int idx_word = 3; + return vty_prefix_list_desc_unset (vty, AFI_IP, argv[idx_word]->arg); } -ALIAS (no_ip_prefix_list_description, - no_ip_prefix_list_description_arg_cmd, - "no ip prefix-list WORD description .LINE", +/* ALIAS_FIXME */ +DEFUN (no_ip_prefix_list_description_comment, + no_ip_prefix_list_description_comment_cmd, + "no ip prefix-list WORD description LINE...", NO_STR IP_STR PREFIX_LIST_STR "Name of a prefix list\n" "Prefix-list specific description\n" "Up to 80 characters describing this prefix-list\n") +{ + return no_ip_prefix_list_description (self, vty, argc, argv); +} DEFUN (show_ip_prefix_list, show_ip_prefix_list_cmd, @@ -1882,12 +1975,13 @@ DEFUN (show_ip_prefix_list_name, PREFIX_LIST_STR "Name of a prefix list\n") { - return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, normal_display); + int idx_word = 3; + return vty_show_prefix_list (vty, AFI_IP, argv[idx_word]->arg, NULL, normal_display); } DEFUN (show_ip_prefix_list_name_seq, show_ip_prefix_list_name_seq_cmd, - "show ip prefix-list WORD seq <1-4294967295>", + "show ip prefix-list WORD seq (1-4294967295)", SHOW_STR IP_STR PREFIX_LIST_STR @@ -1895,7 +1989,9 @@ DEFUN (show_ip_prefix_list_name_seq, "sequence number of an entry\n" "Sequence number\n") { - return vty_show_prefix_list (vty, AFI_IP, argv[0], argv[1], sequential_display); + int idx_word = 3; + int idx_number = 5; + return vty_show_prefix_list (vty, AFI_IP, argv[idx_word]->arg, argv[idx_number]->arg, sequential_display); } DEFUN (show_ip_prefix_list_prefix, @@ -1907,7 +2003,9 @@ DEFUN (show_ip_prefix_list_prefix, "Name of a prefix list\n" "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n") { - return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], normal_display); + int idx_word = 3; + int idx_ipv4_prefixlen = 4; + return vty_show_prefix_list_prefix (vty, AFI_IP, argv[idx_word]->arg, argv[idx_ipv4_prefixlen]->arg, normal_display); } DEFUN (show_ip_prefix_list_prefix_longer, @@ -1920,7 +2018,9 @@ DEFUN (show_ip_prefix_list_prefix_longer, "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" "Lookup longer prefix\n") { - return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], longer_display); + int idx_word = 3; + int idx_ipv4_prefixlen = 4; + return vty_show_prefix_list_prefix (vty, AFI_IP, argv[idx_word]->arg, argv[idx_ipv4_prefixlen]->arg, longer_display); } DEFUN (show_ip_prefix_list_prefix_first_match, @@ -1933,7 +2033,9 @@ DEFUN (show_ip_prefix_list_prefix_first_match, "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n" "First matched prefix\n") { - return vty_show_prefix_list_prefix (vty, AFI_IP, argv[0], argv[1], first_match_display); + int idx_word = 3; + int idx_ipv4_prefixlen = 4; + return vty_show_prefix_list_prefix (vty, AFI_IP, argv[idx_word]->arg, argv[idx_ipv4_prefixlen]->arg, first_match_display); } DEFUN (show_ip_prefix_list_summary, @@ -1956,7 +2058,8 @@ DEFUN (show_ip_prefix_list_summary_name, "Summary of prefix lists\n" "Name of a prefix list\n") { - return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, summary_display); + int idx_word = 4; + return vty_show_prefix_list (vty, AFI_IP, argv[idx_word]->arg, NULL, summary_display); } @@ -1980,7 +2083,8 @@ DEFUN (show_ip_prefix_list_detail_name, "Detail of prefix lists\n" "Name of a prefix list\n") { - return vty_show_prefix_list (vty, AFI_IP, argv[0], NULL, detail_display); + int idx_word = 4; + return vty_show_prefix_list (vty, AFI_IP, argv[idx_word]->arg, NULL, detail_display); } DEFUN (clear_ip_prefix_list, @@ -2001,7 +2105,8 @@ DEFUN (clear_ip_prefix_list_name, PREFIX_LIST_STR "Name of a prefix list\n") { - return vty_clear_prefix_list (vty, AFI_IP, argv[0], NULL); + int idx_word = 3; + return vty_clear_prefix_list (vty, AFI_IP, argv[idx_word]->arg, NULL); } DEFUN (clear_ip_prefix_list_name_prefix, @@ -2013,13 +2118,14 @@ DEFUN (clear_ip_prefix_list_name_prefix, "Name of a prefix list\n" "IP prefix <network>/<length>, e.g., 35.0.0.0/8\n") { - return vty_clear_prefix_list (vty, AFI_IP, argv[0], argv[1]); + int idx_word = 3; + int idx_ipv4_prefixlen = 4; + return vty_clear_prefix_list (vty, AFI_IP, argv[idx_word]->arg, argv[idx_ipv4_prefixlen]->arg); } -#ifdef HAVE_IPV6 DEFUN (ipv6_prefix_list, ipv6_prefix_list_cmd, - "ipv6 prefix-list WORD (deny|permit) (X:X::X:X/M|any)", + "ipv6 prefix-list WORD <deny|permit> <X:X::X:X/M|any>", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -2028,13 +2134,16 @@ DEFUN (ipv6_prefix_list, "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" "Any prefix match. Same as \"::0/0 le 128\"\n") { - return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, - argv[1], argv[2], NULL, NULL); + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv6_any = 4; + return vty_prefix_list_install (vty, AFI_IP6, argv[idx_word]->arg, NULL, + argv[idx_permit_deny]->arg, argv[idx_ipv6_any]->arg, NULL, NULL); } DEFUN (ipv6_prefix_list_ge, ipv6_prefix_list_ge_cmd, - "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128>", + "ipv6 prefix-list WORD <deny|permit> X:X::X:X/M ge (0-128)", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -2044,13 +2153,17 @@ DEFUN (ipv6_prefix_list_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], - argv[2], argv[3], NULL); + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv6_prefixlen = 4; + int idx_number = 6; + return vty_prefix_list_install (vty, AFI_IP6, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number]->arg, NULL); } DEFUN (ipv6_prefix_list_ge_le, ipv6_prefix_list_ge_le_cmd, - "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + "ipv6 prefix-list WORD <deny|permit> X:X::X:X/M ge (0-128) le (0-128)", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -2063,13 +2176,18 @@ DEFUN (ipv6_prefix_list_ge_le, "Maximum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], - argv[2], argv[3], argv[4]); + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv6_prefixlen = 4; + int idx_number = 6; + int idx_number_2 = 8; + return vty_prefix_list_install (vty, AFI_IP6, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number]->arg, argv[idx_number_2]->arg); } DEFUN (ipv6_prefix_list_le, ipv6_prefix_list_le_cmd, - "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128>", + "ipv6 prefix-list WORD <deny|permit> X:X::X:X/M le (0-128)", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -2079,13 +2197,17 @@ DEFUN (ipv6_prefix_list_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], - argv[2], NULL, argv[3]); + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv6_prefixlen = 4; + int idx_number = 6; + return vty_prefix_list_install (vty, AFI_IP6, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, NULL, argv[idx_number]->arg); } DEFUN (ipv6_prefix_list_le_ge, ipv6_prefix_list_le_ge_cmd, - "ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + "ipv6 prefix-list WORD <deny|permit> X:X::X:X/M le (0-128) ge (0-128)", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -2097,13 +2219,18 @@ DEFUN (ipv6_prefix_list_le_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP6, argv[0], NULL, argv[1], - argv[2], argv[4], argv[3]); + int idx_word = 2; + int idx_permit_deny = 3; + int idx_ipv6_prefixlen = 4; + int idx_number = 6; + int idx_number_2 = 8; + return vty_prefix_list_install (vty, AFI_IP6, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number_2]->arg, argv[idx_number]->arg); } DEFUN (ipv6_prefix_list_seq, ipv6_prefix_list_seq_cmd, - "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) (X:X::X:X/M|any)", + "ipv6 prefix-list WORD seq (1-4294967295) <deny|permit> <X:X::X:X/M|any>", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -2114,13 +2241,17 @@ DEFUN (ipv6_prefix_list_seq, "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" "Any prefix match. Same as \"::0/0 le 128\"\n") { - return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], - argv[3], NULL, NULL); + int idx_word = 2; + int idx_number = 4; + int idx_permit_deny = 5; + int idx_ipv6_any = 6; + return vty_prefix_list_install (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv6_any]->arg, NULL, NULL); } DEFUN (ipv6_prefix_list_seq_ge, ipv6_prefix_list_seq_ge_cmd, - "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128>", + "ipv6 prefix-list WORD seq (1-4294967295) <deny|permit> X:X::X:X/M ge (0-128)", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -2132,13 +2263,18 @@ DEFUN (ipv6_prefix_list_seq_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], - argv[3], argv[4], NULL); + int idx_word = 2; + int idx_number = 4; + int idx_permit_deny = 5; + int idx_ipv6_prefixlen = 6; + int idx_number_2 = 8; + return vty_prefix_list_install (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number_2]->arg, NULL); } DEFUN (ipv6_prefix_list_seq_ge_le, ipv6_prefix_list_seq_ge_le_cmd, - "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + "ipv6 prefix-list WORD seq (1-4294967295) <deny|permit> X:X::X:X/M ge (0-128) le (0-128)", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -2152,13 +2288,19 @@ DEFUN (ipv6_prefix_list_seq_ge_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], - argv[3], argv[4], argv[5]); + int idx_word = 2; + int idx_number = 4; + int idx_permit_deny = 5; + int idx_ipv6_prefixlen = 6; + int idx_number_2 = 8; + int idx_number_3 = 10; + return vty_prefix_list_install (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number_2]->arg, argv[idx_number_3]->arg); } DEFUN (ipv6_prefix_list_seq_le, ipv6_prefix_list_seq_le_cmd, - "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128>", + "ipv6 prefix-list WORD seq (1-4294967295) <deny|permit> X:X::X:X/M le (0-128)", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -2170,13 +2312,18 @@ DEFUN (ipv6_prefix_list_seq_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], - argv[3], NULL, argv[4]); + int idx_word = 2; + int idx_number = 4; + int idx_permit_deny = 5; + int idx_ipv6_prefixlen = 6; + int idx_number_2 = 8; + return vty_prefix_list_install (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, NULL, argv[idx_number_2]->arg); } DEFUN (ipv6_prefix_list_seq_le_ge, ipv6_prefix_list_seq_le_ge_cmd, - "ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + "ipv6 prefix-list WORD seq (1-4294967295) <deny|permit> X:X::X:X/M le (0-128) ge (0-128)", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" @@ -2190,8 +2337,14 @@ DEFUN (ipv6_prefix_list_seq_le_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_install (vty, AFI_IP6, argv[0], argv[1], argv[2], - argv[3], argv[5], argv[4]); + int idx_word = 2; + int idx_number = 4; + int idx_permit_deny = 5; + int idx_ipv6_prefixlen = 6; + int idx_number_2 = 8; + int idx_number_3 = 10; + return vty_prefix_list_install (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number_3]->arg, argv[idx_number_2]->arg); } DEFUN (no_ipv6_prefix_list, @@ -2202,13 +2355,14 @@ DEFUN (no_ipv6_prefix_list, PREFIX_LIST_STR "Name of a prefix list\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, NULL, + int idx_word = 3; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, NULL, NULL, NULL, NULL, NULL); } DEFUN (no_ipv6_prefix_list_prefix, no_ipv6_prefix_list_prefix_cmd, - "no ipv6 prefix-list WORD (deny|permit) (X:X::X:X/M|any)", + "no ipv6 prefix-list WORD <deny|permit> <X:X::X:X/M|any>", NO_STR IPV6_STR PREFIX_LIST_STR @@ -2218,13 +2372,16 @@ DEFUN (no_ipv6_prefix_list_prefix, "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" "Any prefix match. Same as \"::0/0 le 128\"\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], - argv[2], NULL, NULL); + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv6_any = 5; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv6_any]->arg, NULL, NULL); } DEFUN (no_ipv6_prefix_list_ge, no_ipv6_prefix_list_ge_cmd, - "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128>", + "no ipv6 prefix-list WORD <deny|permit> X:X::X:X/M ge (0-128)", NO_STR IPV6_STR PREFIX_LIST_STR @@ -2235,13 +2392,17 @@ DEFUN (no_ipv6_prefix_list_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], - argv[2], argv[3], NULL); + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv6_prefixlen = 5; + int idx_number = 7; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number]->arg, NULL); } DEFUN (no_ipv6_prefix_list_ge_le, no_ipv6_prefix_list_ge_le_cmd, - "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + "no ipv6 prefix-list WORD <deny|permit> X:X::X:X/M ge (0-128) le (0-128)", NO_STR IPV6_STR PREFIX_LIST_STR @@ -2254,13 +2415,18 @@ DEFUN (no_ipv6_prefix_list_ge_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], - argv[2], argv[3], argv[4]); + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv6_prefixlen = 5; + int idx_number = 7; + int idx_number_2 = 9; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number]->arg, argv[idx_number_2]->arg); } DEFUN (no_ipv6_prefix_list_le, no_ipv6_prefix_list_le_cmd, - "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128>", + "no ipv6 prefix-list WORD <deny|permit> X:X::X:X/M le (0-128)", NO_STR IPV6_STR PREFIX_LIST_STR @@ -2271,13 +2437,17 @@ DEFUN (no_ipv6_prefix_list_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], - argv[2], NULL, argv[3]); + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv6_prefixlen = 5; + int idx_number = 7; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, NULL, argv[idx_number]->arg); } DEFUN (no_ipv6_prefix_list_le_ge, no_ipv6_prefix_list_le_ge_cmd, - "no ipv6 prefix-list WORD (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + "no ipv6 prefix-list WORD <deny|permit> X:X::X:X/M le (0-128) ge (0-128)", NO_STR IPV6_STR PREFIX_LIST_STR @@ -2290,13 +2460,18 @@ DEFUN (no_ipv6_prefix_list_le_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], NULL, argv[1], - argv[2], argv[4], argv[3]); + int idx_word = 3; + int idx_permit_deny = 4; + int idx_ipv6_prefixlen = 5; + int idx_number = 7; + int idx_number_2 = 9; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, NULL, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number_2]->arg, argv[idx_number]->arg); } DEFUN (no_ipv6_prefix_list_seq, no_ipv6_prefix_list_seq_cmd, - "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) (X:X::X:X/M|any)", + "no ipv6 prefix-list WORD seq (1-4294967295) <deny|permit> <X:X::X:X/M|any>", NO_STR IPV6_STR PREFIX_LIST_STR @@ -2308,13 +2483,17 @@ DEFUN (no_ipv6_prefix_list_seq, "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" "Any prefix match. Same as \"::0/0 le 128\"\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], - argv[3], NULL, NULL); + int idx_word = 3; + int idx_number = 5; + int idx_permit_deny = 6; + int idx_ipv6_any = 7; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv6_any]->arg, NULL, NULL); } DEFUN (no_ipv6_prefix_list_seq_ge, no_ipv6_prefix_list_seq_ge_cmd, - "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128>", + "no ipv6 prefix-list WORD seq (1-4294967295) <deny|permit> X:X::X:X/M ge (0-128)", NO_STR IPV6_STR PREFIX_LIST_STR @@ -2327,13 +2506,18 @@ DEFUN (no_ipv6_prefix_list_seq_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], - argv[3], argv[4], NULL); + int idx_word = 3; + int idx_number = 5; + int idx_permit_deny = 6; + int idx_ipv6_prefixlen = 7; + int idx_number_2 = 9; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number_2]->arg, NULL); } DEFUN (no_ipv6_prefix_list_seq_ge_le, no_ipv6_prefix_list_seq_ge_le_cmd, - "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M ge <0-128> le <0-128>", + "no ipv6 prefix-list WORD seq (1-4294967295) <deny|permit> X:X::X:X/M ge (0-128) le (0-128)", NO_STR IPV6_STR PREFIX_LIST_STR @@ -2348,13 +2532,19 @@ DEFUN (no_ipv6_prefix_list_seq_ge_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], - argv[3], argv[4], argv[5]); + int idx_word = 3; + int idx_number = 5; + int idx_permit_deny = 6; + int idx_ipv6_prefixlen = 7; + int idx_number_2 = 9; + int idx_number_3 = 11; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number_2]->arg, argv[idx_number_3]->arg); } DEFUN (no_ipv6_prefix_list_seq_le, no_ipv6_prefix_list_seq_le_cmd, - "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128>", + "no ipv6 prefix-list WORD seq (1-4294967295) <deny|permit> X:X::X:X/M le (0-128)", NO_STR IPV6_STR PREFIX_LIST_STR @@ -2367,13 +2557,18 @@ DEFUN (no_ipv6_prefix_list_seq_le, "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], - argv[3], NULL, argv[4]); + int idx_word = 3; + int idx_number = 5; + int idx_permit_deny = 6; + int idx_ipv6_prefixlen = 7; + int idx_number_2 = 9; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, NULL, argv[idx_number_2]->arg); } DEFUN (no_ipv6_prefix_list_seq_le_ge, no_ipv6_prefix_list_seq_le_ge_cmd, - "no ipv6 prefix-list WORD seq <1-4294967295> (deny|permit) X:X::X:X/M le <0-128> ge <0-128>", + "no ipv6 prefix-list WORD seq (1-4294967295) <deny|permit> X:X::X:X/M le (0-128) ge (0-128)", NO_STR IPV6_STR PREFIX_LIST_STR @@ -2388,8 +2583,14 @@ DEFUN (no_ipv6_prefix_list_seq_le_ge, "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return vty_prefix_list_uninstall (vty, AFI_IP6, argv[0], argv[1], argv[2], - argv[3], argv[5], argv[4]); + int idx_word = 3; + int idx_number = 5; + int idx_permit_deny = 6; + int idx_ipv6_prefixlen = 7; + int idx_number_2 = 9; + int idx_number_3 = 11; + return vty_prefix_list_uninstall (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, argv[idx_permit_deny]->arg, + argv[idx_ipv6_prefixlen]->arg, argv[idx_number_3]->arg, argv[idx_number_2]->arg); } DEFUN (ipv6_prefix_list_sequence_number, @@ -2417,26 +2618,28 @@ DEFUN (no_ipv6_prefix_list_sequence_number, DEFUN (ipv6_prefix_list_description, ipv6_prefix_list_description_cmd, - "ipv6 prefix-list WORD description .LINE", + "ipv6 prefix-list WORD description LINE...", IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" "Prefix-list specific description\n" "Up to 80 characters describing this prefix-list\n") { + int idx_word = 2; + int iddx_line = 4; struct prefix_list *plist; - plist = prefix_list_get (AFI_IP6, 0, argv[0]); + plist = prefix_list_get (AFI_IP6, 0, argv[idx_word]->arg); if (plist->desc) { XFREE (MTYPE_TMP, plist->desc); plist->desc = NULL; } - plist->desc = argv_concat(argv, argc, 1); + plist->desc = argv_concat(argv, argc, iddx_line); return CMD_SUCCESS; -} +} DEFUN (no_ipv6_prefix_list_description, no_ipv6_prefix_list_description_cmd, @@ -2447,18 +2650,24 @@ DEFUN (no_ipv6_prefix_list_description, "Name of a prefix list\n" "Prefix-list specific description\n") { - return vty_prefix_list_desc_unset (vty, AFI_IP6, argv[0]); + int idx_word = 3; + return vty_prefix_list_desc_unset (vty, AFI_IP6, argv[idx_word]->arg); } -ALIAS (no_ipv6_prefix_list_description, - no_ipv6_prefix_list_description_arg_cmd, - "no ipv6 prefix-list WORD description .LINE", +/* ALIAS_FIXME */ +DEFUN (no_ipv6_prefix_list_description_comment, + no_ipv6_prefix_list_description_comment_cmd, + "no ipv6 prefix-list WORD description LINE...", NO_STR IPV6_STR PREFIX_LIST_STR "Name of a prefix list\n" "Prefix-list specific description\n" "Up to 80 characters describing this prefix-list\n") +{ + return no_ipv6_prefix_list_description (self, vty, argc, argv); +} + DEFUN (show_ipv6_prefix_list, show_ipv6_prefix_list_cmd, @@ -2478,12 +2687,13 @@ DEFUN (show_ipv6_prefix_list_name, PREFIX_LIST_STR "Name of a prefix list\n") { - return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, normal_display); + int idx_word = 3; + return vty_show_prefix_list (vty, AFI_IP6, argv[idx_word]->arg, NULL, normal_display); } DEFUN (show_ipv6_prefix_list_name_seq, show_ipv6_prefix_list_name_seq_cmd, - "show ipv6 prefix-list WORD seq <1-4294967295>", + "show ipv6 prefix-list WORD seq (1-4294967295)", SHOW_STR IPV6_STR PREFIX_LIST_STR @@ -2491,7 +2701,9 @@ DEFUN (show_ipv6_prefix_list_name_seq, "sequence number of an entry\n" "Sequence number\n") { - return vty_show_prefix_list (vty, AFI_IP6, argv[0], argv[1], sequential_display); + int idx_word = 3; + int idx_number = 5; + return vty_show_prefix_list (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_number]->arg, sequential_display); } DEFUN (show_ipv6_prefix_list_prefix, @@ -2503,7 +2715,9 @@ DEFUN (show_ipv6_prefix_list_prefix, "Name of a prefix list\n" "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n") { - return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], normal_display); + int idx_word = 3; + int idx_ipv6_prefixlen = 4; + return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_ipv6_prefixlen]->arg, normal_display); } DEFUN (show_ipv6_prefix_list_prefix_longer, @@ -2516,7 +2730,9 @@ DEFUN (show_ipv6_prefix_list_prefix_longer, "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" "Lookup longer prefix\n") { - return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], longer_display); + int idx_word = 3; + int idx_ipv6_prefixlen = 4; + return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_ipv6_prefixlen]->arg, longer_display); } DEFUN (show_ipv6_prefix_list_prefix_first_match, @@ -2529,7 +2745,9 @@ DEFUN (show_ipv6_prefix_list_prefix_first_match, "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n" "First matched prefix\n") { - return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[0], argv[1], first_match_display); + int idx_word = 3; + int idx_ipv6_prefixlen = 4; + return vty_show_prefix_list_prefix (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_ipv6_prefixlen]->arg, first_match_display); } DEFUN (show_ipv6_prefix_list_summary, @@ -2552,7 +2770,8 @@ DEFUN (show_ipv6_prefix_list_summary_name, "Summary of prefix lists\n" "Name of a prefix list\n") { - return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, summary_display); + int idx_word = 4; + return vty_show_prefix_list (vty, AFI_IP6, argv[idx_word]->arg, NULL, summary_display); } DEFUN (show_ipv6_prefix_list_detail, @@ -2575,7 +2794,8 @@ DEFUN (show_ipv6_prefix_list_detail_name, "Detail of prefix lists\n" "Name of a prefix list\n") { - return vty_show_prefix_list (vty, AFI_IP6, argv[0], NULL, detail_display); + int idx_word = 4; + return vty_show_prefix_list (vty, AFI_IP6, argv[idx_word]->arg, NULL, detail_display); } DEFUN (clear_ipv6_prefix_list, @@ -2596,7 +2816,8 @@ DEFUN (clear_ipv6_prefix_list_name, PREFIX_LIST_STR "Name of a prefix list\n") { - return vty_clear_prefix_list (vty, AFI_IP6, argv[0], NULL); + int idx_word = 3; + return vty_clear_prefix_list (vty, AFI_IP6, argv[idx_word]->arg, NULL); } DEFUN (clear_ipv6_prefix_list_name_prefix, @@ -2608,9 +2829,10 @@ DEFUN (clear_ipv6_prefix_list_name_prefix, "Name of a prefix list\n" "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n") { - return vty_clear_prefix_list (vty, AFI_IP6, argv[0], argv[1]); + int idx_word = 3; + int idx_ipv6_prefixlen = 4; + return vty_clear_prefix_list (vty, AFI_IP6, argv[idx_word]->arg, argv[idx_ipv6_prefixlen]->arg); } -#endif /* HAVE_IPV6 */ /* Configuration write function. */ static int @@ -2964,7 +3186,7 @@ prefix_list_init_ipv4 (void) install_element (CONFIG_NODE, &ip_prefix_list_description_cmd); install_element (CONFIG_NODE, &no_ip_prefix_list_description_cmd); - install_element (CONFIG_NODE, &no_ip_prefix_list_description_arg_cmd); + install_element (CONFIG_NODE, &no_ip_prefix_list_description_comment_cmd); install_element (CONFIG_NODE, &ip_prefix_list_sequence_number_cmd); install_element (CONFIG_NODE, &no_ip_prefix_list_sequence_number_cmd); @@ -2985,7 +3207,6 @@ prefix_list_init_ipv4 (void) install_element (ENABLE_NODE, &clear_ip_prefix_list_name_prefix_cmd); } -#ifdef HAVE_IPV6 /* Prefix-list node. */ static struct cmd_node prefix_ipv6_node = { @@ -3030,7 +3251,7 @@ prefix_list_init_ipv6 (void) install_element (CONFIG_NODE, &ipv6_prefix_list_description_cmd); install_element (CONFIG_NODE, &no_ipv6_prefix_list_description_cmd); - install_element (CONFIG_NODE, &no_ipv6_prefix_list_description_arg_cmd); + install_element (CONFIG_NODE, &no_ipv6_prefix_list_description_comment_cmd); install_element (CONFIG_NODE, &ipv6_prefix_list_sequence_number_cmd); install_element (CONFIG_NODE, &no_ipv6_prefix_list_sequence_number_cmd); @@ -3050,15 +3271,12 @@ prefix_list_init_ipv6 (void) install_element (ENABLE_NODE, &clear_ipv6_prefix_list_name_cmd); install_element (ENABLE_NODE, &clear_ipv6_prefix_list_name_prefix_cmd); } -#endif /* HAVE_IPV6 */ void prefix_list_init () { prefix_list_init_ipv4 (); -#ifdef HAVE_IPV6 prefix_list_init_ipv6 (); -#endif /* HAVE_IPV6 */ } void diff --git a/lib/plist.h b/lib/plist.h index 2c6f13a5c4..89d9a874f0 100644 --- a/lib/plist.h +++ b/lib/plist.h @@ -23,6 +23,11 @@ #ifndef _QUAGGA_PLIST_H #define _QUAGGA_PLIST_H +#include <zebra.h> + +#include "stream.h" +#include "vty.h" + enum prefix_list_type { PREFIX_DENY, diff --git a/lib/prefix.c b/lib/prefix.c index 84a04c5300..0cc759bb7c 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -207,11 +207,9 @@ afi2family (afi_t afi) { if (afi == AFI_IP) return AF_INET; -#ifdef HAVE_IPV6 else if (afi == AFI_IP6) return AF_INET6; -#endif /* HAVE_IPV6 */ - else if (afi == AFI_ETHER) + else if (afi == AFI_L2VPN) return AF_ETHERNET; return 0; } @@ -221,12 +219,10 @@ family2afi (int family) { if (family == AF_INET) return AFI_IP; -#ifdef HAVE_IPV6 else if (family == AF_INET6) return AFI_IP6; -#endif /* HAVE_IPV6 */ else if (family == AF_ETHERNET) - return AFI_ETHER; + return AFI_L2VPN; return 0; } @@ -238,8 +234,8 @@ afi2str(afi_t afi) return "IPv4"; case AFI_IP6: return "IPv6"; - case AFI_ETHER: - return "ethernet"; + case AFI_L2VPN: + return "l2vpn"; case AFI_MAX: return "bad-value"; default: @@ -260,6 +256,8 @@ safi2str(safi_t safi) return "encap"; case SAFI_MPLS_VPN: return "vpn"; + case SAFI_EVPN: + return "evpn"; } return NULL; } @@ -302,23 +300,20 @@ prefix_copy (struct prefix *dest, const struct prefix *src) if (src->family == AF_INET) dest->u.prefix4 = src->u.prefix4; -#ifdef HAVE_IPV6 else if (src->family == AF_INET6) dest->u.prefix6 = src->u.prefix6; -#endif /* HAVE_IPV6 */ + else if (src->family == AF_ETHERNET) + { + memcpy (&dest->u.prefix_evpn, &src->u.prefix_evpn, sizeof (struct evpn_addr)); + } else if (src->family == AF_UNSPEC) { dest->u.lp.id = src->u.lp.id; dest->u.lp.adv_router = src->u.lp.adv_router; } - else if (src->family == AF_ETHERNET) - { - dest->u.prefix_eth = src->u.prefix_eth; - } else { - zlog (NULL, LOG_ERR, "prefix_copy(): Unknown address family %d", - src->family); + zlog_err("prefix_copy(): Unknown address family %d", src->family); assert (0); } } @@ -345,15 +340,12 @@ prefix_same (const struct prefix *p1, const struct prefix *p2) if (p1->family == AF_INET) if (IPV4_ADDR_SAME (&p1->u.prefix4.s_addr, &p2->u.prefix4.s_addr)) return 1; -#ifdef HAVE_IPV6 if (p1->family == AF_INET6 ) if (IPV6_ADDR_SAME (&p1->u.prefix6.s6_addr, &p2->u.prefix6.s6_addr)) return 1; -#endif /* HAVE_IPV6 */ - if (p1->family == AF_ETHERNET) { - if (!memcmp(p1->u.prefix_eth.octet, p2->u.prefix_eth.octet, ETHER_ADDR_LEN)) - return 1; - } + if (p1->family == AF_ETHERNET ) + if (!memcmp (&p1->u.prefix_evpn, &p2->u.prefix_evpn, sizeof (struct evpn_addr))) + return 1; } return 0; } @@ -414,10 +406,11 @@ prefix_common_bits (const struct prefix *p1, const struct prefix *p2) if (p1->family == AF_INET) length = IPV4_MAX_BYTELEN; -#ifdef HAVE_IPV6 if (p1->family == AF_INET6) length = IPV6_MAX_BYTELEN; -#endif + if (p1->family == AF_ETHERNET) + length = 8 * sizeof (struct evpn_addr); + if (p1->family != p2->family || !length) return -1; @@ -441,10 +434,8 @@ prefix_family_str (const struct prefix *p) { if (p->family == AF_INET) return "inet"; -#ifdef HAVE_IPV6 if (p->family == AF_INET6) return "inet6"; -#endif /* HAVE_IPV6 */ if (p->family == AF_ETHERNET) return "ether"; return "unspec"; @@ -617,8 +608,6 @@ prefix_ipv4_any (const struct prefix_ipv4 *p) return (p->prefix.s_addr == 0 && p->prefixlen == 0); } -#ifdef HAVE_IPV6 - /* Allocate a new ip version 6 route */ struct prefix_ipv6 * prefix_ipv6_new (void) @@ -737,21 +726,6 @@ apply_mask_ipv6 (struct prefix_ipv6 *p) } void -str2in6_addr (const char *str, struct in6_addr *addr) -{ - int i; - unsigned int x; - - /* %x must point to unsinged int */ - for (i = 0; i < 16; i++) - { - sscanf (str + (i * 2), "%02x", &x); - addr->s6_addr[i] = x & 0xff; - } -} -#endif /* HAVE_IPV6 */ - -void apply_mask (struct prefix *p) { switch (p->family) @@ -759,11 +733,9 @@ apply_mask (struct prefix *p) case AF_INET: apply_mask_ipv4 ((struct prefix_ipv4 *)p); break; -#ifdef HAVE_IPV6 case AF_INET6: apply_mask_ipv6 ((struct prefix_ipv6 *)p); break; -#endif /* HAVE_IPV6 */ default: break; } @@ -786,7 +758,6 @@ sockunion2prefix (const union sockunion *dest, p->prefixlen = ip_masklen (mask->sin.sin_addr); return (struct prefix *) p; } -#ifdef HAVE_IPV6 if (dest->sa.sa_family == AF_INET6) { struct prefix_ipv6 *p; @@ -797,7 +768,6 @@ sockunion2prefix (const union sockunion *dest, memcpy (&p->prefix, &dest->sin6.sin6_addr, sizeof (struct in6_addr)); return (struct prefix *) p; } -#endif /* HAVE_IPV6 */ return NULL; } @@ -815,7 +785,6 @@ sockunion2hostprefix (const union sockunion *su, struct prefix *prefix) p->prefixlen = IPV4_MAX_BITLEN; return (struct prefix *) p; } -#ifdef HAVE_IPV6 if (su->sa.sa_family == AF_INET6) { struct prefix_ipv6 *p; @@ -826,7 +795,6 @@ sockunion2hostprefix (const union sockunion *su, struct prefix *prefix) memcpy (&p->prefix, &su->sin6.sin6_addr, sizeof (struct in6_addr)); return (struct prefix *) p; } -#endif /* HAVE_IPV6 */ return NULL; } @@ -838,10 +806,8 @@ prefix2sockunion (const struct prefix *p, union sockunion *su) su->sa.sa_family = p->family; if (p->family == AF_INET) su->sin.sin_addr = p->u.prefix4; -#ifdef HAVE_IPV6 if (p->family == AF_INET6) memcpy (&su->sin6.sin6_addr, &p->u.prefix6, sizeof (struct in6_addr)); -#endif /* HAVE_IPV6 */ } int @@ -852,11 +818,9 @@ prefix_blen (const struct prefix *p) case AF_INET: return IPV4_MAX_BYTELEN; break; -#ifdef HAVE_IPV6 case AF_INET6: return IPV6_MAX_BYTELEN; break; -#endif /* HAVE_IPV6 */ case AF_ETHERNET: return ETHER_ADDR_LEN; } @@ -874,12 +838,10 @@ str2prefix (const char *str, struct prefix *p) if (ret) return ret; -#ifdef HAVE_IPV6 /* Next we try to convert string to struct prefix_ipv6. */ ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p); if (ret) return ret; -#endif /* HAVE_IPV6 */ /* Next we try to convert string to struct prefix_eth. */ ret = str2prefix_eth (str, (struct prefix_eth *) p); @@ -890,23 +852,46 @@ str2prefix (const char *str, struct prefix *p) } const char * -prefix2str (union prefix46constptr pu, char *str, int size) +prefix2str (union prefixconstptr pu, char *str, int size) { const struct prefix *p = pu.p; + char buf[PREFIX2STR_BUFFER]; - if (p->family == AF_ETHERNET) - { - snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x/%d", - p->u.prefix_eth.octet[0], p->u.prefix_eth.octet[1], - p->u.prefix_eth.octet[2], p->u.prefix_eth.octet[3], - p->u.prefix_eth.octet[4], p->u.prefix_eth.octet[5], - p->prefixlen); - } - else + switch (p->family) { - char buf[PREFIX2STR_BUFFER]; - inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf)); - snprintf(str, size, "%s/%d", buf, p->prefixlen); + case AF_INET: + case AF_INET6: + snprintf (str, size, "%s/%d", + inet_ntop (p->family, &p->u.prefix, buf, PREFIX2STR_BUFFER), + p->prefixlen); + break; + + case AF_ETHERNET: + if (p->u.prefix_evpn.route_type == 5) + { + u_char family; + family = (p->u.prefix_evpn.flags & (IP_ADDR_V4 | IP_PREFIX_V4)) ? + AF_INET : AF_INET6; + snprintf (str, size, "[%d]:[%u][%s]/%d", + p->u.prefix_evpn.route_type, + p->u.prefix_evpn.eth_tag, + inet_ntop (family, &p->u.prefix_evpn.ip.addr, + buf, PREFIX2STR_BUFFER), + p->prefixlen); + } + else + { + sprintf (str, "UNK AF_ETHER prefix"); + snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x/%d", + p->u.prefix_eth.octet[0], p->u.prefix_eth.octet[1], + p->u.prefix_eth.octet[2], p->u.prefix_eth.octet[3], + p->u.prefix_eth.octet[4], p->u.prefix_eth.octet[5], + p->prefixlen); + } + break; + default: + sprintf (str, "UNK prefix"); + break; } return str; @@ -1035,7 +1020,6 @@ netmask_str2prefix_str (const char *net_str, const char *mask_str, return 1; } -#ifdef HAVE_IPV6 /* Utility function for making IPv6 address string. */ const char * inet6_ntoa (struct in6_addr addr) @@ -1045,4 +1029,50 @@ inet6_ntoa (struct in6_addr addr) inet_ntop (AF_INET6, &addr, buf, INET6_ADDRSTRLEN); return buf; } -#endif /* HAVE_IPV6 */ + +/* converts to internal representation of mac address + * returns 1 on success, 0 otherwise + * format accepted: AA:BB:CC:DD:EE:FF + * if mac parameter is null, then check only + */ +int prefix_str2mac(const char *str, struct ethaddr *mac) +{ + unsigned int a[6]; + int i; + + if (!str) + return 0; + + if (sscanf (str, "%2x:%2x:%2x:%2x:%2x:%2x", + a + 0, a + 1, a + 2, a + 3, a + 4, a + 5) != 6) + { + /* error in incoming str length */ + return 0; + } + /* valid mac address */ + if (!mac) + return 1; + for (i = 0; i < 6; ++i) + mac->octet[i] = a[i] & 0xff; + return 1; +} + +char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size) +{ + char *ptr; + + if (!mac) + return NULL; + if (!buf) + ptr = (char *)XMALLOC(MTYPE_TMP, ETHER_ADDR_STRLEN* sizeof(char)); + else + { + assert (size >= ETHER_ADDR_STRLEN); + ptr = buf; + } + snprintf(ptr, (ETHER_ADDR_STRLEN), + "%02x:%02x:%02x:%02x:%02x:%02x", (uint8_t) mac->octet[0], + (uint8_t) mac->octet[1], (uint8_t) mac->octet[2], (uint8_t) mac->octet[3], + (uint8_t) mac->octet[4], (uint8_t) mac->octet[5]); + return ptr; +} diff --git a/lib/prefix.h b/lib/prefix.h index 85488ccea7..eb3ae3dafb 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -35,17 +35,58 @@ #include "sockunion.h" #ifndef ETHER_ADDR_LEN +#ifdef ETHERADDRL #define ETHER_ADDR_LEN ETHERADDRL +#else +#define ETHER_ADDR_LEN 6 +#endif #endif +#define ETHER_ADDR_STRLEN (3*ETHER_ADDR_LEN) /* * there isn't a portable ethernet address type. We define our * own to simplify internal handling */ struct ethaddr { u_char octet[ETHER_ADDR_LEN]; -} __packed; +} __attribute__ ((packed)); + +/* length is the number of valuable bits of prefix structure +* 18 bytes is current length in structure, if address is ipv4 +* 30 bytes is in case of ipv6 +*/ +#define PREFIX_LEN_ROUTE_TYPE_5_IPV4 (18*8) +#define PREFIX_LEN_ROUTE_TYPE_5_IPV6 (30*8) + +/* EVPN address (RFC 7432) */ +struct evpn_addr +{ + u_char route_type; + u_char flags; +#define IP_ADDR_NONE 0x0 +#define IP_ADDR_V4 0x1 +#define IP_ADDR_V6 0x2 +#define IP_PREFIX_V4 0x4 +#define IP_PREFIX_V6 0x8 + struct ethaddr mac; + uint32_t eth_tag; + u_char ip_prefix_length; + union + { + u_char addr; + struct in_addr v4_addr; + struct in6_addr v6_addr; + } ip; +}; + +/* EVPN prefix structure. */ +struct prefix_evpn +{ + u_char family; + u_char prefixlen; + struct evpn_addr prefix __attribute__ ((aligned (8))); +}; /* * A struct prefix contains an address family, a prefix length, and an @@ -74,9 +115,7 @@ struct prefix { u_char prefix; struct in_addr prefix4; -#ifdef HAVE_IPV6 struct in6_addr prefix6; -#endif /* HAVE_IPV6 */ struct { struct in_addr id; @@ -85,6 +124,7 @@ struct prefix struct ethaddr prefix_eth; /* AF_ETHERNET */ u_char val[8]; uintptr_t ptr; + struct evpn_addr prefix_evpn; } u __attribute__ ((aligned (8))); }; @@ -97,14 +137,12 @@ struct prefix_ipv4 }; /* IPv6 prefix structure. */ -#ifdef HAVE_IPV6 struct prefix_ipv6 { u_char family; u_char prefixlen; struct in6_addr prefix __attribute__ ((aligned (8))); }; -#endif /* HAVE_IPV6 */ struct prefix_ls { @@ -138,23 +176,33 @@ struct prefix_ptr uintptr_t prefix __attribute__ ((aligned (8))); }; +struct prefix_sg +{ + u_char family; + u_char prefixlen; + struct in_addr src __attribute ((aligned (8))); + struct in_addr grp; +}; + /* helper to get type safety/avoid casts on calls * (w/o this, functions accepting all prefix types need casts on the caller * side, which strips type safety since the cast will accept any pointer * type.) */ -union prefix46ptr +union prefixptr { struct prefix *p; struct prefix_ipv4 *p4; struct prefix_ipv6 *p6; + struct prefix_evpn *evp; } __attribute__ ((transparent_union)); -union prefix46constptr +union prefixconstptr { const struct prefix *p; const struct prefix_ipv4 *p4; const struct prefix_ipv6 *p6; + const struct prefix_evpn *evp; } __attribute__ ((transparent_union)); #ifndef INET_ADDRSTRLEN @@ -226,14 +274,9 @@ extern const char *prefix_family_str (const struct prefix *); extern int prefix_blen (const struct prefix *); extern int str2prefix (const char *, struct prefix *); -/* - * 8 groups of 4 bytes of hexadecimal + 7 seperators is 39 - * /128 = 4 bytes - * Null = 1 byte - * 39 + 4 + 1 = 44 bytes - */ -#define PREFIX2STR_BUFFER 44 -extern const char *prefix2str (union prefix46constptr, char *, int); +#define PREFIX2STR_BUFFER PREFIX_STRLEN + +extern const char *prefix2str (union prefixconstptr, char *, int); extern int prefix_match (const struct prefix *, const struct prefix *); extern int prefix_same (const struct prefix *, const struct prefix *); extern int prefix_cmp (const struct prefix *, const struct prefix *); @@ -271,7 +314,6 @@ extern in_addr_t ipv4_broadcast_addr (in_addr_t hostaddr, int masklen); extern int netmask_str2prefix_str (const char *, const char *, char *); -#ifdef HAVE_IPV6 extern struct prefix_ipv6 *prefix_ipv6_new (void); extern void prefix_ipv6_free (struct prefix_ipv6 *); extern int str2prefix_ipv6 (const char *, struct prefix_ipv6 *); @@ -283,9 +325,11 @@ extern void apply_mask_ipv6 (struct prefix_ipv6 *); extern int ip6_masklen (struct in6_addr); extern void masklen2ip6 (const int, struct in6_addr *); -extern void str2in6_addr (const char *, struct in6_addr *); extern const char *inet6_ntoa (struct in6_addr); +extern int prefix_str2mac(const char *str, struct ethaddr *mac); +extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size); + static inline int ipv6_martian (struct in6_addr *addr) { struct in6_addr localhost_addr; @@ -298,8 +342,6 @@ static inline int ipv6_martian (struct in6_addr *addr) return 0; } -#endif /* HAVE_IPV6 */ - extern int all_digit (const char *); /* NOTE: This routine expects the address argument in network byte order. */ diff --git a/lib/ptm_lib.c b/lib/ptm_lib.c index 0666797fad..a93d7b8476 100644 --- a/lib/ptm_lib.c +++ b/lib/ptm_lib.c @@ -458,7 +458,7 @@ ptm_lib_register(char *client_name, hdl = calloc(1, sizeof(*hdl)); if (hdl) { - strcpy(hdl->client_name, client_name); + strncpy(hdl->client_name, client_name, PTMLIB_MAXNAMELEN - 1); hdl->cmd_cb = cmd_cb; hdl->notify_cb = notify_cb; hdl->response_cb = response_cb; diff --git a/lib/qobj.c b/lib/qobj.c index f64972e32a..8a386d2486 100644 --- a/lib/qobj.c +++ b/lib/qobj.c @@ -49,7 +49,7 @@ void qobj_reg(struct qobj_node *node, struct qobj_nodetype *type) node->nid = (uint64_t)random(); node->nid ^= (uint64_t)random() << 32; } - while (hash_get (nodes, node, hash_alloc_intern) != node); + while (!node->nid || hash_get (nodes, node, hash_alloc_intern) != node); } void qobj_unreg(struct qobj_node *node) @@ -73,7 +73,8 @@ void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type) void qobj_init (void) { - nodes = hash_create (qobj_key, qobj_cmp); + if (!nodes) + nodes = hash_create (qobj_key, qobj_cmp); } void qobj_finish (void) diff --git a/lib/qobj.h b/lib/qobj.h index 4c326731ed..64a6774bff 100644 --- a/lib/qobj.h +++ b/lib/qobj.h @@ -109,6 +109,8 @@ void *qobj_get_typed(uint64_t id, struct qobj_nodetype *type); #define QOBJ_ID(ptr) \ ((ptr)->qobj_node.nid) +#define QOBJ_ID_0SAFE(ptr) \ + ({ typeof (ptr) _ptr = (ptr); _ptr ? _ptr->qobj_node.nid : 0ULL; }) void qobj_init(void); void qobj_finish(void); diff --git a/lib/route_types.pl b/lib/route_types.pl index 62c7417b84..27ca950787 100755 --- a/lib/route_types.pl +++ b/lib/route_types.pl @@ -149,7 +149,7 @@ sub collect { push @names, "any"; push @help, " \"Any of the above protocols\\n\""; } - return ("\"(" . join("|", @names) . ")\"", join(" \\\n", @help)); + return ("\"<" . join("|", @names) . ">\"", join(" \\\n", @help)); } for my $daemon (sort keys %daemons) { diff --git a/lib/route_types.txt b/lib/route_types.txt index 154f03f01c..5cb06ffb7f 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -48,10 +48,11 @@ ZEBRA_ROUTE_STATIC, static, zebra, 'S', 1, 1, "static" ZEBRA_ROUTE_RIP, rip, ripd, 'R', 1, 0, "RIP" ZEBRA_ROUTE_RIPNG, ripng, ripngd, 'R', 0, 1, "RIPng" ZEBRA_ROUTE_OSPF, ospf, ospfd, 'O', 1, 0, "OSPF" -ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv6" +ZEBRA_ROUTE_OSPF6, ospf6, ospf6d, 'O', 0, 1, "OSPFv3" ZEBRA_ROUTE_ISIS, isis, isisd, 'I', 1, 1, "IS-IS" ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP" ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM" +ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, "NHRP" # HSLS and OLSR both are AFI independent (so: 1, 1), however # we want to disable for them for general Quagga distribution. # This at least makes it trivial for users of these protocols @@ -85,6 +86,7 @@ ZEBRA_ROUTE_OSPF6, "Open Shortest Path First (IPv6) (OSPFv3)" ZEBRA_ROUTE_ISIS, "Intermediate System to Intermediate System (IS-IS)" ZEBRA_ROUTE_BGP, "Border Gateway Protocol (BGP)" ZEBRA_ROUTE_PIM, "Protocol Independent Multicast (PIM)" +ZEBRA_ROUTE_NHRP, "Next Hop Resolution Protocol (NHRP)" ZEBRA_ROUTE_HSLS, "Hazy-Sighted Link State Protocol (HSLS)" ZEBRA_ROUTE_VNC, "Virtual Network Control (VNC)" ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)" diff --git a/lib/routemap.c b/lib/routemap.c index ace4961f72..1647ac3668 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -24,10 +24,11 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "memory.h" #include "vector.h" #include "prefix.h" +#include "vty.h" #include "routemap.h" #include "command.h" -#include "vty.h" #include "log.h" +#include "log_int.h" #include "hash.h" DEFINE_MTYPE_STATIC(LIB, ROUTE_MAP, "Route map") @@ -47,6 +48,594 @@ static vector route_match_vec; /* Vector for route set rules. */ static vector route_set_vec; +struct route_map_match_set_hooks +{ + /* match interface */ + int (*match_interface) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match interface */ + int (*no_match_interface) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ip address */ + int (*match_ip_address) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ip address */ + int (*no_match_ip_address) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ip address prefix list */ + int (*match_ip_address_prefix_list) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ip address prefix list */ + int (*no_match_ip_address_prefix_list) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ip next hop */ + int (*match_ip_next_hop) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ip next hop */ + int (*no_match_ip_next_hop) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ip next hop prefix list */ + int (*match_ip_next_hop_prefix_list) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ip next hop prefix list */ + int (*no_match_ip_next_hop_prefix_list) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match ipv6 address */ + int (*match_ipv6_address) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ipv6 address */ + int (*no_match_ipv6_address) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + + /* match ipv6 address prefix list */ + int (*match_ipv6_address_prefix_list) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match ipv6 address prefix list */ + int (*no_match_ipv6_address_prefix_list) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match metric */ + int (*match_metric) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match metric */ + int (*no_match_metric) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* match tag */ + int (*match_tag) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* no match tag */ + int (*no_match_tag) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + + /* set ip nexthop */ + int (*set_ip_nexthop) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg); + + /* no set ip nexthop */ + int (*no_set_ip_nexthop) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg); + + /* set ipv6 nexthop local */ + int (*set_ipv6_nexthop_local) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg); + + /* no set ipv6 nexthop local */ + int (*no_set_ipv6_nexthop_local) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg); + + /* set metric */ + int (*set_metric) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg); + + /* no set metric */ + int (*no_set_metric) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg); + + /* set tag */ + int (*set_tag) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg); + + /* no set tag */ + int (*no_set_tag) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg); + +}; + +struct route_map_match_set_hooks rmap_match_set_hook; + +/* match interface */ +void +route_map_match_interface_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.match_interface = func; +} + +/* no match interface */ +void +route_map_no_match_interface_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.no_match_interface = func; +} + +/* match ip address */ +void +route_map_match_ip_address_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.match_ip_address = func; +} + +/* no match ip address */ +void +route_map_no_match_ip_address_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.no_match_ip_address = func; +} + +/* match ip address prefix list */ +void +route_map_match_ip_address_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.match_ip_address_prefix_list = func; +} + +/* no match ip address prefix list */ +void +route_map_no_match_ip_address_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.no_match_ip_address_prefix_list = func; +} + +/* match ip next hop */ +void +route_map_match_ip_next_hop_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.match_ip_next_hop = func; +} + +/* no match ip next hop */ +void +route_map_no_match_ip_next_hop_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.no_match_ip_next_hop = func; +} + +/* match ip next hop prefix list */ +void +route_map_match_ip_next_hop_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.match_ip_next_hop_prefix_list = func; +} + +/* no match ip next hop prefix list */ +void +route_map_no_match_ip_next_hop_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.no_match_ip_next_hop_prefix_list = func; +} + +/* match ipv6 address */ +void +route_map_match_ipv6_address_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.match_ipv6_address = func; +} + +/* no match ipv6 address */ +void +route_map_no_match_ipv6_address_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.no_match_ipv6_address = func; +} + + +/* match ipv6 address prefix list */ +void +route_map_match_ipv6_address_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.match_ipv6_address_prefix_list = func; +} + +/* no match ipv6 address prefix list */ +void +route_map_no_match_ipv6_address_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.no_match_ipv6_address_prefix_list = func; +} + +/* match metric */ +void +route_map_match_metric_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.match_metric = func; +} + +/* no match metric */ +void +route_map_no_match_metric_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.no_match_metric = func; +} + +/* match tag */ +void +route_map_match_tag_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.match_tag = func; +} + +/* no match tag */ +void +route_map_no_match_tag_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)) +{ + rmap_match_set_hook.no_match_tag = func; +} + +/* set ip nexthop */ +void +route_map_set_ip_nexthop_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.set_ip_nexthop = func; +} + +/* no set ip nexthop */ +void +route_map_no_set_ip_nexthop_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.no_set_ip_nexthop = func; +} + +/* set ipv6 nexthop local */ +void +route_map_set_ipv6_nexthop_local_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.set_ipv6_nexthop_local = func; +} + +/* no set ipv6 nexthop local */ +void +route_map_no_set_ipv6_nexthop_local_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.no_set_ipv6_nexthop_local = func; +} + +/* set metric */ +void +route_map_set_metric_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.set_metric = func; +} + +/* no set metric */ +void +route_map_no_set_metric_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.no_set_metric = func; +} + +/* set tag */ +void +route_map_set_tag_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.set_tag = func; +} + +/* no set tag */ +void +route_map_no_set_tag_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)) +{ + rmap_match_set_hook.no_set_tag = func; +} + +int +generic_match_add (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type) +{ + int ret; + + ret = route_map_add_match (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (type != RMAP_EVENT_MATCH_ADDED) + { + route_map_upd8_dependency (type, arg, index->map->name); + } + return CMD_SUCCESS; +} + +int +generic_match_delete (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type) +{ + int ret; + char *dep_name = NULL; + const char *tmpstr; + char *rmap_name = NULL; + + if (type != RMAP_EVENT_MATCH_DELETED) + { + /* ignore the mundane, the types without any dependency */ + if (arg == NULL) + { + if ((tmpstr = route_map_get_match_arg(index, command)) != NULL) + dep_name = XSTRDUP(MTYPE_ROUTE_MAP_RULE, tmpstr); + } + else + { + dep_name = XSTRDUP(MTYPE_ROUTE_MAP_RULE, arg); + } + rmap_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, index->map->name); + } + + ret = route_map_delete_match (index, command, dep_name); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% BGP Can't find rule.%s", VTY_NEWLINE); + break; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE); + break; + } + if (dep_name) + XFREE(MTYPE_ROUTE_MAP_RULE, dep_name); + if (rmap_name) + XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); + return CMD_WARNING; + } + + if (type != RMAP_EVENT_MATCH_DELETED && dep_name) + route_map_upd8_dependency(type, dep_name, rmap_name); + + if (dep_name) + XFREE(MTYPE_ROUTE_MAP_RULE, dep_name); + if (rmap_name) + XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); + + return CMD_SUCCESS; +} + +int +generic_set_add (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_add_set (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +int +generic_set_delete (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_delete_set (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + + /* Route map rule. This rule has both `match' rule and `set' rule. */ struct route_map_rule { @@ -122,6 +711,7 @@ enum route_map_dep_type ROUTE_MAP_DEP_RMAP = 1, ROUTE_MAP_DEP_CLIST, ROUTE_MAP_DEP_ECLIST, + ROUTE_MAP_DEP_LCLIST, ROUTE_MAP_DEP_PLIST, ROUTE_MAP_DEP_ASPATH, ROUTE_MAP_DEP_FILTER, @@ -211,29 +801,29 @@ route_map_free_map (struct route_map *map) struct route_map_list *list; struct route_map_index *index; + if (map == NULL) + return; + while ((index = map->head) != NULL) route_map_index_delete (index, 0); list = &route_map_master; - if (map != NULL) - { - QOBJ_UNREG (map); + QOBJ_UNREG (map); - if (map->next) - map->next->prev = map->prev; - else - list->tail = map->prev; + if (map->next) + map->next->prev = map->prev; + else + list->tail = map->prev; - if (map->prev) - map->prev->next = map->next; - else - list->head = map->next; + if (map->prev) + map->prev->next = map->next; + else + list->head = map->next; - hash_release(route_map_master_hash, map); - XFREE (MTYPE_ROUTE_MAP_NAME, map->name); - XFREE (MTYPE_ROUTE_MAP, map); - } + hash_release(route_map_master_hash, map); + XFREE (MTYPE_ROUTE_MAP_NAME, map->name); + XFREE (MTYPE_ROUTE_MAP, map); } /* Route map delete from list. */ @@ -401,9 +991,11 @@ vty_show_route_map_entry (struct vty *vty, struct route_map *map) /* Print the name of the protocol */ if (zlog_default) - vty_out (vty, "%s", zlog_proto_names[zlog_default->protocol]); - if (zlog_default->instance) - vty_out (vty, " %d", zlog_default->instance); + { + vty_out (vty, "%s", zlog_protoname()); + if (zlog_default->instance) + vty_out (vty, " %d", zlog_default->instance); + } vty_out (vty, ":%s", VTY_NEWLINE); for (index = map->head; index; index = index->next) @@ -460,9 +1052,8 @@ vty_show_route_map (struct vty *vty, const char *name) } else { - if (zlog_default) - vty_out (vty, "%s", zlog_proto_names[zlog_default->protocol]); - if (zlog_default->instance) + vty_out (vty, "%s", zlog_protoname()); + if (zlog_default && zlog_default->instance) vty_out (vty, " %d", zlog_default->instance); vty_out (vty, ": 'route-map %s' not found%s", name, VTY_NEWLINE); return CMD_SUCCESS; @@ -1023,9 +1614,8 @@ route_map_apply (struct route_map *map, struct prefix *prefix, if (recursion > RMAP_RECURSION_LIMIT) { - zlog (NULL, LOG_WARNING, - "route-map recursion limit (%d) reached, discarding route", - RMAP_RECURSION_LIMIT); + zlog_warn("route-map recursion limit (%d) reached, discarding route", + RMAP_RECURSION_LIMIT); recursion = 0; return RMAP_DENYMATCH; } @@ -1229,6 +1819,7 @@ route_map_dep_update (struct hash *dephash, const char *dep_name, case RMAP_EVENT_CLIST_ADDED: case RMAP_EVENT_ECLIST_ADDED: case RMAP_EVENT_ASLIST_ADDED: + case RMAP_EVENT_LLIST_ADDED: case RMAP_EVENT_CALL_ADDED: case RMAP_EVENT_FILTER_ADDED: if (rmap_debug) @@ -1250,6 +1841,7 @@ route_map_dep_update (struct hash *dephash, const char *dep_name, case RMAP_EVENT_CLIST_DELETED: case RMAP_EVENT_ECLIST_DELETED: case RMAP_EVENT_ASLIST_DELETED: + case RMAP_EVENT_LLIST_DELETED: case RMAP_EVENT_CALL_DELETED: case RMAP_EVENT_FILTER_DELETED: if (rmap_debug) @@ -1312,6 +1904,10 @@ route_map_get_dep_hash (route_map_event_t event) case RMAP_EVENT_ASLIST_DELETED: upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_ASPATH]; break; + case RMAP_EVENT_LLIST_ADDED: + case RMAP_EVENT_LLIST_DELETED: + upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_LCLIST]; + break; case RMAP_EVENT_CALL_ADDED: case RMAP_EVENT_CALL_DELETED: upd8_hash = route_map_dep_hash[ROUTE_MAP_DEP_RMAP]; @@ -1386,52 +1982,586 @@ route_map_notify_dependencies (const char *affected_name, route_map_event_t even XFREE (MTYPE_ROUTE_MAP_NAME, name); } + /* VTY related functions. */ -DEFUN (route_map, - route_map_cmd, - "route-map WORD (deny|permit) <1-65535>", - "Create route-map or enter route-map command mode\n" - "Route map tag\n" - "Route map denies set operations\n" - "Route map permits set operations\n" - "Sequence to insert to/delete from existing route-map entry\n") +DEFUN (match_interface, + match_interface_cmd, + "match interface WORD", + MATCH_STR + "match first hop interface of route\n" + "Interface name\n") +{ + int idx_word = 2; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.match_interface) + return rmap_match_set_hook.match_interface (vty, index, "interface", argv[idx_word]->arg, RMAP_EVENT_MATCH_ADDED); + return CMD_SUCCESS; +} + +DEFUN (no_match_interface, + no_match_interface_cmd, + "no match interface [INTERFACE]", + NO_STR + MATCH_STR + "Match first hop interface of route\n" + "Interface name\n") { - int permit; - unsigned long pref; - struct route_map *map; - struct route_map_index *index; - char *endptr = NULL; + char *iface = (argc == 4) ? argv[3]->arg : NULL; + VTY_DECLVAR_CONTEXT (route_map_index, index); - /* Permit check. */ - if (strncmp (argv[1], "permit", strlen (argv[1])) == 0) - permit = RMAP_PERMIT; - else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0) - permit = RMAP_DENY; - else + if (rmap_match_set_hook.no_match_interface) + return rmap_match_set_hook.no_match_interface (vty, index, "interface", iface, RMAP_EVENT_MATCH_DELETED); + return CMD_SUCCESS; +} + + +DEFUN (match_ip_address, + match_ip_address_cmd, + "match ip address <(1-199)|(1300-2699)|WORD>", + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + int idx_acl = 3; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.match_ip_address) + return rmap_match_set_hook.match_ip_address (vty, index, "ip address", argv[idx_acl]->arg, + RMAP_EVENT_FILTER_ADDED); + return CMD_SUCCESS; +} + + +DEFUN (no_match_ip_address, + no_match_ip_address_cmd, + "no match ip address [<(1-199)|(1300-2699)|WORD>]", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + int idx_word = 4; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.no_match_ip_address) + { + if (argc <= idx_word) + return rmap_match_set_hook.no_match_ip_address (vty, index, "ip address", NULL, + RMAP_EVENT_FILTER_DELETED); + return rmap_match_set_hook.no_match_ip_address (vty, index, "ip address", argv[idx_word]->arg, + RMAP_EVENT_FILTER_DELETED); + } + return CMD_SUCCESS; +} + + +DEFUN (match_ip_address_prefix_list, + match_ip_address_prefix_list_cmd, + "match ip address prefix-list WORD", + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + int idx_word = 4; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.match_ip_address_prefix_list) + return rmap_match_set_hook.match_ip_address_prefix_list (vty, index, "ip address prefix-list", + argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED); + return CMD_SUCCESS; +} + + +DEFUN (no_match_ip_address_prefix_list, + no_match_ip_address_prefix_list_cmd, + "no match ip address prefix-list [WORD]", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + int idx_word = 5; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.no_match_ip_address_prefix_list) + { + if (argc <= idx_word) + return rmap_match_set_hook.no_match_ip_address_prefix_list (vty, index, "ip address prefix-list", + NULL, RMAP_EVENT_PLIST_DELETED); + return rmap_match_set_hook.no_match_ip_address_prefix_list(vty, index, "ip address prefix-list", + argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED); + } + return CMD_SUCCESS; +} + + +DEFUN (match_ip_next_hop, + match_ip_next_hop_cmd, + "match ip next-hop <(1-199)|(1300-2699)|WORD>", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + int idx_acl = 3; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.match_ip_next_hop) + return rmap_match_set_hook.match_ip_next_hop (vty, index, "ip next-hop", argv[idx_acl]->arg, + RMAP_EVENT_FILTER_ADDED); + return CMD_SUCCESS; +} + + +DEFUN (no_match_ip_next_hop, + no_match_ip_next_hop_cmd, + "no match ip next-hop [<(1-199)|(1300-2699)|WORD>]", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + int idx_word = 4; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.no_match_ip_next_hop) + { + if (argc <= idx_word) + return rmap_match_set_hook.no_match_ip_next_hop (vty, index, "ip next-hop", NULL, + RMAP_EVENT_FILTER_DELETED); + return rmap_match_set_hook.no_match_ip_next_hop (vty, index, "ip next-hop", argv[idx_word]->arg, + RMAP_EVENT_FILTER_DELETED); + } + return CMD_SUCCESS; +} + + +DEFUN (match_ip_next_hop_prefix_list, + match_ip_next_hop_prefix_list_cmd, + "match ip next-hop prefix-list WORD", + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + int idx_word = 4; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.match_ip_next_hop_prefix_list) + return rmap_match_set_hook.match_ip_next_hop_prefix_list (vty, index, "ip next-hop prefix-list", + argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED); + return CMD_SUCCESS; +} + +DEFUN (no_match_ip_next_hop_prefix_list, + no_match_ip_next_hop_prefix_list_cmd, + "no match ip next-hop prefix-list [WORD]", + NO_STR + MATCH_STR + IP_STR + "Match next-hop address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + int idx_word = 5; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.no_match_ip_next_hop) + { + if (argc <= idx_word) + return rmap_match_set_hook.no_match_ip_next_hop (vty, index, "ip next-hop prefix-list", + NULL, RMAP_EVENT_PLIST_DELETED); + return rmap_match_set_hook.no_match_ip_next_hop (vty, index, "ip next-hop prefix-list", + argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED); + } + return CMD_SUCCESS; +} + + +DEFUN (match_ipv6_address, + match_ipv6_address_cmd, + "match ipv6 address WORD", + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + int idx_word = 3; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.match_ipv6_address) + return rmap_match_set_hook.match_ipv6_address (vty, index, "ipv6 address", argv[idx_word]->arg, + RMAP_EVENT_FILTER_ADDED); + return CMD_SUCCESS; +} + +DEFUN (no_match_ipv6_address, + no_match_ipv6_address_cmd, + "no match ipv6 address WORD", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + int idx_word = 4; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.no_match_ipv6_address) + return rmap_match_set_hook.no_match_ipv6_address (vty, index, "ipv6 address", argv[idx_word]->arg, + RMAP_EVENT_FILTER_DELETED); + return CMD_SUCCESS; +} + + +DEFUN (match_ipv6_address_prefix_list, + match_ipv6_address_prefix_list_cmd, + "match ipv6 address prefix-list WORD", + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + int idx_word = 4; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.match_ipv6_address_prefix_list) + return rmap_match_set_hook.match_ipv6_address_prefix_list (vty, index, "ipv6 address prefix-list", + argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED); + return CMD_SUCCESS; +} + +DEFUN (no_match_ipv6_address_prefix_list, + no_match_ipv6_address_prefix_list_cmd, + "no match ipv6 address prefix-list WORD", + NO_STR + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + int idx_word = 5; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.no_match_ipv6_address_prefix_list) + return rmap_match_set_hook.no_match_ipv6_address_prefix_list(vty, index, "ipv6 address prefix-list", + argv[idx_word]->arg, RMAP_EVENT_PLIST_DELETED); + return CMD_SUCCESS; +} + + +DEFUN (match_metric, + match_metric_cmd, + "match metric (0-4294967295)", + MATCH_STR + "Match metric of route\n" + "Metric value\n") +{ + int idx_number = 2; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.match_metric) + return rmap_match_set_hook.match_metric(vty, index, "metric", argv[idx_number]->arg, + RMAP_EVENT_MATCH_ADDED); + return CMD_SUCCESS; +} + + +DEFUN (no_match_metric, + no_match_metric_cmd, + "no match metric [(0-4294967295)]", + NO_STR + MATCH_STR + "Match metric of route\n" + "Metric value\n") +{ + int idx_number = 3; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.no_match_metric) { - vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE); + if (argc <= idx_number) + return rmap_match_set_hook.no_match_metric (vty, index, "metric", + NULL, RMAP_EVENT_MATCH_DELETED); + return rmap_match_set_hook.no_match_metric(vty, index, "metric", + argv[idx_number]->arg, + RMAP_EVENT_MATCH_DELETED); + } + return CMD_SUCCESS; +} + + +DEFUN (match_tag, + match_tag_cmd, + "match tag (1-4294967295)", + MATCH_STR + "Match tag of route\n" + "Tag value\n") +{ + int idx_number = 2; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.match_tag) + return rmap_match_set_hook.match_tag(vty, index, "tag", argv[idx_number]->arg, + RMAP_EVENT_MATCH_ADDED); + return CMD_SUCCESS; +} + + +DEFUN (no_match_tag, + no_match_tag_cmd, + "no match tag [(1-4294967295)]", + NO_STR + MATCH_STR + "Match tag of route\n" + "Tag value\n") +{ + VTY_DECLVAR_CONTEXT (route_map_index, index); + + int idx = 0; + char *arg = argv_find (argv, argc, "(1-4294967295)", &idx) ? + argv[idx]->arg : NULL; + + if (rmap_match_set_hook.no_match_tag) + return rmap_match_set_hook.no_match_tag (vty, index, "tag", arg, + RMAP_EVENT_MATCH_DELETED); + return CMD_SUCCESS; +} + + +DEFUN (set_ip_nexthop, + set_ip_nexthop_cmd, + "set ip next-hop A.B.C.D", + SET_STR + IP_STR + "Next hop address\n" + "IP address of next hop\n") +{ + int idx_ipv4 = 3; + union sockunion su; + int ret; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + ret = str2sockunion (argv[idx_ipv4]->arg, &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed nexthop address%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (su.sin.sin_addr.s_addr == 0 || + IPV4_CLASS_DE(su.sin.sin_addr.s_addr)) + { + vty_out (vty, "%% nexthop address cannot be 0.0.0.0, multicast " + "or reserved%s", VTY_NEWLINE); return CMD_WARNING; } - /* Preference check. */ - pref = strtoul (argv[2], &endptr, 10); - if (pref == ULONG_MAX || *endptr != '\0') + if (rmap_match_set_hook.set_ip_nexthop) + return rmap_match_set_hook.set_ip_nexthop(vty, index, "ip next-hop", argv[idx_ipv4]->arg); + return CMD_SUCCESS; +} + + +DEFUN (no_set_ip_nexthop, + no_set_ip_nexthop_cmd, + "no set ip next-hop [<peer-address|A.B.C.D>]", + NO_STR + SET_STR + IP_STR + "Next hop address\n" + "Use peer address (for BGP only)\n" + "IP address of next hop\n") +{ + int idx_peer = 4; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.no_set_ip_nexthop) { - vty_out (vty, "the fourth field must be positive integer%s", - VTY_NEWLINE); + if (argc <= idx_peer) + return rmap_match_set_hook.no_set_ip_nexthop (vty, index, "ip next-hop", NULL); + return rmap_match_set_hook.no_set_ip_nexthop (vty, index, "ip next-hop", argv[idx_peer]->arg); + } + return CMD_SUCCESS; +} + + +DEFUN (set_ipv6_nexthop_local, + set_ipv6_nexthop_local_cmd, + "set ipv6 next-hop local X:X::X:X", + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n" + "IPv6 address of next hop\n") +{ + int idx_ipv6 = 4; + struct in6_addr addr; + int ret; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + ret = inet_pton (AF_INET6, argv[idx_ipv6]->arg, &addr); + if (!ret) + { + vty_out (vty, "%% Malformed nexthop address%s", VTY_NEWLINE); return CMD_WARNING; } - if (pref == 0 || pref > 65535) + if (!IN6_IS_ADDR_LINKLOCAL(&addr)) { - vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE); + vty_out (vty, "%% Invalid link-local nexthop address%s", VTY_NEWLINE); return CMD_WARNING; } + if (rmap_match_set_hook.set_ipv6_nexthop_local) + return rmap_match_set_hook.set_ipv6_nexthop_local (vty, index, "ipv6 next-hop local", argv[idx_ipv6]->arg); + return CMD_SUCCESS; +} + + +DEFUN (no_set_ipv6_nexthop_local, + no_set_ipv6_nexthop_local_cmd, + "no set ipv6 next-hop local [X:X::X:X]", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 local address\n" + "IPv6 address of next hop\n") +{ + int idx_ipv6 = 5; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.no_set_ipv6_nexthop_local) + { + if (argc <= idx_ipv6) + return rmap_match_set_hook.no_set_ipv6_nexthop_local (vty, index, "ipv6 next-hop local", NULL); + return rmap_match_set_hook.no_set_ipv6_nexthop_local (vty, index, "ipv6 next-hop local", argv[5]->arg); + } + return CMD_SUCCESS; +} + +DEFUN (set_metric, + set_metric_cmd, + "set metric <(0-4294967295)|rtt|+rtt|-rtt|+metric|-metric>", + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n" + "Assign round trip time\n" + "Add round trip time\n" + "Subtract round trip time\n" + "Add metric\n" + "Subtract metric\n") +{ + int idx_number = 2; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.set_metric) + return rmap_match_set_hook.set_metric (vty, index, "metric", argv[idx_number]->arg); + return CMD_SUCCESS; +} + + +DEFUN (no_set_metric, + no_set_metric_cmd, + "no set metric [(0-4294967295)]", + NO_STR + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") +{ + int idx_number = 3; + VTY_DECLVAR_CONTEXT (route_map_index, index); + + if (rmap_match_set_hook.no_set_metric) + { + if (argc <= idx_number) + return rmap_match_set_hook.no_set_metric (vty, index, "metric", NULL); + return rmap_match_set_hook.no_set_metric (vty, index, "metric", argv[idx_number]->arg); + } + return CMD_SUCCESS; +} + + +DEFUN (set_tag, + set_tag_cmd, + "set tag (1-4294967295)", + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + VTY_DECLVAR_CONTEXT (route_map_index, index); + + int idx_number = 2; + if (rmap_match_set_hook.set_tag) + return rmap_match_set_hook.set_tag (vty, index, "tag", argv[idx_number]->arg); + return CMD_SUCCESS; +} + + +DEFUN (no_set_tag, + no_set_tag_cmd, + "no set tag [(1-4294967295)]", + NO_STR + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + VTY_DECLVAR_CONTEXT (route_map_index, index); + + int idx_number = 3; + if (rmap_match_set_hook.no_set_tag) + { + if (argc <= idx_number) + return rmap_match_set_hook.no_set_tag (vty, index, "tag", NULL); + return rmap_match_set_hook.no_set_tag (vty, index, "tag", argv[idx_number]->arg); + } + return CMD_SUCCESS; +} + + + +DEFUN (route_map, + route_map_cmd, + "route-map WORD <deny|permit> (1-65535)", + "Create route-map or enter route-map command mode\n" + "Route map tag\n" + "Route map denies set operations\n" + "Route map permits set operations\n" + "Sequence to insert to/delete from existing route-map entry\n") +{ + int idx_word = 1; + int idx_permit_deny = 2; + int idx_number = 3; + struct route_map *map; + struct route_map_index *index; + char *endptr = NULL; + int permit = argv[idx_permit_deny]->arg[0] == 'p' ? RMAP_PERMIT : RMAP_DENY; + unsigned long pref = strtoul (argv[idx_number]->arg, &endptr, 10); + const char *mapname = argv[idx_word]->arg; + /* Get route map. */ - map = route_map_get (argv[0]); + map = route_map_get (mapname); index = route_map_index_get (map, permit, pref); - VTY_PUSH_CONTEXT_COMPAT (RMAP_NODE, index); + VTY_PUSH_CONTEXT (RMAP_NODE, index); return CMD_SUCCESS; } @@ -1442,13 +2572,14 @@ DEFUN (no_route_map_all, "Create route-map or enter route-map command mode\n" "Route map tag\n") { + int idx_word = 2; + const char *mapname = argv[idx_word]->arg; struct route_map *map; - map = route_map_lookup_by_name (argv[0]); + map = route_map_lookup_by_name (mapname); if (map == NULL) { - vty_out (vty, "%% Could not find route-map %s%s", - argv[0], VTY_NEWLINE); + vty_out (vty, "%% Could not find route-map %s%s", mapname, VTY_NEWLINE); return CMD_WARNING; } @@ -1459,7 +2590,7 @@ DEFUN (no_route_map_all, DEFUN (no_route_map, no_route_map_cmd, - "no route-map WORD (deny|permit) <1-65535>", + "no route-map WORD <deny|permit> (1-65535)", NO_STR "Create route-map or enter route-map command mode\n" "Route map tag\n" @@ -1467,43 +2598,22 @@ DEFUN (no_route_map, "Route map permits set operations\n" "Sequence to insert to/delete from existing route-map entry\n") { - int permit; - unsigned long pref; + int idx_word = 2; + int idx_permit_deny = 3; + int idx_number = 4; struct route_map *map; struct route_map_index *index; char *endptr = NULL; - - /* Permit check. */ - if (strncmp (argv[1], "permit", strlen (argv[1])) == 0) - permit = RMAP_PERMIT; - else if (strncmp (argv[1], "deny", strlen (argv[1])) == 0) - permit = RMAP_DENY; - else - { - vty_out (vty, "the third field must be [permit|deny]%s", VTY_NEWLINE); - return CMD_WARNING; - } - - /* Preference. */ - pref = strtoul (argv[2], &endptr, 10); - if (pref == ULONG_MAX || *endptr != '\0') - { - vty_out (vty, "the fourth field must be positive integer%s", - VTY_NEWLINE); - return CMD_WARNING; - } - if (pref == 0 || pref > 65535) - { - vty_out (vty, "the fourth field must be <1-65535>%s", VTY_NEWLINE); - return CMD_WARNING; - } + int permit = argv[idx_permit_deny]->arg[0] == 'p' ? RMAP_PERMIT : RMAP_DENY; + const char *prefstr = argv[idx_number]->arg; + const char *mapname = argv[idx_word]->arg; + unsigned long pref = strtoul (prefstr, &endptr, 10); /* Existence check. */ - map = route_map_lookup_by_name (argv[0]); + map = route_map_lookup_by_name (mapname); if (map == NULL) { - vty_out (vty, "%% Could not find route-map %s%s", - argv[0], VTY_NEWLINE); + vty_out (vty, "%% Could not find route-map %s%s", mapname, VTY_NEWLINE); return CMD_WARNING; } @@ -1512,7 +2622,7 @@ DEFUN (no_route_map, if (index == NULL) { vty_out (vty, "%% Could not find route-map entry %s %s%s", - argv[0], argv[2], VTY_NEWLINE); + mapname, prefstr, VTY_NEWLINE); return CMD_WARNING; } @@ -1556,7 +2666,7 @@ DEFUN (no_rmap_onmatch_next, "Next clause\n") { struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); - + if (index) index->exitpolicy = RMAP_EXIT; @@ -1565,11 +2675,14 @@ DEFUN (no_rmap_onmatch_next, DEFUN (rmap_onmatch_goto, rmap_onmatch_goto_cmd, - "on-match goto <1-65535>", + "on-match goto (1-65535)", "Exit policy on matches\n" "Goto Clause number\n" "Number\n") { + int idx = 0; + char *num = argv_find (argv, argc, "(1-65535)", &idx) ? argv[idx]->arg : NULL; + struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); int d = 0; @@ -1583,16 +2696,15 @@ DEFUN (rmap_onmatch_goto, return CMD_WARNING; } - if (argc == 1 && argv[0]) - VTY_GET_INTEGER_RANGE("route-map index", d, argv[0], 1, 65535); + if (num) + VTY_GET_INTEGER_RANGE("route-map index", d, num, 1, 65535); else d = index->pref + 1; if (d <= index->pref) { /* Can't allow you to do that, Dave */ - vty_out (vty, "can't jump backwards in route-maps%s", - VTY_NEWLINE); + vty_out (vty, "can't jump backwards in route-maps%s", VTY_NEWLINE); return CMD_WARNING; } else @@ -1619,31 +2731,28 @@ DEFUN (no_rmap_onmatch_goto, return CMD_SUCCESS; } -/* Cisco/GNU Zebra compatible ALIASes for on-match next */ -ALIAS (rmap_onmatch_goto, +/* Cisco/GNU Zebra compatibility aliases */ +/* ALIAS_FIXME */ +DEFUN (rmap_continue, rmap_continue_cmd, - "continue", - "Continue on a different entry within the route-map\n") - -ALIAS (no_rmap_onmatch_goto, - no_rmap_continue_cmd, - "no continue", - NO_STR - "Continue on a different entry within the route-map\n") - -/* GNU Zebra compatible */ -ALIAS (rmap_onmatch_goto, - rmap_continue_seq_cmd, - "continue <1-65535>", + "continue (1-65535)", "Continue on a different entry within the route-map\n" "Route-map entry sequence number\n") +{ + return rmap_onmatch_goto (self, vty, argc, argv); +} -ALIAS (no_rmap_onmatch_goto, - no_rmap_continue_seq, - "no continue <1-65535>", +/* ALIAS_FIXME */ +DEFUN (no_rmap_continue, + no_rmap_continue_cmd, + "no continue [(1-65535)]", NO_STR "Continue on a different entry within the route-map\n" "Route-map entry sequence number\n") +{ + return no_rmap_onmatch_goto (self, vty, argc, argv); +} + DEFUN (rmap_show_name, rmap_show_name_cmd, @@ -1652,37 +2761,31 @@ DEFUN (rmap_show_name, "route-map information\n" "route-map name\n") { - const char *name = NULL; - if (argc) - name = argv[0]; - return vty_show_route_map (vty, name); + int idx_word = 2; + const char *name = (argc == 3) ? argv[idx_word]->arg : NULL; + return vty_show_route_map (vty, name); } -ALIAS (rmap_onmatch_goto, - rmap_continue_index_cmd, - "continue <1-65535>", - "Exit policy on matches\n" - "Goto Clause number\n") - DEFUN (rmap_call, rmap_call_cmd, "call WORD", "Jump to another Route-Map after match+set\n" "Target route-map name\n") { + int idx_word = 1; struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); + const char *rmap = argv[idx_word]->arg; - if (index) + assert(index); + + if (index->nextrm) { - if (index->nextrm) - { - route_map_upd8_dependency (RMAP_EVENT_CALL_DELETED, - index->nextrm, - index->map->name); - XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); - } - index->nextrm = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[0]); + route_map_upd8_dependency (RMAP_EVENT_CALL_DELETED, + index->nextrm, + index->map->name); + XFREE (MTYPE_ROUTE_MAP_NAME, index->nextrm); } + index->nextrm = XSTRDUP (MTYPE_ROUTE_MAP_NAME, rmap); /* Execute event hook. */ route_map_upd8_dependency (RMAP_EVENT_CALL_ADDED, @@ -1713,17 +2816,18 @@ DEFUN (no_rmap_call, DEFUN (rmap_description, rmap_description_cmd, - "description .LINE", + "description LINE...", "Route-map comment\n" "Comment describing this route-map rule\n") { + int idx_line = 1; struct route_map_index *index = VTY_GET_CONTEXT (route_map_index); if (index) { if (index->description) XFREE (MTYPE_TMP, index->description); - index->description = argv_concat (argv, argc, 0); + index->description = argv_concat (argv, argc, idx_line); } return CMD_SUCCESS; } @@ -1884,11 +2988,10 @@ route_map_init (void) install_element (RMAP_NODE, &no_rmap_onmatch_next_cmd); install_element (RMAP_NODE, &rmap_onmatch_goto_cmd); install_element (RMAP_NODE, &no_rmap_onmatch_goto_cmd); - - /* Install the continue stuff (ALIAS of on-match). */ install_element (RMAP_NODE, &rmap_continue_cmd); install_element (RMAP_NODE, &no_rmap_continue_cmd); - install_element (RMAP_NODE, &rmap_continue_index_cmd); + + /* Install the continue stuff (ALIAS of on-match). */ /* Install the call stuff. */ install_element (RMAP_NODE, &rmap_call_cmd); @@ -1900,4 +3003,44 @@ route_map_init (void) /* Install show command */ install_element (ENABLE_NODE, &rmap_show_name_cmd); + + install_element (RMAP_NODE, &match_interface_cmd); + install_element (RMAP_NODE, &no_match_interface_cmd); + + install_element (RMAP_NODE, &match_ip_address_cmd); + install_element (RMAP_NODE, &no_match_ip_address_cmd); + + install_element (RMAP_NODE, &match_ip_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_address_prefix_list_cmd); + + install_element (RMAP_NODE, &match_ip_next_hop_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_cmd); + + install_element (RMAP_NODE, &match_ip_next_hop_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ip_next_hop_prefix_list_cmd); + + install_element (RMAP_NODE, &match_ipv6_address_cmd); + install_element (RMAP_NODE, &no_match_ipv6_address_cmd); + + install_element (RMAP_NODE, &match_ipv6_address_prefix_list_cmd); + install_element (RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd); + + install_element (RMAP_NODE, &match_metric_cmd); + install_element (RMAP_NODE, &no_match_metric_cmd); + + install_element (RMAP_NODE, &match_tag_cmd); + install_element (RMAP_NODE, &no_match_tag_cmd); + + install_element (RMAP_NODE, &set_ip_nexthop_cmd); + install_element (RMAP_NODE, &no_set_ip_nexthop_cmd); + + install_element (RMAP_NODE, &set_ipv6_nexthop_local_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_cmd); + + install_element (RMAP_NODE, &set_metric_cmd); + install_element (RMAP_NODE, &no_set_metric_cmd); + + install_element (RMAP_NODE, &set_tag_cmd); + install_element (RMAP_NODE, &no_set_tag_cmd); + } diff --git a/lib/routemap.h b/lib/routemap.h index b5cdd27277..b378c64eae 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -25,6 +25,8 @@ #include "prefix.h" #include "memory.h" #include "qobj.h" +#include "vty.h" + DECLARE_MTYPE(ROUTE_MAP_NAME) DECLARE_MTYPE(ROUTE_MAP_RULE) DECLARE_MTYPE(ROUTE_MAP_COMPILED) @@ -82,6 +84,8 @@ typedef enum RMAP_EVENT_CLIST_DELETED, RMAP_EVENT_ECLIST_ADDED, RMAP_EVENT_ECLIST_DELETED, + RMAP_EVENT_LLIST_ADDED, + RMAP_EVENT_LLIST_DELETED, RMAP_EVENT_ASLIST_ADDED, RMAP_EVENT_ASLIST_DELETED, RMAP_EVENT_FILTER_ADDED, @@ -234,6 +238,172 @@ extern void route_map_upd8_dependency (route_map_event_t type, const char *arg, extern void route_map_notify_dependencies (const char *affected_name, route_map_event_t event); +extern int generic_match_add (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); + +extern int generic_match_delete (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type); +extern int generic_set_add (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); +extern int generic_set_delete (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + +/* match interface */ +extern void route_map_match_interface_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* no match interface */ +extern void route_map_no_match_interface_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* match ip address */ +extern void route_map_match_ip_address_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* no match ip address */ +extern void route_map_no_match_ip_address_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* match ip address prefix list */ +extern void route_map_match_ip_address_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* no match ip address prefix list */ +extern void route_map_no_match_ip_address_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* match ip next hop */ +extern void route_map_match_ip_next_hop_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* no match ip next hop */ +extern void route_map_no_match_ip_next_hop_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* match ip next hop prefix list */ +extern void route_map_match_ip_next_hop_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* no match ip next hop prefix list */ +extern void route_map_no_match_ip_next_hop_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* match ipv6 address */ +extern void route_map_match_ipv6_address_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* no match ipv6 address */ +extern void route_map_no_match_ipv6_address_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* match ipv6 address prefix list */ +extern void route_map_match_ipv6_address_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* no match ipv6 address prefix list */ +extern void route_map_no_match_ipv6_address_prefix_list_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* match metric */ +extern void route_map_match_metric_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* no match metric */ +extern void route_map_no_match_metric_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* match tag */ +extern void route_map_match_tag_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* no match tag */ +extern void route_map_no_match_tag_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type)); +/* set ip nexthop */ +extern void route_map_set_ip_nexthop_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)); +/* no set ip nexthop */ +extern void route_map_no_set_ip_nexthop_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)); +/* set ipv6 nexthop local */ +extern void route_map_set_ipv6_nexthop_local_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)); +/* no set ipv6 nexthop local */ +extern void route_map_no_set_ipv6_nexthop_local_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)); +/* set metric */ +extern void route_map_set_metric_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)); +/* no set metric */ +extern void route_map_no_set_metric_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)); +/* set tag */ +extern void route_map_set_tag_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)); +/* no set tag */ +extern void route_map_no_set_tag_hook (int (*func) (struct vty *vty, + struct route_map_index *index, + const char *command, + const char *arg)); + extern void *route_map_rule_tag_compile (const char *arg); extern void route_map_rule_tag_free (void *rule); diff --git a/lib/skiplist.c b/lib/skiplist.c index 2a90b2c7c6..05f489c905 100644 --- a/lib/skiplist.c +++ b/lib/skiplist.c @@ -439,7 +439,8 @@ skiplist_next_value( return -1; *valuePointer = q->value; - *cursor = q; + if (cursor) + *cursor = q; CHECKLAST(l); return 0; } diff --git a/lib/smux.c b/lib/smux.c index a9c99e1545..3abfadcd28 100644 --- a/lib/smux.c +++ b/lib/smux.c @@ -154,16 +154,10 @@ static int smux_socket (void) { int ret; -#ifdef HAVE_IPV6 struct addrinfo hints, *res0, *res; int gai; -#else - struct sockaddr_in serv; - struct servent *sp; -#endif int sock = 0; -#ifdef HAVE_IPV6 memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; @@ -183,9 +177,7 @@ smux_socket (void) for(res=res0; res; res=res->ai_next) { if (res->ai_family != AF_INET -#ifdef HAVE_IPV6 && res->ai_family != AF_INET6 -#endif /* HAVE_IPV6 */ ) continue; @@ -206,40 +198,6 @@ smux_socket (void) freeaddrinfo(res0); if (sock < 0) zlog_warn ("Can't connect to SNMP agent with SMUX"); -#else - sock = socket (AF_INET, SOCK_STREAM, 0); - if (sock < 0) - { - zlog_warn ("Can't make socket for SNMP"); - return -1; - } - - memset (&serv, 0, sizeof (struct sockaddr_in)); - serv.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - serv.sin_len = sizeof (struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - - sp = getservbyname ("smux", "tcp"); - if (sp != NULL) - serv.sin_port = sp->s_port; - else - serv.sin_port = htons (SMUX_PORT_DEFAULT); - - serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - - sockopt_reuseaddr (sock); - sockopt_reuseport (sock); - - ret = connect (sock, (struct sockaddr *) &serv, sizeof (struct sockaddr_in)); - if (ret < 0) - { - close (sock); - smux_sock = -1; - zlog_warn ("Can't connect to SNMP agent with SMUX"); - return -1; - } -#endif return sock; } @@ -1370,7 +1328,8 @@ DEFUN (smux_peer, "SNMP MUX peer settings\n" "Object ID used in SMUX peering\n") { - if (smux_peer_oid (vty, argv[0], NULL) == 0) + int idx_oid = 2; + if (smux_peer_oid (vty, argv[idx_oid]->arg, NULL) == 0) { smux_start(); return CMD_SUCCESS; @@ -1387,7 +1346,8 @@ DEFUN (smux_peer_password, "SMUX peering object ID\n" "SMUX peering password\n") { - if (smux_peer_oid (vty, argv[0], argv[1]) == 0) + int idx_oid = 2; + if (smux_peer_oid (vty, argv[idx_oid]->arg, argv[3]->rg) == 0) { smux_start(); return CMD_SUCCESS; @@ -1398,32 +1358,17 @@ DEFUN (smux_peer_password, DEFUN (no_smux_peer, no_smux_peer_cmd, - "no smux peer", + "no smux peer [OID [PASSWORD]]", NO_STR "SNMP MUX protocol settings\n" - "SNMP MUX peer settings\n") + "SNMP MUX peer settings\n" + "SMUX peering object ID\n" + "SMUX peering password\n") { smux_stop(); return smux_peer_default (); } -ALIAS (no_smux_peer, - no_smux_peer_oid_cmd, - "no smux peer OID", - NO_STR - "SNMP MUX protocol settings\n" - "SNMP MUX peer settings\n" - "SMUX peering object ID\n") - -ALIAS (no_smux_peer, - no_smux_peer_oid_password_cmd, - "no smux peer OID PASSWORD", - NO_STR - "SNMP MUX protocol settings\n" - "SNMP MUX peer settings\n" - "SMUX peering object ID\n" - "SMUX peering password\n") - static int config_write_smux (struct vty *vty) { diff --git a/lib/sockopt.c b/lib/sockopt.c index e661b4cc56..2a9f907cb3 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -29,29 +29,6 @@ #include "sockopt.h" #include "sockunion.h" -/* Replace the path of given defaultpath with newpath, but keep filename */ -void -set_socket_path (char *path, const char *defaultpath, char *newpath, int maxsize) -{ - const char *sock_name; - - sock_name = strrchr(defaultpath, '/'); - if (sock_name) - /* skip '/' */ - sock_name++; - else - /* - * VTYSH_PATH configured as relative path - * during config? Should really never happen for - * sensible config - */ - sock_name = defaultpath; - - strlcpy (path, newpath, maxsize); - strlcat (path, "/", maxsize); - strlcat (path, sock_name, maxsize); -} - void setsockopt_so_recvbuf (int sock, int size) { @@ -109,7 +86,6 @@ getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) return NULL; } -#ifdef HAVE_IPV6 /* Set IPv6 packet info to the socket. */ int setsockopt_ipv6_pktinfo (int sock, int val) @@ -221,7 +197,6 @@ setsockopt_ipv6_tclass(int sock, int tclass) #endif return ret; } -#endif /* HAVE_IPV6 */ /* * Process multicast socket options for IPv4 in an OS-dependent manner. @@ -467,11 +442,9 @@ setsockopt_ifindex (int af, int sock, ifindex_t val) case AF_INET: ret = setsockopt_ipv4_ifindex (sock, val); break; -#ifdef HAVE_IPV6 case AF_INET6: ret = setsockopt_ipv6_pktinfo (sock, val); break; -#endif default: zlog_warn ("setsockopt_ifindex: unknown address family %d", af); } @@ -558,11 +531,9 @@ getsockopt_ifindex (int af, struct msghdr *msgh) case AF_INET: return (getsockopt_ipv4_ifindex (msgh)); break; -#ifdef HAVE_IPV6 case AF_INET6: return (getsockopt_ipv6_ifindex (msgh)); break; -#endif default: zlog_warn ("getsockopt_ifindex: unknown address family %d", af); return 0; @@ -669,7 +640,6 @@ sockopt_tcp_signature (int sock, union sockunion *su, const char *password) return 0; } -#ifdef HAVE_IPV6 /* If this does not work, then all users of this sockopt will need to * differentiate between IPv4 and IPv6, and keep seperate sockets for * each. @@ -686,7 +656,6 @@ sockopt_tcp_signature (int sock, union sockunion *su, const char *password) su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); memcpy (&su2->sin6.sin6_addr.s6_addr32[3], &su->sin.sin_addr, 4); } -#endif } memset (&md5sig, 0, sizeof (md5sig)); diff --git a/lib/sockopt.h b/lib/sockopt.h index 7e1bd62446..1b7be1e49f 100644 --- a/lib/sockopt.h +++ b/lib/sockopt.h @@ -24,15 +24,10 @@ #include "sockunion.h" -/* Override (vty) socket paths, but keep the filename */ -extern void set_socket_path (char *path, const char *defaultpath, - char *newpath, int maxsize); - extern void setsockopt_so_recvbuf (int sock, int size); extern void setsockopt_so_sendbuf (const int sock, int size); extern int getsockopt_so_sendbuf (const int sock); -#ifdef HAVE_IPV6 extern int setsockopt_ipv6_pktinfo (int, int); extern int setsockopt_ipv6_checksum (int, int); extern int setsockopt_ipv6_multicast_hops (int, int); @@ -40,13 +35,7 @@ extern int setsockopt_ipv6_unicast_hops (int, int); extern int setsockopt_ipv6_hoplimit (int, int); extern int setsockopt_ipv6_multicast_loop (int, int); extern int setsockopt_ipv6_tclass (int, int); -#endif /* HAVE_IPV6 */ -/* - * It is OK to reference in6_pktinfo here without a protecting #if - * because this macro will only be used #if HAVE_IPV6, and in6_pktinfo - * is not optional for HAVE_IPV6. - */ #define SOPT_SIZE_CMSG_PKTINFO_IPV6() (sizeof (struct in6_pktinfo)); /* diff --git a/lib/sockunion.c b/lib/sockunion.c index f4b6ce12cc..9ba2ce82f6 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -38,11 +38,9 @@ inet_sutop (const union sockunion *su, char *str) case AF_INET: inet_ntop (AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN); break; -#ifdef HAVE_IPV6 case AF_INET6: inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN); break; -#endif /* HAVE_IPV6 */ } return str; } @@ -63,7 +61,6 @@ str2sockunion (const char *str, union sockunion *su) #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ return 0; } -#ifdef HAVE_IPV6 ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr); if (ret > 0) /* Valid IPv6 address format. */ { @@ -73,7 +70,6 @@ str2sockunion (const char *str, union sockunion *su) #endif /* SIN6_LEN */ return 0; } -#endif /* HAVE_IPV6 */ return -1; } @@ -87,10 +83,8 @@ sockunion2str (const union sockunion *su, char *buf, size_t len) return buf; case AF_INET: return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len); -#ifdef HAVE_IPV6 case AF_INET6: return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len); -#endif /* HAVE_IPV6 */ } snprintf (buf, len, "(af %d)", sockunion_family(su)); return buf; @@ -114,7 +108,6 @@ sockunion_normalise_mapped (union sockunion *su) { struct sockaddr_in sin; -#ifdef HAVE_IPV6 if (su->sa.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr)) { @@ -124,7 +117,6 @@ sockunion_normalise_mapped (union sockunion *su) memcpy (&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4); memcpy (su, &sin, sizeof (struct sockaddr_in)); } -#endif /* HAVE_IPV6 */ } /* return sockunion structure : this function should be revised. */ @@ -156,8 +148,8 @@ sockunion_socket (const union sockunion *su) if (sock < 0) { char buf[SU_ADDRSTRLEN]; - zlog (NULL, LOG_WARNING, "Can't make socket for %s : %s", - sockunion_log (su, buf, SU_ADDRSTRLEN), safe_strerror (errno)); + zlog_warn("Can't make socket for %s : %s", + sockunion_log(su, buf, SU_ADDRSTRLEN), safe_strerror(errno)); return -1; } @@ -190,11 +182,9 @@ sockunion_sizeof (const union sockunion *su) case AF_INET: ret = sizeof (struct sockaddr_in); break; -#ifdef HAVE_IPV6 case AF_INET6: ret = sizeof (struct sockaddr_in6); break; -#endif /* AF_INET6 */ } return ret; } @@ -218,7 +208,6 @@ sockunion_connect (int fd, const union sockunion *peersu, unsigned short port, case AF_INET: su.sin.sin_port = port; break; -#ifdef HAVE_IPV6 case AF_INET6: su.sin6.sin6_port = port; #ifdef KAME @@ -229,7 +218,6 @@ sockunion_connect (int fd, const union sockunion *peersu, unsigned short port, } #endif /* KAME */ break; -#endif /* HAVE_IPV6 */ } /* Make socket non-block. */ @@ -276,7 +264,7 @@ sockunion_stream_socket (union sockunion *su) sock = socket (su->sa.sa_family, SOCK_STREAM, 0); if (sock < 0) - zlog (NULL, LOG_WARNING, "can't make socket sockunion_stream_socket"); + zlog_warn("can't make socket sockunion_stream_socket"); return sock; } @@ -299,7 +287,6 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port, if (su_addr == NULL) sockunion2ip (su) = htonl (INADDR_ANY); } -#ifdef HAVE_IPV6 else if (su->sa.sa_family == AF_INET6) { size = sizeof (struct sockaddr_in6); @@ -316,15 +303,13 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port, #endif /* LINUX_IPV6 */ } } -#endif /* HAVE_IPV6 */ - ret = bind (sock, (struct sockaddr *)su, size); if (ret < 0) { char buf[SU_ADDRSTRLEN]; - zlog (NULL, LOG_WARNING, "can't bind socket for %s : %s", - sockunion_log (su, buf, SU_ADDRSTRLEN), safe_strerror (errno)); + zlog_warn("can't bind socket for %s : %s", + sockunion_log(su, buf, SU_ADDRSTRLEN), safe_strerror(errno)); } return ret; @@ -340,7 +325,7 @@ sockopt_reuseaddr (int sock) (void *) &on, sizeof (on)); if (ret < 0) { - zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEADDR to socket %d", sock); + zlog_warn("can't set sockopt SO_REUSEADDR to socket %d", sock); return -1; } return 0; @@ -357,7 +342,7 @@ sockopt_reuseport (int sock) (void *) &on, sizeof (on)); if (ret < 0) { - zlog (NULL, LOG_WARNING, "can't set sockopt SO_REUSEPORT to socket %d", sock); + zlog_warn("can't set sockopt SO_REUSEPORT to socket %d", sock); return -1; } return 0; @@ -382,26 +367,24 @@ sockopt_ttl (int family, int sock, int ttl) (void *) &ttl, sizeof (int)); if (ret < 0) { - zlog (NULL, LOG_WARNING, "can't set sockopt IP_TTL %d to socket %d", ttl, sock); + zlog_warn("can't set sockopt IP_TTL %d to socket %d", ttl, sock); return -1; } return 0; } #endif /* IP_TTL */ -#ifdef HAVE_IPV6 if (family == AF_INET6) { ret = setsockopt (sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (void *) &ttl, sizeof (int)); if (ret < 0) { - zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d", - ttl, sock); + zlog_warn("can't set sockopt IPV6_UNICAST_HOPS %d to socket %d", + ttl, sock); return -1; } return 0; } -#endif /* HAVE_IPV6 */ return 0; } @@ -442,9 +425,8 @@ sockopt_minttl (int family, int sock, int minttl) { int ret = setsockopt (sock, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl)); if (ret < 0) - zlog (NULL, LOG_WARNING, - "can't set sockopt IP_MINTTL to %d on socket %d: %s", - minttl, sock, safe_strerror (errno)); + zlog_warn("can't set sockopt IP_MINTTL to %d on socket %d: %s", + minttl, sock, safe_strerror(errno)); return ret; } #endif /* IP_MINTTL */ @@ -453,9 +435,8 @@ sockopt_minttl (int family, int sock, int minttl) { int ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MINHOPCOUNT, &minttl, sizeof(minttl)); if (ret < 0) - zlog (NULL, LOG_WARNING, - "can't set sockopt IPV6_MINHOPCOUNT to %d on socket %d: %s", - minttl, sock, safe_strerror (errno)); + zlog_warn("can't set sockopt IPV6_MINHOPCOUNT to %d on socket %d: %s", + minttl, sock, safe_strerror(errno)); return ret; } #endif @@ -469,7 +450,6 @@ sockopt_v6only (int family, int sock) { int ret, on = 1; -#ifdef HAVE_IPV6 #ifdef IPV6_V6ONLY if (family == AF_INET6) { @@ -477,14 +457,13 @@ sockopt_v6only (int family, int sock) (void *) &on, sizeof (int)); if (ret < 0) { - zlog (NULL, LOG_WARNING, "can't set sockopt IPV6_V6ONLY " - "to socket %d", sock); + zlog_warn("can't set sockopt IPV6_V6ONLY " "to socket %d", + sock); return -1; } return 0; } #endif /* IPV6_V6ONLY */ -#endif /* HAVE_IPV6 */ return 0; } @@ -503,7 +482,6 @@ sockunion_same (const union sockunion *su1, const union sockunion *su2) ret = memcmp (&su1->sin.sin_addr, &su2->sin.sin_addr, sizeof (struct in_addr)); break; -#ifdef HAVE_IPV6 case AF_INET6: ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr, sizeof (struct in6_addr)); @@ -514,7 +492,6 @@ sockunion_same (const union sockunion *su1, const union sockunion *su2) ret = (su1->sin6.sin6_scope_id == su2->sin6.sin6_scope_id) ? 0 : 1; } break; -#endif /* HAVE_IPV6 */ } if (ret == 0) return 1; @@ -529,10 +506,8 @@ sockunion_hash (const union sockunion *su) { case AF_INET: return jhash_1word(su->sin.sin_addr.s_addr, 0); -#ifdef HAVE_IPV6 case AF_INET6: return jhash2(su->sin6.sin6_addr.s6_addr32, ZEBRA_NUM_OF(su->sin6.sin6_addr.s6_addr32), 0); -#endif /* HAVE_IPV6 */ } return 0; } @@ -544,10 +519,8 @@ family2addrsize(int family) { case AF_INET: return sizeof(struct in_addr); -#ifdef HAVE_IPV6 case AF_INET6: return sizeof(struct in6_addr); -#endif /* HAVE_IPV6 */ } return 0; } @@ -565,10 +538,8 @@ sockunion_get_addr(const union sockunion *su) { case AF_INET: return (const u_char *) &su->sin.sin_addr.s_addr; -#ifdef HAVE_IPV6 case AF_INET6: return (const u_char *) &su->sin6.sin6_addr; -#endif /* HAVE_IPV6 */ } return NULL; } @@ -585,11 +556,9 @@ sockunion_set(union sockunion *su, int family, const u_char *addr, size_t bytes) case AF_INET: memcpy(&su->sin.sin_addr.s_addr, addr, bytes); break; -#ifdef HAVE_IPV6 case AF_INET6: memcpy(&su->sin6.sin6_addr, addr, bytes); break; -#endif /* HAVE_IPV6 */ } } @@ -603,9 +572,7 @@ sockunion_getsockname (int fd) { struct sockaddr sa; struct sockaddr_in sin; -#ifdef HAVE_IPV6 struct sockaddr_in6 sin6; -#endif /* HAVE_IPV6 */ char tmp_buffer[128]; } name; union sockunion *su; @@ -627,7 +594,6 @@ sockunion_getsockname (int fd) memcpy (su, &name, sizeof (struct sockaddr_in)); return su; } -#ifdef HAVE_IPV6 if (name.sa.sa_family == AF_INET6) { su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); @@ -635,7 +601,6 @@ sockunion_getsockname (int fd) sockunion_normalise_mapped (su); return su; } -#endif /* HAVE_IPV6 */ return NULL; } @@ -649,9 +614,7 @@ sockunion_getpeername (int fd) { struct sockaddr sa; struct sockaddr_in sin; -#ifdef HAVE_IPV6 struct sockaddr_in6 sin6; -#endif /* HAVE_IPV6 */ char tmp_buffer[128]; } name; union sockunion *su; @@ -661,8 +624,7 @@ sockunion_getpeername (int fd) ret = getpeername (fd, (struct sockaddr *)&name, &len); if (ret < 0) { - zlog (NULL, LOG_WARNING, "Can't get remote address and port: %s", - safe_strerror (errno)); + zlog_warn("Can't get remote address and port: %s", safe_strerror(errno)); return NULL; } @@ -672,7 +634,6 @@ sockunion_getpeername (int fd) memcpy (su, &name, sizeof (struct sockaddr_in)); return su; } -#ifdef HAVE_IPV6 if (name.sa.sa_family == AF_INET6) { su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); @@ -680,7 +641,6 @@ sockunion_getpeername (int fd) sockunion_normalise_mapped (su); return su; } -#endif /* HAVE_IPV6 */ return NULL; } @@ -696,7 +656,6 @@ sockunion_print (const union sockunion *su) case AF_INET: printf ("%s\n", inet_ntoa (su->sin.sin_addr)); break; -#ifdef HAVE_IPV6 case AF_INET6: { char buf [SU_ADDRSTRLEN]; @@ -705,7 +664,6 @@ sockunion_print (const union sockunion *su) buf, sizeof (buf))); } break; -#endif /* HAVE_IPV6 */ #ifdef AF_LINK case AF_LINK: @@ -723,7 +681,6 @@ sockunion_print (const union sockunion *su) } } -#ifdef HAVE_IPV6 static int in6addr_cmp (const struct in6_addr *addr1, const struct in6_addr *addr2) { @@ -742,7 +699,6 @@ in6addr_cmp (const struct in6_addr *addr1, const struct in6_addr *addr2) } return 0; } -#endif /* HAVE_IPV6 */ int sockunion_cmp (const union sockunion *su1, const union sockunion *su2) @@ -761,10 +717,8 @@ sockunion_cmp (const union sockunion *su1, const union sockunion *su2) else return -1; } -#ifdef HAVE_IPV6 if (su1->sa.sa_family == AF_INET6) return in6addr_cmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); -#endif /* HAVE_IPV6 */ return 0; } diff --git a/lib/sockunion.h b/lib/sockunion.h index 3af3d78059..bed68e1ee1 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -47,11 +47,7 @@ enum connect_result }; /* Default address family. */ -#ifdef HAVE_IPV6 #define AF_INET_UNION AF_INET6 -#else -#define AF_INET_UNION AF_INET -#endif /* Sockunion address string length. Same as INET6_ADDRSTRLEN. */ #define SU_ADDRSTRLEN 46 diff --git a/lib/spf_backoff.c b/lib/spf_backoff.c new file mode 100644 index 0000000000..e923f232b8 --- /dev/null +++ b/lib/spf_backoff.c @@ -0,0 +1,341 @@ +/* + * This is an implementation of the IETF SPF delay algorithm + * as explained in draft-ietf-rtgwg-backoff-algo-04 + * + * Created: 25-01-2017 by S. Litkowski + * + * Copyright (C) 2017 Orange Labs http://www.orange.com/ + * Copyright (C) 2017 by Christian Franke, Open Source Routing / NetDEF Inc. + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#include "spf_backoff.h" + +#include "command.h" +#include "memory.h" +#include "thread.h" +#include "vty.h" + +DEFINE_MTYPE_STATIC(LIB, SPF_BACKOFF, "SPF backoff") +DEFINE_MTYPE_STATIC(LIB, SPF_BACKOFF_NAME, "SPF backoff name") + +static bool debug_spf_backoff = false; +#define backoff_debug(...) \ + do \ + { \ + if (debug_spf_backoff) \ + zlog_debug(__VA_ARGS__); \ + } \ + while (0) + +enum spf_backoff_state { + SPF_BACKOFF_QUIET, + SPF_BACKOFF_SHORT_WAIT, + SPF_BACKOFF_LONG_WAIT +}; + +struct spf_backoff { + struct thread_master *m; + + /* Timers as per draft */ + long init_delay; + long short_delay; + long long_delay; + long holddown; + long timetolearn; + + /* State machine */ + enum spf_backoff_state state; + struct thread *t_holddown; + struct thread *t_timetolearn; + + /* For debugging */ + char *name; + struct timeval first_event_time; + struct timeval last_event_time; +}; + +static const char * +spf_backoff_state2str(enum spf_backoff_state state) +{ + switch (state) + { + case SPF_BACKOFF_QUIET: + return "QUIET"; + case SPF_BACKOFF_SHORT_WAIT: + return "SHORT_WAIT"; + case SPF_BACKOFF_LONG_WAIT: + return "LONG_WAIT"; + } + return "???"; +} + +struct spf_backoff * +spf_backoff_new(struct thread_master *m, + const char *name, + long init_delay, + long short_delay, + long long_delay, + long holddown, + long timetolearn) +{ + struct spf_backoff *rv; + + rv = XCALLOC(MTYPE_SPF_BACKOFF, sizeof(*rv)); + rv->m = m; + + rv->init_delay = init_delay; + rv->short_delay = short_delay; + rv->long_delay = long_delay; + rv->holddown = holddown; + rv->timetolearn = timetolearn; + + rv->state = SPF_BACKOFF_QUIET; + + rv->name = XSTRDUP(MTYPE_SPF_BACKOFF_NAME, name); + return rv; +} + +void +spf_backoff_free(struct spf_backoff *backoff) +{ + if (!backoff) + return; + + THREAD_TIMER_OFF(backoff->t_holddown); + THREAD_TIMER_OFF(backoff->t_timetolearn); + XFREE(MTYPE_SPF_BACKOFF_NAME, backoff->name); + + XFREE(MTYPE_SPF_BACKOFF, backoff); +} + +static int +spf_backoff_timetolearn_elapsed(struct thread *thread) +{ + struct spf_backoff *backoff = THREAD_ARG(thread); + + backoff->t_timetolearn = NULL; + backoff->state = SPF_BACKOFF_LONG_WAIT; + backoff_debug("SPF Back-off(%s) TIMETOLEARN elapsed, move to state %s", + backoff->name, spf_backoff_state2str(backoff->state)); + return 0; +} + +static int +spf_backoff_holddown_elapsed(struct thread *thread) +{ + struct spf_backoff *backoff = THREAD_ARG(thread); + + backoff->t_holddown = NULL; + THREAD_TIMER_OFF(backoff->t_timetolearn); + timerclear(&backoff->first_event_time); + backoff->state = SPF_BACKOFF_QUIET; + backoff_debug("SPF Back-off(%s) HOLDDOWN elapsed, move to state %s", + backoff->name, spf_backoff_state2str(backoff->state)); + return 0; +} + +long spf_backoff_schedule(struct spf_backoff *backoff) +{ + long rv; + struct timeval now; + + gettimeofday(&now, NULL); + + backoff_debug("SPF Back-off(%s) schedule called in state %s", + backoff->name, spf_backoff_state2str(backoff->state)); + + backoff->last_event_time = now; + + switch (backoff->state) + { + case SPF_BACKOFF_QUIET: + backoff->state = SPF_BACKOFF_SHORT_WAIT; + THREAD_TIMER_MSEC_ON(backoff->m, backoff->t_timetolearn, + spf_backoff_timetolearn_elapsed, backoff, + backoff->timetolearn); + THREAD_TIMER_MSEC_ON(backoff->m, backoff->t_holddown, + spf_backoff_holddown_elapsed, backoff, + backoff->holddown); + backoff->first_event_time = now; + rv = backoff->init_delay; + break; + case SPF_BACKOFF_SHORT_WAIT: + case SPF_BACKOFF_LONG_WAIT: + THREAD_TIMER_OFF(backoff->t_holddown); + THREAD_TIMER_MSEC_ON(backoff->m, backoff->t_holddown, + spf_backoff_holddown_elapsed, backoff, + backoff->holddown); + if (backoff->state == SPF_BACKOFF_SHORT_WAIT) + rv = backoff->short_delay; + else + rv = backoff->long_delay; + break; + default: + zlog_warn("SPF Back-off(%s) in unknown state", backoff->name); + rv = backoff->init_delay; + } + + backoff_debug("SPF Back-off(%s) changed state to %s and returned %ld delay", + backoff->name, spf_backoff_state2str(backoff->state), rv); + return rv; +} + +static const char * +timeval_format(struct timeval *tv) +{ + struct tm tm_store; + struct tm *tm; + static char timebuf[256]; + + if (!tv->tv_sec && !tv->tv_usec) + return "(never)"; + + tm = localtime_r(&tv->tv_sec, &tm_store); + if (!tm || strftime(timebuf, sizeof(timebuf), + "%Z %a %Y-%m-%d %H:%M:%S", tm) == 0) + { + return "???"; + } + + size_t offset = strlen(timebuf); + snprintf(timebuf + offset, sizeof(timebuf) - offset, ".%ld", tv->tv_usec); + + return timebuf; +} + +void +spf_backoff_show(struct spf_backoff *backoff, struct vty *vty, + const char *prefix) +{ + vty_out(vty, "%sCurrent state: %s%s", prefix, + spf_backoff_state2str(backoff->state), VTY_NEWLINE); + vty_out(vty, "%sInit timer: %ld msec%s", prefix, + backoff->init_delay, VTY_NEWLINE); + vty_out(vty, "%sShort timer: %ld msec%s", prefix, + backoff->short_delay, VTY_NEWLINE); + vty_out(vty, "%sLong timer: %ld msec%s", prefix, + backoff->long_delay, VTY_NEWLINE); + vty_out(vty, "%sHolddown timer: %ld msec%s", prefix, + backoff->holddown, VTY_NEWLINE); + if (backoff->t_holddown) + { + struct timeval remain = thread_timer_remain(backoff->t_holddown); + vty_out(vty, "%s Still runs for %ld msec%s", + prefix, remain.tv_sec * 1000 + remain.tv_usec/1000, VTY_NEWLINE); + } + else + { + vty_out(vty, "%s Inactive%s", prefix, VTY_NEWLINE); + } + + vty_out(vty, "%sTimeToLearn timer: %ld msec%s", prefix, + backoff->timetolearn, VTY_NEWLINE); + if (backoff->t_timetolearn) + { + struct timeval remain = thread_timer_remain(backoff->t_timetolearn); + vty_out(vty, "%s Still runs for %ld msec%s", + prefix, remain.tv_sec * 1000 + remain.tv_usec/1000, VTY_NEWLINE); + } + else + { + vty_out(vty, "%s Inactive%s", prefix, VTY_NEWLINE); + } + + vty_out(vty, "%sFirst event: %s%s", prefix, + timeval_format(&backoff->first_event_time), VTY_NEWLINE); + vty_out(vty, "%sLast event: %s%s", prefix, + timeval_format(&backoff->last_event_time), VTY_NEWLINE); +} + +DEFUN(spf_backoff_debug, + spf_backoff_debug_cmd, + "debug spf-delay-ietf", + DEBUG_STR + "SPF Back-off Debugging\n") +{ + debug_spf_backoff = true; + return CMD_SUCCESS; +} + +DEFUN(no_spf_backoff_debug, + no_spf_backoff_debug_cmd, + "no debug spf-delay-ietf", + NO_STR + DEBUG_STR + "SPF Back-off Debugging\n") +{ + debug_spf_backoff = false; + return CMD_SUCCESS; +} + +int +spf_backoff_write_config(struct vty *vty) +{ + int written = 0; + + if (debug_spf_backoff) + { + vty_out(vty, "debug spf-delay-ietf%s", VTY_NEWLINE); + written++; + } + + return written; +} + +void +spf_backoff_cmd_init(void) +{ + install_element(ENABLE_NODE, &spf_backoff_debug_cmd); + install_element(CONFIG_NODE, &spf_backoff_debug_cmd); + install_element(ENABLE_NODE, &no_spf_backoff_debug_cmd); + install_element(CONFIG_NODE, &no_spf_backoff_debug_cmd); +} + +long +spf_backoff_init_delay(struct spf_backoff *backoff) +{ + return backoff->init_delay; +} + +long +spf_backoff_short_delay(struct spf_backoff *backoff) +{ + return backoff->short_delay; +} + +long +spf_backoff_long_delay(struct spf_backoff *backoff) +{ + return backoff->long_delay; +} + +long +spf_backoff_holddown(struct spf_backoff *backoff) +{ + return backoff->holddown; +} + +long +spf_backoff_timetolearn(struct spf_backoff *backoff) +{ + return backoff->timetolearn; +} diff --git a/lib/spf_backoff.h b/lib/spf_backoff.h new file mode 100644 index 0000000000..552ca4ae41 --- /dev/null +++ b/lib/spf_backoff.h @@ -0,0 +1,66 @@ +/* + * This is an implementation of the IETF SPF delay algorithm + * as explained in draft-ietf-rtgwg-backoff-algo-04 + * + * Created: 25-01-2017 by S. Litkowski + * + * Copyright (C) 2017 Orange Labs http://www.orange.com/ + * Copyright (C) 2017 by Christian Franke, Open Source Routing / NetDEF Inc. + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef _ZEBRA_SPF_BACKOFF_H +#define _ZEBRA_SPF_BACKOFF_H + +struct spf_backoff; +struct thread_master; +struct vty; + +struct spf_backoff *spf_backoff_new(struct thread_master *m, + const char *name, + long init_delay, + long short_delay, + long long_delay, + long holddown, + long timetolearn); + +void spf_backoff_free(struct spf_backoff *backoff); + +/* Called whenever an IGP event is received, returns how many + * milliseconds routing table computation should be delayed */ +long spf_backoff_schedule(struct spf_backoff *backoff); + +/* Shows status of SPF backoff instance */ +void spf_backoff_show(struct spf_backoff *backoff, + struct vty *vty, + const char *prefix); + +/* Writes out global SPF backoff debug config */ +int spf_backoff_write_config(struct vty *vty); + +/* Registers global SPF backoff debug commands */ +void spf_backoff_cmd_init(void); + +/* Accessor functions for SPF backoff parameters */ +long spf_backoff_init_delay(struct spf_backoff *backoff); +long spf_backoff_short_delay(struct spf_backoff *backoff); +long spf_backoff_long_delay(struct spf_backoff *backoff); +long spf_backoff_holddown(struct spf_backoff *backoff); +long spf_backoff_timetolearn(struct spf_backoff *backoff); + +#endif diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c new file mode 100644 index 0000000000..04c9eff79a --- /dev/null +++ b/lib/srcdest_table.c @@ -0,0 +1,312 @@ +/* + * SRC-DEST Routing Table + * + * Copyright (C) 2017 by David Lamparter & Christian Franke, + * Open Source Routing / NetDEF Inc. + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#include "srcdest_table.h" + +#include "memory.h" +#include "prefix.h" +#include "table.h" + +DEFINE_MTYPE_STATIC(LIB, ROUTE_SRC_NODE, "Route source node") + +/* ----- functions to manage rnodes _with_ srcdest table ----- */ +struct srcdest_rnode +{ + /* must be first in structure for casting to/from route_node */ + ROUTE_NODE_FIELDS; + + struct route_table *src_table; +}; + +static struct srcdest_rnode * +srcdest_rnode_from_rnode (struct route_node *rn) +{ + assert (rnode_is_dstnode (rn)); + return (struct srcdest_rnode *) rn; +} + +static struct route_node * +srcdest_rnode_to_rnode (struct srcdest_rnode *srn) +{ + return (struct route_node *) srn; +} + +static struct route_node * +srcdest_rnode_create (route_table_delegate_t *delegate, + struct route_table *table) +{ + struct srcdest_rnode *srn; + srn = XCALLOC (MTYPE_ROUTE_NODE, sizeof (struct srcdest_rnode)); + return srcdest_rnode_to_rnode(srn); +} + +static void +srcdest_rnode_destroy (route_table_delegate_t *delegate, + struct route_table *table, struct route_node *rn) +{ + struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn); + struct route_table *src_table; + + /* Clear route node's src_table here already, otherwise the + * deletion of the last node in the src_table will trigger + * another call to route_table_finish for the src_table. + * + * (Compare with srcdest_srcnode_destroy) + */ + src_table = srn->src_table; + srn->src_table = NULL; + route_table_finish(src_table); + XFREE (MTYPE_ROUTE_NODE, rn); +} + +route_table_delegate_t _srcdest_dstnode_delegate = { + .create_node = srcdest_rnode_create, + .destroy_node = srcdest_rnode_destroy +}; + +/* ----- functions to manage rnodes _in_ srcdest table ----- */ + +/* node creation / deletion for srcdest source prefix nodes. + * the route_node isn't actually different from the normal route_node, + * but the cleanup is special to free the table (and possibly the + * destination prefix's route_node) */ + +static struct route_node * +srcdest_srcnode_create (route_table_delegate_t *delegate, + struct route_table *table) +{ + return XCALLOC (MTYPE_ROUTE_SRC_NODE, sizeof (struct route_node)); +} + +static void +srcdest_srcnode_destroy (route_table_delegate_t *delegate, + struct route_table *table, struct route_node *rn) +{ + struct srcdest_rnode *srn; + + XFREE (MTYPE_ROUTE_SRC_NODE, rn); + + srn = table->info; + if (srn->src_table && route_table_count (srn->src_table) == 0) + { + /* deleting the route_table from inside destroy_node is ONLY + * permitted IF table->count is 0! see lib/table.c route_node_delete() + * for details */ + route_table_finish (srn->src_table); + srn->src_table = NULL; + + /* drop the ref we're holding in srcdest_node_get(). there might be + * non-srcdest routes, so the route_node may still exist. hence, it's + * important to clear src_table above. */ + route_unlock_node (srcdest_rnode_to_rnode (srn)); + } +} + +route_table_delegate_t _srcdest_srcnode_delegate = { + .create_node = srcdest_srcnode_create, + .destroy_node = srcdest_srcnode_destroy +}; + +/* NB: read comments in code for refcounting before using! */ +static struct route_node * +srcdest_srcnode_get (struct route_node *rn, struct prefix_ipv6 *src_p) +{ + struct srcdest_rnode *srn; + + if (!src_p || src_p->prefixlen == 0) + return rn; + + srn = srcdest_rnode_from_rnode (rn); + if (!srn->src_table) + { + /* this won't use srcdest_rnode, we're already on the source here */ + srn->src_table = route_table_init_with_delegate (&_srcdest_srcnode_delegate); + srn->src_table->info = srn; + + /* there is no route_unlock_node on the original rn here. + * The reference is kept for the src_table. */ + } + else + { + /* only keep 1 reference for the src_table, makes the refcounting + * more similar to the non-srcdest case. Either way after return from + * function, the only reference held is the one on the return value. + * + * We can safely drop our reference here because src_table is holding + * another reference, so this won't free rn */ + route_unlock_node (rn); + } + + return route_node_get (srn->src_table, (struct prefix *)src_p); +} + +static struct route_node * +srcdest_srcnode_lookup (struct route_node *rn, struct prefix_ipv6 *src_p) +{ + struct srcdest_rnode *srn; + + if (!rn || !src_p || src_p->prefixlen == 0) + return rn; + + /* We got this rn from a lookup, so its refcnt was incremented. As we won't + * return return rn from any point beyond here, we should decrement its refcnt. + */ + route_unlock_node (rn); + + srn = srcdest_rnode_from_rnode (rn); + if (!srn->src_table) + return NULL; + + return route_node_lookup (srn->src_table, (struct prefix *)src_p); +} + +/* ----- exported functions ----- */ + +struct route_table * +srcdest_table_init(void) +{ + return route_table_init_with_delegate(&_srcdest_dstnode_delegate); +} + +struct route_node * +srcdest_route_next(struct route_node *rn) +{ + struct route_node *next, *parent; + + /* For a non src-dest node, just return route_next */ + if (!(rnode_is_dstnode(rn) || rnode_is_srcnode(rn))) + return route_next(rn); + + if (rnode_is_dstnode(rn)) + { + /* This means the route_node is part of the top hierarchy + * and refers to a destination prefix. */ + struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn); + + if (srn->src_table) + next = route_top(srn->src_table); + else + next = NULL; + + if (next) + { + /* There is a source prefix. Return the node for it */ + route_unlock_node(rn); + return next; + } + else + { + /* There is no source prefix, just continue as usual */ + return route_next(rn); + } + } + + /* This part handles the case of iterating source nodes. */ + parent = route_lock_node(rn->table->info); + next = route_next(rn); + + if (next) + { + /* There is another source node, continue in the source table */ + route_unlock_node(parent); + return next; + } + else + { + /* The source table is complete, continue in the parent table */ + return route_next(parent); + } +} + +struct route_node * +srcdest_rnode_get (struct route_table *table, union prefixptr dst_pu, + struct prefix_ipv6 *src_p) +{ + struct prefix_ipv6 *dst_p = dst_pu.p6; + struct route_node *rn; + + rn = route_node_get (table, (struct prefix *) dst_p); + return srcdest_srcnode_get (rn, src_p); +} + +struct route_node * +srcdest_rnode_lookup (struct route_table *table, union prefixptr dst_pu, + struct prefix_ipv6 *src_p) +{ + struct prefix_ipv6 *dst_p = dst_pu.p6; + struct route_node *rn; + struct route_node *srn; + + rn = route_node_lookup_maynull (table, (struct prefix *) dst_p); + srn = srcdest_srcnode_lookup (rn, src_p); + + if (rn != NULL && rn == srn && !rn->info) + { + /* Match the behavior of route_node_lookup and don't return an + * empty route-node for a dest-route */ + route_unlock_node(rn); + return NULL; + } + return srn; +} + +void +srcdest_rnode_prefixes (struct route_node *rn, struct prefix **p, + struct prefix **src_p) +{ + if (rnode_is_srcnode (rn)) + { + struct route_node *dst_rn = rn->table->info; + if (p) + *p = &dst_rn->p; + if (src_p) + *src_p = &rn->p; + } + else + { + if (p) + *p = &rn->p; + if (src_p) + *src_p = NULL; + } +} + +const char * +srcdest_rnode2str (struct route_node *rn, char *str, int size) +{ + struct prefix *dst_p, *src_p; + char dst_buf[PREFIX_STRLEN], src_buf[PREFIX_STRLEN]; + + srcdest_rnode_prefixes(rn, &dst_p, &src_p); + + snprintf(str, size, "%s%s%s", + prefix2str(dst_p, dst_buf, sizeof(dst_buf)), + (src_p && src_p->prefixlen) ? " from " : "", + (src_p && src_p->prefixlen) ? prefix2str(src_p, src_buf, + sizeof(src_buf)) + : ""); + return str; +} diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h new file mode 100644 index 0000000000..207f5d121d --- /dev/null +++ b/lib/srcdest_table.h @@ -0,0 +1,101 @@ +/* + * SRC-DEST Routing Table + * + * Copyright (C) 2017 by David Lamparter & Christian Franke, + * Open Source Routing / NetDEF Inc. + * + * This file is part of FreeRangeRouting (FRR) + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_SRC_DEST_TABLE_H +#define _ZEBRA_SRC_DEST_TABLE_H + +/* old/IPv4/non-srcdest: + * table -> route_node .info -> [obj] + * + * new/IPv6/srcdest: + * table -...-> srcdest_rnode [prefix = dest] .info -> [obj] + * .src_table -> + * srcdest table -...-> route_node [prefix = src] .info -> [obj] + * + * non-srcdest routes (src = ::/0) are treated just like before, their + * information being directly there in the info pointer. + * + * srcdest routes are found by looking up destination first, then looking + * up the source in the "src_table". src_table contains normal route_nodes, + * whose prefix is the _source_ prefix. + * + * NB: info can be NULL on the destination rnode, if there are only srcdest + * routes for a particular destination prefix. + */ + +#include "prefix.h" +#include "table.h" + +#define SRCDEST2STR_BUFFER (2*PREFIX2STR_BUFFER + sizeof(" from ")) + +/* extended route node for IPv6 srcdest routing */ +struct srcdest_rnode; + +extern route_table_delegate_t _srcdest_dstnode_delegate; +extern route_table_delegate_t _srcdest_srcnode_delegate; + +extern struct route_table *srcdest_table_init(void); +extern struct route_node *srcdest_rnode_get(struct route_table *table, + union prefixptr dst_pu, + struct prefix_ipv6 *src_p); +extern struct route_node *srcdest_rnode_lookup(struct route_table *table, + union prefixptr dst_pu, + struct prefix_ipv6 *src_p); +extern void srcdest_rnode_prefixes (struct route_node *rn, struct prefix **p, + struct prefix **src_p); +extern const char *srcdest_rnode2str(struct route_node *rn, char *str, int size); +extern struct route_node *srcdest_route_next(struct route_node *rn); + +static inline int +rnode_is_dstnode (struct route_node *rn) +{ + return rn->table->delegate == &_srcdest_dstnode_delegate; +} + +static inline int +rnode_is_srcnode (struct route_node *rn) +{ + return rn->table->delegate == &_srcdest_srcnode_delegate; +} + +static inline struct route_table * +srcdest_rnode_table (struct route_node *rn) +{ + if (rnode_is_srcnode (rn)) + { + struct route_node *dst_rn = rn->table->info; + return dst_rn->table; + } + else + { + return rn->table; + } +} +static inline void * +srcdest_rnode_table_info (struct route_node *rn) +{ + return srcdest_rnode_table(rn)->info; +} + +#endif /* _ZEBRA_SRC_DEST_TABLE_H */ diff --git a/lib/table.c b/lib/table.c index 5133ef6974..7f789dd3cd 100644 --- a/lib/table.c +++ b/lib/table.c @@ -28,7 +28,7 @@ #include "sockunion.h" DEFINE_MTYPE( LIB, ROUTE_TABLE, "Route table") -DEFINE_MTYPE_STATIC(LIB, ROUTE_NODE, "Route node") +DEFINE_MTYPE( LIB, ROUTE_NODE, "Route node") static void route_node_delete (struct route_node *); static void route_table_free (struct route_table *); @@ -78,6 +78,8 @@ route_node_set (struct route_table *table, const struct prefix *prefix) static void route_node_free (struct route_table *table, struct route_node *node) { + if (table->cleanup) + table->cleanup(table, node); table->delegate->destroy_node (table->delegate, table, node); } @@ -250,7 +252,6 @@ route_node_match_ipv4 (const struct route_table *table, return route_node_match (table, (struct prefix *) &p); } -#ifdef HAVE_IPV6 struct route_node * route_node_match_ipv6 (const struct route_table *table, const struct in6_addr *addr) @@ -264,7 +265,6 @@ route_node_match_ipv6 (const struct route_table *table, return route_node_match (table, (struct prefix *) &p); } -#endif /* HAVE_IPV6 */ /* Lookup same prefix node. Return NULL when we can't find route. */ struct route_node * @@ -288,6 +288,28 @@ route_node_lookup (const struct route_table *table, const struct prefix *p) return NULL; } +/* Lookup same prefix node. Return NULL when we can't find route. */ +struct route_node * +route_node_lookup_maynull (const struct route_table *table, const struct prefix *p) +{ + struct route_node *node; + u_char prefixlen = p->prefixlen; + const u_char *prefix = &p->u.prefix; + + node = table->top; + + while (node && node->p.prefixlen <= prefixlen && + prefix_match (&node->p, p)) + { + if (node->p.prefixlen == prefixlen) + return route_lock_node (node); + + node = node->link[prefix_bit(prefix, node->p.prefixlen)]; + } + + return NULL; +} + /* Add node to routing table. */ struct route_node * route_node_get (struct route_table *const table, const struct prefix *p) @@ -380,6 +402,14 @@ route_node_delete (struct route_node *node) node->table->count--; + /* WARNING: FRAGILE CODE! + * route_node_free may have the side effect of free'ing the entire table. + * this is permitted only if table->count got decremented to zero above, + * because in that case parent will also be NULL, so that we won't try to + * delete a now-stale parent below. + * + * cf. srcdest_srcnode_destroy() in zebra/zebra_rib.c */ + route_node_free (node->table, node); /* If parent node is stub then delete it also. */ diff --git a/lib/table.h b/lib/table.h index 78bf5da748..1691a8e20a 100644 --- a/lib/table.h +++ b/lib/table.h @@ -25,6 +25,7 @@ #include "memory.h" DECLARE_MTYPE(ROUTE_TABLE) +DECLARE_MTYPE(ROUTE_NODE) /* * Forward declarations. @@ -61,6 +62,7 @@ struct route_table * Delegate that performs certain functions for this table. */ route_table_delegate_t *delegate; + void (*cleanup)(struct route_table *, struct route_node *); unsigned long count; @@ -157,15 +159,15 @@ extern struct route_node *route_node_get (struct route_table *const, const struct prefix *); extern struct route_node *route_node_lookup (const struct route_table *, const struct prefix *); +extern struct route_node *route_node_lookup_maynull (const struct route_table *, + const struct prefix *); extern struct route_node *route_lock_node (struct route_node *node); extern struct route_node *route_node_match (const struct route_table *, const struct prefix *); extern struct route_node *route_node_match_ipv4 (const struct route_table *, const struct in_addr *); -#ifdef HAVE_IPV6 extern struct route_node *route_node_match_ipv6 (const struct route_table *, const struct in6_addr *); -#endif /* HAVE_IPV6 */ extern unsigned long route_table_count (const struct route_table *); diff --git a/lib/thread.c b/lib/thread.c index e10d0c48bf..6138e79718 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -41,151 +41,16 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats") #include <mach/mach_time.h> #endif -/* Recent absolute time of day */ -struct timeval recent_time; /* Relative time, since startup */ -static struct timeval relative_time; - static struct hash *cpu_record = NULL; -/* Adjust so that tv_usec is in the range [0,TIMER_SECOND_MICRO). - And change negative values to 0. */ -static struct timeval -timeval_adjust (struct timeval a) -{ - while (a.tv_usec >= TIMER_SECOND_MICRO) - { - a.tv_usec -= TIMER_SECOND_MICRO; - a.tv_sec++; - } - - while (a.tv_usec < 0) - { - a.tv_usec += TIMER_SECOND_MICRO; - a.tv_sec--; - } - - if (a.tv_sec < 0) - /* Change negative timeouts to 0. */ - a.tv_sec = a.tv_usec = 0; - - return a; -} - -static struct timeval -timeval_subtract (struct timeval a, struct timeval b) -{ - struct timeval ret; - - ret.tv_usec = a.tv_usec - b.tv_usec; - ret.tv_sec = a.tv_sec - b.tv_sec; - - return timeval_adjust (ret); -} - -static long -timeval_cmp (struct timeval a, struct timeval b) -{ - return (a.tv_sec == b.tv_sec - ? a.tv_usec - b.tv_usec : a.tv_sec - b.tv_sec); -} - -unsigned long +static unsigned long timeval_elapsed (struct timeval a, struct timeval b) { return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + (a.tv_usec - b.tv_usec)); } -/* gettimeofday wrapper, to keep recent_time updated */ -static int -quagga_gettimeofday (struct timeval *tv) -{ - int ret; - - assert (tv); - - if (!(ret = gettimeofday (&recent_time, NULL))) - { - /* avoid copy if user passed recent_time pointer.. */ - if (tv != &recent_time) - *tv = recent_time; - return 0; - } - return ret; -} - -static int -quagga_get_relative (struct timeval *tv) -{ - int ret; - -#ifdef HAVE_CLOCK_MONOTONIC - { - struct timespec tp; - if (!(ret = clock_gettime (CLOCK_MONOTONIC, &tp))) - { - relative_time.tv_sec = tp.tv_sec; - relative_time.tv_usec = tp.tv_nsec / 1000; - } - } -#elif defined(__APPLE__) - { - uint64_t ticks; - uint64_t useconds; - static mach_timebase_info_data_t timebase_info; - - ticks = mach_absolute_time(); - if (timebase_info.denom == 0) - mach_timebase_info(&timebase_info); - - useconds = ticks * timebase_info.numer / timebase_info.denom / 1000; - relative_time.tv_sec = useconds / 1000000; - relative_time.tv_usec = useconds % 1000000; - - return 0; - } -#else /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */ -#error no monotonic clock on this system -#endif /* HAVE_CLOCK_MONOTONIC */ - - if (tv) - *tv = relative_time; - - return ret; -} - -/* Exported Quagga timestamp function. - * Modelled on POSIX clock_gettime. - */ -int -quagga_gettime (enum quagga_clkid clkid, struct timeval *tv) -{ - switch (clkid) - { - case QUAGGA_CLK_MONOTONIC: - return quagga_get_relative (tv); - default: - errno = EINVAL; - return -1; - } -} - -time_t -quagga_monotime (void) -{ - struct timeval tv; - quagga_get_relative(&tv); - return tv.tv_sec; -} - -/* Public export of recent_relative_time by value */ -struct timeval -recent_relative_time (void) -{ - return relative_time; -} - static unsigned int cpu_record_hash_key (struct cpu_thread_history *a) { @@ -221,8 +86,8 @@ static void vty_out_cpu_thread_history(struct vty* vty, struct cpu_thread_history *a) { - vty_out(vty, "%10ld.%03ld %9d %8ld %9ld %8ld %9ld", - a->cpu.total/1000, a->cpu.total%1000, a->total_calls, + vty_out(vty, "%5d %10ld.%03ld %9d %8ld %9ld %8ld %9ld", + a->total_active, a->cpu.total/1000, a->cpu.total%1000, a->total_calls, a->cpu.total/a->total_calls, a->cpu.max, a->real.total/a->total_calls, a->real.max); vty_out(vty, " %c%c%c%c%c%c %s%s", @@ -247,6 +112,7 @@ cpu_record_hash_print(struct hash_backet *bucket, if ( !(a->types & *filter) ) return; vty_out_cpu_thread_history(vty,a); + totals->total_active += a->total_active; totals->total_calls += a->total_calls; totals->real.total += a->real.total; if (totals->real.max < a->real.max) @@ -268,7 +134,7 @@ cpu_record_print(struct vty *vty, thread_type filter) vty_out(vty, "%21s %18s %18s%s", "", "CPU (user+system):", "Real (wall-clock):", VTY_NEWLINE); - vty_out(vty, " Runtime(ms) Invoked Avg uSec Max uSecs"); + vty_out(vty, "Active Runtime(ms) Invoked Avg uSec Max uSecs"); vty_out(vty, " Avg uSec Max uSecs"); vty_out(vty, " Type Thread%s", VTY_NEWLINE); hash_iterate(cpu_record, @@ -287,15 +153,16 @@ DEFUN (show_thread_cpu, "Thread CPU usage\n" "Display filter (rwtexb)\n") { + int idx_filter = 3; int i = 0; thread_type filter = (thread_type) -1U; - if (argc > 0) + if (argc > 3) { filter = 0; - while (argv[0][i] != '\0') + while (argv[idx_filter]->arg[i] != '\0') { - switch ( argv[0][i] ) + switch ( argv[idx_filter]->arg[i] ) { case 'r': case 'R': @@ -330,7 +197,7 @@ DEFUN (show_thread_cpu, { vty_out(vty, "Invalid filter \"%s\" specified," " must contain at least one of 'RWTEXB'%s", - argv[0], VTY_NEWLINE); + argv[idx_filter]->arg, VTY_NEWLINE); return CMD_WARNING; } } @@ -369,15 +236,16 @@ DEFUN (clear_thread_cpu, "Thread CPU usage\n" "Display filter (rwtexb)\n") { + int idx_filter = 3; int i = 0; thread_type filter = (thread_type) -1U; - if (argc > 0) + if (argc > 3) { filter = 0; - while (argv[0][i] != '\0') + while (argv[idx_filter]->arg[i] != '\0') { - switch ( argv[0][i] ) + switch ( argv[idx_filter]->arg[i] ) { case 'r': case 'R': @@ -412,7 +280,7 @@ DEFUN (clear_thread_cpu, { vty_out(vty, "Invalid filter \"%s\" specified," " must contain at least one of 'RWTEXB'%s", - argv[0], VTY_NEWLINE); + argv[idx_filter]->arg, VTY_NEWLINE); return CMD_WARNING; } } @@ -421,17 +289,22 @@ DEFUN (clear_thread_cpu, return CMD_SUCCESS; } +void +thread_cmd_init (void) +{ + install_element (VIEW_NODE, &show_thread_cpu_cmd); + install_element (ENABLE_NODE, &clear_thread_cpu_cmd); +} + static int thread_timer_cmp(void *a, void *b) { struct thread *thread_a = a; struct thread *thread_b = b; - long cmp = timeval_cmp(thread_a->u.sands, thread_b->u.sands); - - if (cmp < 0) + if (timercmp (&thread_a->u.sands, &thread_b->u.sands, <)) return -1; - if (cmp > 0) + if (timercmp (&thread_a->u.sands, &thread_b->u.sands, >)) return 1; return 0; } @@ -561,7 +434,9 @@ thread_add_unuse (struct thread_master *m, struct thread *thread) assert (m != NULL && thread != NULL); assert (thread->next == NULL); assert (thread->prev == NULL); - assert (thread->type == THREAD_UNUSED); + + thread->type = THREAD_UNUSED; + thread->hist->total_active--; thread_list_add (&m->unuse, thread); } @@ -659,12 +534,8 @@ thread_master_free (struct thread_master *m) unsigned long thread_timer_remain_second (struct thread *thread) { - quagga_get_relative (NULL); - - if (thread->u.sands.tv_sec - relative_time.tv_sec > 0) - return thread->u.sands.tv_sec - relative_time.tv_sec; - else - return 0; + int64_t remain = monotime_until(&thread->u.sands, NULL) / 1000000LL; + return remain < 0 ? 0 : remain; } #define debugargdef const char *funcname, const char *schedfrom, int fromln @@ -673,9 +544,9 @@ thread_timer_remain_second (struct thread *thread) struct timeval thread_timer_remain(struct thread *thread) { - quagga_get_relative(NULL); - - return timeval_subtract(thread->u.sands, relative_time); + struct timeval remain; + monotime_until(&thread->u.sands, &remain); + return remain; } /* Get new thread. */ @@ -684,6 +555,7 @@ thread_get (struct thread_master *m, u_char type, int (*func) (struct thread *), void *arg, debugargdef) { struct thread *thread = thread_trim_head (&m->unuse); + struct cpu_thread_history tmp; if (! thread) { @@ -693,11 +565,30 @@ thread_get (struct thread_master *m, u_char type, thread->type = type; thread->add_type = type; thread->master = m; - thread->func = func; thread->arg = arg; thread->index = -1; thread->yield = THREAD_YIELD_TIME_SLOT; /* default */ + /* + * So if the passed in funcname is not what we have + * stored that means the thread->hist needs to be + * updated. We keep the last one around in unused + * under the assumption that we are probably + * going to immediately allocate the same + * type of thread. + * This hopefully saves us some serious + * hash_get lookups. + */ + if (thread->funcname != funcname || + thread->func != func) + { + tmp.func = func; + tmp.funcname = funcname; + thread->hist = hash_get (cpu_record, &tmp, + (void * (*) (void *))cpu_record_hash_alloc); + } + thread->hist->total_active++; + thread->func = func; thread->funcname = funcname; thread->schedfrom = schedfrom; thread->schedfrom_line = fromln; @@ -828,7 +719,8 @@ funcname_thread_add_read_write (int dir, struct thread_master *m, #else if (FD_ISSET (fd, fdset)) { - zlog (NULL, LOG_WARNING, "There is already %s fd [%d]", (dir == THREAD_READ) ? "read" : "write", fd); + zlog_warn ("There is already %s fd [%d]", + (dir == THREAD_READ) ? "read" : "write", fd); return NULL; } @@ -855,7 +747,6 @@ funcname_thread_add_timer_timeval (struct thread_master *m, { struct thread *thread; struct pqueue *queue; - struct timeval alarm_time; assert (m != NULL); @@ -865,11 +756,8 @@ funcname_thread_add_timer_timeval (struct thread_master *m, queue = ((type == THREAD_TIMER) ? m->timer : m->background); thread = thread_get (m, type, func, arg, debugargpass); - /* Do we need jitter here? */ - quagga_get_relative (NULL); - alarm_time.tv_sec = relative_time.tv_sec + time_relative->tv_sec; - alarm_time.tv_usec = relative_time.tv_usec + time_relative->tv_usec; - thread->u.sands = timeval_adjust(alarm_time); + monotime(&thread->u.sands); + timeradd(&thread->u.sands, time_relative, &thread->u.sands); pqueue_enqueue(thread, queue); return thread; @@ -1054,7 +942,6 @@ thread_cancel (struct thread *thread) assert(!"Thread should be either in queue or list or array!"); } - thread->type = THREAD_UNUSED; thread_add_unuse (thread->master, thread); } @@ -1077,7 +964,6 @@ thread_cancel_event (struct thread_master *m, void *arg) { ret++; thread_list_delete (&m->event, t); - t->type = THREAD_UNUSED; thread_add_unuse (m, t); } } @@ -1095,7 +981,6 @@ thread_cancel_event (struct thread_master *m, void *arg) { ret++; thread_list_delete (&m->ready, t); - t->type = THREAD_UNUSED; thread_add_unuse (m, t); } } @@ -1108,7 +993,7 @@ thread_timer_wait (struct pqueue *queue, struct timeval *timer_val) if (queue->size) { struct thread *next_timer = queue->array[0]; - *timer_val = timeval_subtract (next_timer->u.sands, relative_time); + monotime_until(&next_timer->u.sands, timer_val); return timer_val; } return NULL; @@ -1119,7 +1004,6 @@ thread_run (struct thread_master *m, struct thread *thread, struct thread *fetch) { *fetch = *thread; - thread->type = THREAD_UNUSED; thread_add_unuse (m, thread); return fetch; } @@ -1237,7 +1121,7 @@ thread_timer_process (struct pqueue *queue, struct timeval *timenow) while (queue->size) { thread = queue->array[0]; - if (timeval_cmp (*timenow, thread->u.sands) < 0) + if (timercmp (timenow, &thread->u.sands, <)) return ready; pqueue_dequeue(queue); thread->type = THREAD_READY; @@ -1275,6 +1159,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch) thread_fd_set readfd; thread_fd_set writefd; thread_fd_set exceptfd; + struct timeval now; struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 }; struct timeval timer_val_bg; struct timeval *timer_wait = &timer_val; @@ -1311,15 +1196,20 @@ thread_fetch (struct thread_master *m, struct thread *fetch) /* Calculate select wait timer if nothing else to do */ if (m->ready.count == 0) { - quagga_get_relative (NULL); timer_wait = thread_timer_wait (m->timer, &timer_val); timer_wait_bg = thread_timer_wait (m->background, &timer_val_bg); if (timer_wait_bg && - (!timer_wait || (timeval_cmp (*timer_wait, *timer_wait_bg) > 0))) + (!timer_wait || (timercmp (timer_wait, timer_wait_bg, >)))) timer_wait = timer_wait_bg; } + if (timer_wait && timer_wait->tv_sec < 0) + { + timerclear(&timer_val); + timer_wait = &timer_val; + } + num = fd_select (m, FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait); /* Signals should get quick treatment */ @@ -1334,8 +1224,8 @@ thread_fetch (struct thread_master *m, struct thread *fetch) /* Check foreground timers. Historically, they have had higher priority than I/O threads, so let's push them onto the ready list in front of the I/O threads. */ - quagga_get_relative (NULL); - thread_timer_process (m->timer, &relative_time); + monotime(&now); + thread_timer_process (m->timer, &now); /* Got IO, process it */ if (num > 0) @@ -1351,7 +1241,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch) #endif /* Background timer/events, lowest priority */ - thread_timer_process (m->background, &relative_time); + thread_timer_process (m->background, &now); if ((thread = thread_trim_head (&m->ready)) != NULL) return thread_run (m, thread, fetch); @@ -1380,9 +1270,7 @@ thread_consumed_time (RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) int thread_should_yield (struct thread *thread) { - quagga_get_relative (NULL); - return (timeval_elapsed(relative_time, thread->real) > - thread->yield); + return monotime_since(&thread->real, NULL) > (int64_t)thread->yield; } void @@ -1394,17 +1282,8 @@ thread_set_yield_time (struct thread *thread, unsigned long yield_time) void thread_getrusage (RUSAGE_T *r) { - quagga_get_relative (NULL); + monotime(&r->real); getrusage(RUSAGE_SELF, &(r->cpu)); - r->real = relative_time; - -#ifdef HAVE_CLOCK_MONOTONIC - /* quagga_get_relative() only updates recent_time if gettimeofday - * based, not when using CLOCK_MONOTONIC. As we export recent_time - * and guarantee to update it before threads are run... - */ - quagga_gettimeofday(&recent_time); -#endif /* HAVE_CLOCK_MONOTONIC */ } struct thread *thread_current = NULL; @@ -1418,23 +1297,6 @@ thread_call (struct thread *thread) unsigned long realtime, cputime; RUSAGE_T before, after; - /* Cache a pointer to the relevant cpu history thread, if the thread - * does not have it yet. - * - * Callers submitting 'dummy threads' hence must take care that - * thread->cpu is NULL - */ - if (!thread->hist) - { - struct cpu_thread_history tmp; - - tmp.func = thread->func; - tmp.funcname = thread->funcname; - - thread->hist = hash_get (cpu_record, &tmp, - (void * (*) (void *))cpu_record_hash_alloc); - } - GETRUSAGE (&before); thread->real = before.real; @@ -1479,18 +1341,22 @@ funcname_thread_execute (struct thread_master *m, int val, debugargdef) { - struct thread dummy; + struct cpu_thread_history tmp; + struct thread dummy; memset (&dummy, 0, sizeof (struct thread)); dummy.type = THREAD_EVENT; dummy.add_type = THREAD_EXECUTE; dummy.master = NULL; - dummy.func = func; dummy.arg = arg; dummy.u.val = val; - dummy.funcname = funcname; + tmp.func = dummy.func = func; + tmp.funcname = dummy.funcname = funcname; + dummy.hist = hash_get (cpu_record, &tmp, + (void * (*) (void *))cpu_record_hash_alloc); + dummy.schedfrom = schedfrom; dummy.schedfrom_line = fromln; diff --git a/lib/thread.h b/lib/thread.h index 3440a92999..34adcc4d09 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -23,6 +23,7 @@ #define _ZEBRA_THREAD_H #include <zebra.h> +#include "monotime.h" struct rusage_t { @@ -115,6 +116,7 @@ struct cpu_thread_history { int (*func)(struct thread *); unsigned int total_calls; + unsigned int total_active; struct time_stats { unsigned long total, max; @@ -124,11 +126,6 @@ struct cpu_thread_history const char *funcname; }; -/* Clocks supported by Quagga */ -enum quagga_clkid { - QUAGGA_CLK_MONOTONIC = 1, /* monotonic, against an indeterminate base */ -}; - /* Struct timeval's tv_usec one second value. */ #define TIMER_SECOND_MICRO 1000000L @@ -238,33 +235,17 @@ extern void thread_call (struct thread *); extern unsigned long thread_timer_remain_second (struct thread *); extern struct timeval thread_timer_remain(struct thread*); extern int thread_should_yield (struct thread *); -extern unsigned long timeval_elapsed (struct timeval a, struct timeval b); /* set yield time for thread */ extern void thread_set_yield_time (struct thread *, unsigned long); /* Internal libfrr exports */ extern void thread_getrusage (RUSAGE_T *); -extern struct cmd_element show_thread_cpu_cmd; -extern struct cmd_element clear_thread_cpu_cmd; - -/* replacements for the system gettimeofday(), clock_gettime() and - * time() functions, providing support for non-decrementing clock on - * all systems, and fully monotonic on /some/ systems. - */ -extern int quagga_gettime (enum quagga_clkid, struct timeval *); -extern time_t quagga_monotime (void); +extern void thread_cmd_init (void); /* Returns elapsed real (wall clock) time. */ extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, unsigned long *cpu_time_elapsed); -/* Global variable containing a recent result from gettimeofday. This can - be used instead of calling gettimeofday if a recent value is sufficient. - This is guaranteed to be refreshed before a thread is called. */ -extern struct timeval recent_time; -/* Similar to recent_time, but a monotonically increasing time value */ -extern struct timeval recent_relative_time (void); - /* only for use in logging functions! */ extern struct thread *thread_current; diff --git a/lib/vector.c b/lib/vector.c index 03ad3171d6..e16fcf5315 100644 --- a/lib/vector.c +++ b/lib/vector.c @@ -25,7 +25,7 @@ #include "memory.h" DEFINE_MTYPE_STATIC(LIB, VECTOR, "Vector") -DEFINE_MTYPE( LIB, VECTOR_INDEX, "Vector index") +DEFINE_MTYPE_STATIC(LIB, VECTOR_INDEX, "Vector index") /* Initialize vector : allocate memory and return vector. */ vector @@ -44,18 +44,6 @@ vector_init (unsigned int size) } void -vector_only_wrapper_free (vector v) -{ - XFREE (MTYPE_VECTOR, v); -} - -void -vector_only_index_free (void *index) -{ - XFREE (MTYPE_VECTOR_INDEX, index); -} - -void vector_free (vector v) { XFREE (MTYPE_VECTOR_INDEX, v->index); @@ -177,6 +165,24 @@ vector_unset (vector v, unsigned int i) } } +void +vector_unset_value (vector v, void *val) +{ + size_t i; + + for (i = 0; i < v->active; i++) + if (v->index[i] == val) + { + v->index[i] = NULL; + break; + } + + if (i + 1 == v->active) + do + v->active--; + while (i && v->index[--i] == NULL); +} + /* Count the number of not emplty slot. */ unsigned int vector_count (vector v) diff --git a/lib/vector.h b/lib/vector.h index d8f4c78608..f57f28bbd0 100644 --- a/lib/vector.h +++ b/lib/vector.h @@ -24,7 +24,6 @@ #define _ZEBRA_VECTOR_H #include "memory.h" -DECLARE_MTYPE(VECTOR_INDEX) /* struct for vector */ struct _vector @@ -54,9 +53,9 @@ extern int vector_empty_slot (vector v); extern int vector_set (vector v, void *val); extern int vector_set_index (vector v, unsigned int i, void *val); extern void vector_unset (vector v, unsigned int i); +extern void vector_unset_value (vector v, void *val); + extern unsigned int vector_count (vector v); -extern void vector_only_wrapper_free (vector v); -extern void vector_only_index_free (void *index); extern void vector_free (vector v); extern vector vector_copy (vector v); @@ -480,20 +480,21 @@ DEFUN (vrf, "Select a VRF to configure\n" "VRF's name\n") { + int idx_name = 1; + const char *vrfname = argv[idx_name]->arg; struct vrf *vrfp; - size_t sl; - if ((sl = strlen(argv[0])) > VRF_NAMSIZ) + if (strlen(vrfname) > VRF_NAMSIZ) { vty_out (vty, "%% VRF name %s is invalid: length exceeds " - "%d characters%s", - argv[0], VRF_NAMSIZ, VTY_NEWLINE); + "%d characters%s", + vrfname, VRF_NAMSIZ, VTY_NEWLINE); return CMD_WARNING; } - vrfp = vrf_get (VRF_UNKNOWN, argv[0]); + vrfp = vrf_get (VRF_UNKNOWN, vrfname); - VTY_PUSH_CONTEXT_COMPAT (VRF_NODE, vrfp); + VTY_PUSH_CONTEXT (VRF_NODE, vrfp); return CMD_SUCCESS; } @@ -505,20 +506,22 @@ DEFUN_NOSH (no_vrf, "Delete a pseudo VRF's configuration\n" "VRF's name\n") { + const char *vrfname = argv[2]->arg; + struct vrf *vrfp; - vrfp = vrf_lookup_by_name (argv[0]); + vrfp = vrf_lookup_by_name (vrfname); if (vrfp == NULL) { - vty_out (vty, "%% VRF %s does not exist%s", argv[0], VTY_NEWLINE); + vty_out (vty, "%% VRF %s does not exist%s", vrfname, VTY_NEWLINE); return CMD_WARNING; } if (CHECK_FLAG (vrfp->status, VRF_ACTIVE)) { vty_out (vty, "%% Only inactive VRFs can be deleted%s", - VTY_NEWLINE); + VTY_NEWLINE); return CMD_WARNING; } @@ -527,6 +530,14 @@ DEFUN_NOSH (no_vrf, return CMD_SUCCESS; } + +struct cmd_node vrf_node = +{ + VRF_NODE, + "%s(config-vrf)# ", + 1 +}; + /* * Debug CLI for vrf's */ @@ -579,3 +590,12 @@ vrf_install_commands (void) install_element (CONFIG_NODE, &no_vrf_debug_cmd); install_element (ENABLE_NODE, &no_vrf_debug_cmd); } + +void +vrf_cmd_init (int (*writefunc)(struct vty *vty)) +{ + install_element (CONFIG_NODE, &vrf_cmd); + install_element (CONFIG_NODE, &no_vrf_cmd); + install_node (&vrf_node, writefunc); + install_default (VRF_NODE); +} @@ -26,6 +26,7 @@ #include "openbsd-tree.h" #include "linklist.h" #include "qobj.h" +#include "vty.h" /* The default NS ID */ #define NS_DEFAULT 0 @@ -33,6 +34,7 @@ /* The default VRF ID */ #define VRF_DEFAULT 0 #define VRF_UNKNOWN UINT16_MAX +#define VRF_ALL UINT16_MAX - 1 /* Pending: May need to refine this. */ #ifndef IFLA_VRF_MAX @@ -52,11 +54,7 @@ enum { /* * The command strings */ - -#define VRF_CMD_STR "vrf NAME" #define VRF_CMD_HELP_STR "Specify the VRF\nThe VRF name\n" - -#define VRF_ALL_CMD_STR "vrf all" #define VRF_ALL_CMD_HELP_STR "Specify the VRF\nAll VRFs\n" /* @@ -175,6 +173,8 @@ extern int vrf_bitmap_check (vrf_bitmap_t, vrf_id_t); extern void vrf_init (void); extern void vrf_terminate (void); +extern void vrf_cmd_init (int (*writefunc)(struct vty *vty)); + /* * VRF utilities */ @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. + * 02111-1307, USA. */ #include <zebra.h> @@ -44,7 +44,7 @@ DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer") DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history") /* Vty events */ -enum event +enum event { VTY_SERV, VTY_READ, @@ -117,37 +117,37 @@ vty_out (struct vty *vty, const char *format, ...) /* Initial buffer is not enough. */ if (len < 0 || len >= size) - { - while (1) - { - if (len > -1) - size = len + 1; - else - size = size * 2; - - p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); - if (! p) - return -1; - - va_start (args, format); - len = vsnprintf (p, size, format, args); - va_end (args); - - if (len > -1 && len < size) - break; - } - } + { + while (1) + { + if (len > -1) + size = len + 1; + else + size = size * 2; + + p = XREALLOC (MTYPE_VTY_OUT_BUF, p, size); + if (! p) + return -1; + + va_start (args, format); + len = vsnprintf (p, size, format, args); + va_end (args); + + if (len > -1 && len < size) + break; + } + } /* When initial buffer is enough to store all output. */ if (! p) - p = buf; + p = buf; /* Pointer p must point out buffer. */ buffer_put (vty->obuf, (u_char *) p, len); /* If p is not different with buf, it is allocated buffer. */ if (p != buf) - XFREE (MTYPE_VTY_OUT_BUF, p); + XFREE (MTYPE_VTY_OUT_BUF, p); } return len; @@ -155,7 +155,7 @@ vty_out (struct vty *vty, const char *format, ...) static int vty_log_out (struct vty *vty, const char *level, const char *proto_str, - const char *format, struct timestamp_control *ctl, va_list va) + const char *format, struct timestamp_control *ctl, va_list va) { int ret; int len; @@ -189,13 +189,13 @@ vty_log_out (struct vty *vty, const char *level, const char *proto_str, if (write(vty->wfd, buf, len) < 0) { if (ERRNO_IO_RETRY(errno)) - /* Kernel buffer is full, probably too much debugging output, so just - drop the data and ignore. */ - return -1; + /* Kernel buffer is full, probably too much debugging output, so just + drop the data and ignore. */ + return -1; /* Fatal I/O error. */ vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ zlog_warn("%s: write failed to vty client fd %d, closing: %s", - __func__, vty->fd, safe_strerror(errno)); + __func__, vty->fd, safe_strerror(errno)); buffer_reset(vty->obuf); /* cannot call vty_close, because a parent routine may still try to access the vty struct */ @@ -211,10 +211,10 @@ void vty_time_print (struct vty *vty, int cr) { char buf[QUAGGA_TIMESTAMP_LEN]; - + if (quagga_timestamp(0, buf, sizeof(buf)) == 0) { - zlog (NULL, LOG_INFO, "quagga_timestamp error"); + zlog_info("quagga_timestamp error"); return; } if (cr) @@ -236,20 +236,20 @@ vty_hello (struct vty *vty) f = fopen (host.motdfile, "r"); if (f) - { - while (fgets (buf, sizeof (buf), f)) - { - char *s; - /* work backwards to ignore trailling isspace() */ - for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1)); - s--); - *s = '\0'; - vty_out (vty, "%s%s", buf, VTY_NEWLINE); - } - fclose (f); - } + { + while (fgets (buf, sizeof (buf), f)) + { + char *s; + /* work backwards to ignore trailling isspace() */ + for (s = buf + strlen (buf); (s > buf) && isspace ((int)*(s - 1)); + s--); + *s = '\0'; + vty_out (vty, "%s%s", buf, VTY_NEWLINE); + } + fclose (f); + } else - vty_out (vty, "MOTD file not found%s", VTY_NEWLINE); + vty_out (vty, "MOTD file not found%s", VTY_NEWLINE); } else if (host.motd) vty_out (vty, "%s", host.motd); @@ -266,10 +266,10 @@ vty_prompt (struct vty *vty) { hostname = host.name; if (!hostname) - { - uname (&names); - hostname = names.nodename; - } + { + uname (&names); + hostname = names.nodename; + } vty_out (vty, cmd_prompt (vty->node), hostname); } } @@ -322,7 +322,7 @@ vty_new () { struct vty *new = XCALLOC (MTYPE_VTY, sizeof (struct vty)); - new->obuf = buffer_new(0); /* Use default buffer size. */ + new->obuf = buffer_new(0); /* Use default buffer size. */ new->buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); new->error_buf = XCALLOC (MTYPE_VTY, VTY_BUFSIZ); new->max = VTY_BUFSIZ; @@ -343,19 +343,19 @@ vty_auth (struct vty *vty, char *buf) { case AUTH_NODE: if (host.encrypt) - passwd = host.password_encrypt; + passwd = host.password_encrypt; else - passwd = host.password; + passwd = host.password; if (host.advanced) - next_node = host.enable ? VIEW_NODE : ENABLE_NODE; + next_node = host.enable ? VIEW_NODE : ENABLE_NODE; else - next_node = VIEW_NODE; + next_node = VIEW_NODE; break; case AUTH_ENABLE_NODE: if (host.encrypt) - passwd = host.enable_encrypt; + passwd = host.enable_encrypt; else - passwd = host.enable; + passwd = host.enable; next_node = ENABLE_NODE; break; } @@ -363,9 +363,9 @@ vty_auth (struct vty *vty, char *buf) if (passwd) { if (host.encrypt) - fail = strcmp (crypt(buf, passwd), passwd); + fail = strcmp (crypt(buf, passwd), passwd); else - fail = strcmp (buf, passwd); + fail = strcmp (buf, passwd); } else fail = 1; @@ -373,26 +373,26 @@ vty_auth (struct vty *vty, char *buf) if (! fail) { vty->fail = 0; - vty->node = next_node; /* Success ! */ + vty->node = next_node; /* Success ! */ } else { vty->fail++; if (vty->fail >= 3) - { - if (vty->node == AUTH_NODE) - { - vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); - vty->status = VTY_CLOSE; - } - else - { - /* AUTH_ENABLE_NODE */ - vty->fail = 0; - vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); + { + if (vty->node == AUTH_NODE) + { + vty_out (vty, "%% Bad passwords, too many failures!%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + } + else + { + /* AUTH_ENABLE_NODE */ + vty->fail = 0; + vty_out (vty, "%% Bad enable passwords, too many failures!%s", VTY_NEWLINE); vty->status = VTY_CLOSE; - } - } + } + } } } @@ -419,7 +419,7 @@ vty_command (struct vty *vty, char *buf) if (cp != NULL && *cp != '\0') { unsigned i; - char vty_str[VTY_BUFSIZ]; + char vty_str[VTY_BUFSIZ]; char prompt_str[VTY_BUFSIZ]; /* format the base vty info */ @@ -437,7 +437,7 @@ vty_command (struct vty *vty, char *buf) snprintf(prompt_str, sizeof(prompt_str), cmd_prompt (vty->node), vty_str); /* now log the command */ - zlog(NULL, LOG_ERR, "%s%s", prompt_str, buf); + zlog_err("%s%s", prompt_str, buf); } /* Split readline string up into the vector */ vline = cmd_make_strvec (buf); @@ -457,18 +457,15 @@ vty_command (struct vty *vty, char *buf) ret = cmd_execute_command (vline, vty, NULL, 0); /* Get the name of the protocol if any */ - if (zlog_default) - protocolname = zlog_proto_names[zlog_default->protocol]; - else - protocolname = zlog_proto_names[ZLOG_NONE]; - + protocolname = zlog_protoname(); + #ifdef CONSUMED_TIME_CHECK GETRUSAGE(&after); if ((realtime = thread_consumed_time(&after, &before, &cputime)) > - CONSUMED_TIME_CHECK) + CONSUMED_TIME_CHECK) /* Warn about CPU hog that must be fixed. */ zlog_warn("SLOW COMMAND: command took %lums (cpu time %lums): %s", - realtime/1000, cputime/1000, buf); + realtime/1000, cputime/1000, buf); } #endif /* CONSUMED_TIME_CHECK */ @@ -476,18 +473,18 @@ vty_command (struct vty *vty, char *buf) switch (ret) { case CMD_WARNING: - if (vty->type == VTY_FILE) - vty_out (vty, "Warning...%s", VTY_NEWLINE); - break; + if (vty->type == VTY_FILE) + vty_out (vty, "Warning...%s", VTY_NEWLINE); + break; case CMD_ERR_AMBIGUOUS: - vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); - break; + vty_out (vty, "%% Ambiguous command.%s", VTY_NEWLINE); + break; case CMD_ERR_NO_MATCH: - vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); - break; + vty_out (vty, "%% [%s] Unknown command: %s%s", protocolname, buf, VTY_NEWLINE); + break; case CMD_ERR_INCOMPLETE: - vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); - break; + vty_out (vty, "%% Command incomplete.%s", VTY_NEWLINE); + break; } cmd_free_strvec (vline); @@ -685,7 +682,7 @@ vty_forward_word (struct vty *vty) { while (vty->cp != vty->length && vty->buf[vty->cp] != ' ') vty_forward_char (vty); - + while (vty->cp != vty->length && vty->buf[vty->cp] == ' ') vty_forward_char (vty); } @@ -715,7 +712,7 @@ static void vty_down_level (struct vty *vty) { vty_out (vty, "%s", VTY_NEWLINE); - (*config_exit_cmd.func)(NULL, vty, 0, NULL); + cmd_exit (vty); vty_prompt (vty); vty->cp = 0; } @@ -742,6 +739,7 @@ vty_end_config (struct vty *vty) case BGP_VPNV6_NODE: case BGP_ENCAP_NODE: case BGP_ENCAPV6_NODE: + case BGP_VRF_POLICY_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: @@ -749,6 +747,7 @@ vty_end_config (struct vty *vty) case BGP_IPV4M_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: + case BGP_EVPN_NODE: case RMAP_NODE: case OSPF_NODE: case OSPF6_NODE: @@ -791,14 +790,14 @@ vty_delete_char (struct vty *vty) } if (vty->cp == vty->length) - return; /* completion need here? */ + return; /* completion need here? */ size = vty->length - vty->cp; vty->length--; memmove (&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1); vty->buf[vty->length] = '\0'; - + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) return; @@ -828,7 +827,7 @@ vty_kill_line (struct vty *vty) int size; size = vty->length - vty->cp; - + if (size == 0) return; @@ -922,8 +921,8 @@ vty_complete_command (struct vty *vty) if (isspace ((int) vty->buf[vty->length - 1])) vector_set (vline, NULL); - matched = cmd_complete_command_lib (vline, vty, &ret, 1); - + matched = cmd_complete_command (vline, vty, &ret); + cmd_free_strvec (vline); vty_out (vty, "%s", VTY_NEWLINE); @@ -940,6 +939,14 @@ vty_complete_command (struct vty *vty) vty_redraw_line (vty); break; case CMD_COMPLETE_FULL_MATCH: + if (!matched[0]) + { + /* 2016-11-28 equinox -- need to debug, SEGV here */ + vty_out (vty, "%% CLI BUG: FULL_MATCH with NULL str%s", VTY_NEWLINE); + vty_prompt (vty); + vty_redraw_line (vty); + break; + } vty_prompt (vty); vty_redraw_line (vty); vty_backward_pure_word (vty); @@ -953,17 +960,15 @@ vty_complete_command (struct vty *vty) vty_backward_pure_word (vty); vty_insert_word_overwrite (vty, matched[0]); XFREE (MTYPE_TMP, matched[0]); - vector_only_index_free (matched); - return; break; case CMD_COMPLETE_LIST_MATCH: for (i = 0; matched[i] != NULL; i++) - { - if (i != 0 && ((i % 6) == 0)) - vty_out (vty, "%s", VTY_NEWLINE); - vty_out (vty, "%-10s ", matched[i]); - XFREE (MTYPE_TMP, matched[i]); - } + { + if (i != 0 && ((i % 6) == 0)) + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%-10s ", matched[i]); + XFREE (MTYPE_TMP, matched[i]); + } vty_out (vty, "%s", VTY_NEWLINE); vty_prompt (vty); @@ -977,18 +982,18 @@ vty_complete_command (struct vty *vty) break; } if (matched) - vector_only_index_free (matched); + XFREE (MTYPE_TMP, matched); } static void vty_describe_fold (struct vty *vty, int cmd_width, - unsigned int desc_width, struct cmd_token *token) + unsigned int desc_width, struct cmd_token *token) { char *buf; const char *cmd, *p; int pos; - cmd = token->cmd[0] == '.' ? token->cmd + 1 : token->cmd; + cmd = token->text; if (desc_width <= 0) { @@ -1037,7 +1042,7 @@ vty_describe_command (struct vty *vty) vline = vector_init (1); vector_set (vline, NULL); } - else + else if (isspace ((int) vty->buf[vty->length - 1])) vector_set (vline, NULL); @@ -1056,24 +1061,22 @@ vty_describe_command (struct vty *vty) vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); goto out; break; - } + } /* Get width of command string. */ width = 0; for (i = 0; i < vector_active (describe); i++) if ((token = vector_slot (describe, i)) != NULL) { - unsigned int len; + unsigned int len; - if (token->cmd[0] == '\0') - continue; + if (token->text[0] == '\0') + continue; - len = strlen (token->cmd); - if (token->cmd[0] == '.') - len--; + len = strlen (token->text); - if (width < len) - width = len; + if (width < len) + width = len; } /* Get width of description string. */ @@ -1083,45 +1086,45 @@ vty_describe_command (struct vty *vty) for (i = 0; i < vector_active (describe); i++) if ((token = vector_slot (describe, i)) != NULL) { - if (token->cmd[0] == '\0') - continue; - - if (strcmp (token->cmd, command_cr) == 0) - { - token_cr = token; - continue; - } - - if (!token->desc) - vty_out (vty, " %-s%s", - token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, - VTY_NEWLINE); - else if (desc_width >= strlen (token->desc)) - vty_out (vty, " %-*s %s%s", width, - token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, - token->desc, VTY_NEWLINE); - else - vty_describe_fold (vty, width, desc_width, token); + if (token->text[0] == '\0') + continue; + + if (strcmp (token->text, CMD_CR_TEXT) == 0) + { + token_cr = token; + continue; + } + + if (!token->desc) + vty_out (vty, " %-s%s", + token->text, + VTY_NEWLINE); + else if (desc_width >= strlen (token->desc)) + vty_out (vty, " %-*s %s%s", width, + token->text, + token->desc, VTY_NEWLINE); + else + vty_describe_fold (vty, width, desc_width, token); #if 0 - vty_out (vty, " %-*s %s%s", width - desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, - desc->str ? desc->str : "", VTY_NEWLINE); + vty_out (vty, " %-*s %s%s", width + desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, + desc->str ? desc->str : "", VTY_NEWLINE); #endif /* 0 */ } if ((token = token_cr)) { if (!token->desc) - vty_out (vty, " %-s%s", - token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, - VTY_NEWLINE); + vty_out (vty, " %-s%s", + token->text, + VTY_NEWLINE); else if (desc_width >= strlen (token->desc)) - vty_out (vty, " %-*s %s%s", width, - token->cmd[0] == '.' ? token->cmd + 1 : token->cmd, - token->desc, VTY_NEWLINE); + vty_out (vty, " %-*s %s%s", width, + token->text, + token->desc, VTY_NEWLINE); else - vty_describe_fold (vty, width, desc_width, token); + vty_describe_fold (vty, width, desc_width, token); } out: @@ -1232,41 +1235,41 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) for (i = 0; i < nbytes; i++) { switch (buf[i]) - { - case IAC: - vty_out (vty, "IAC "); - break; - case WILL: - vty_out (vty, "WILL "); - break; - case WONT: - vty_out (vty, "WONT "); - break; - case DO: - vty_out (vty, "DO "); - break; - case DONT: - vty_out (vty, "DONT "); - break; - case SB: - vty_out (vty, "SB "); - break; - case SE: - vty_out (vty, "SE "); - break; - case TELOPT_ECHO: - vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); - break; - case TELOPT_SGA: - vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); - break; - case TELOPT_NAWS: - vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); - break; - default: - vty_out (vty, "%x ", buf[i]); - break; - } + { + case IAC: + vty_out (vty, "IAC "); + break; + case WILL: + vty_out (vty, "WILL "); + break; + case WONT: + vty_out (vty, "WONT "); + break; + case DO: + vty_out (vty, "DO "); + break; + case DONT: + vty_out (vty, "DONT "); + break; + case SB: + vty_out (vty, "SB "); + break; + case SE: + vty_out (vty, "SE "); + break; + case TELOPT_ECHO: + vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE); + break; + case TELOPT_SGA: + vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE); + break; + case TELOPT_NAWS: + vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE); + break; + default: + vty_out (vty, "%x ", buf[i]); + break; + } } vty_out (vty, "%s", VTY_NEWLINE); @@ -1279,42 +1282,42 @@ vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes) vty->iac_sb_in_progress = 1; return 0; break; - case SE: + case SE: { - if (!vty->iac_sb_in_progress) - return 0; - - if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) - { - vty->iac_sb_in_progress = 0; - return 0; - } - switch (vty->sb_buf[0]) - { - case TELOPT_NAWS: - if (vty->sb_len != TELNET_NAWS_SB_LEN) - zlog_warn("RFC 1073 violation detected: telnet NAWS option " - "should send %d characters, but we received %lu", - TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); - else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) - zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, " - "too small to handle the telnet NAWS option", - (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); - else - { - vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); - vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); + if (!vty->iac_sb_in_progress) + return 0; + + if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) + { + vty->iac_sb_in_progress = 0; + return 0; + } + switch (vty->sb_buf[0]) + { + case TELOPT_NAWS: + if (vty->sb_len != TELNET_NAWS_SB_LEN) + zlog_warn("RFC 1073 violation detected: telnet NAWS option " + "should send %d characters, but we received %lu", + TELNET_NAWS_SB_LEN, (u_long)vty->sb_len); + else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN) + zlog_err("Bug detected: sizeof(vty->sb_buf) %lu < %d, " + "too small to handle the telnet NAWS option", + (u_long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN); + else + { + vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]); + vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]); #ifdef TELNET_OPTION_DEBUG - vty_out(vty, "TELNET NAWS window size negotiation completed: " - "width %d, height %d%s", - vty->width, vty->height, VTY_NEWLINE); + vty_out(vty, "TELNET NAWS window size negotiation completed: " + "width %d, height %d%s", + vty->width, vty->height, VTY_NEWLINE); #endif - } - break; - } - vty->iac_sb_in_progress = 0; - return 0; - break; + } + break; + } + vty->iac_sb_in_progress = 0; + return 0; + break; } default: break; @@ -1339,7 +1342,7 @@ vty_execute (struct vty *vty) default: ret = vty_command (vty, vty->buf); if (vty->type == VTY_TERM) - vty_hist_add (vty); + vty_hist_add (vty); break; } @@ -1409,187 +1412,187 @@ vty_read (struct thread *thread) if ((nbytes = read (vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) { if (nbytes < 0) - { - if (ERRNO_IO_RETRY(errno)) - { - vty_event (VTY_READ, vty_sock, vty); - return 0; - } - vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("%s: read error on vty client fd %d, closing: %s", - __func__, vty->fd, safe_strerror(errno)); + { + if (ERRNO_IO_RETRY(errno)) + { + vty_event (VTY_READ, vty_sock, vty); + return 0; + } + vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ + zlog_warn("%s: read error on vty client fd %d, closing: %s", + __func__, vty->fd, safe_strerror(errno)); buffer_reset(vty->obuf); - } + } vty->status = VTY_CLOSE; } - for (i = 0; i < nbytes; i++) + for (i = 0; i < nbytes; i++) { if (buf[i] == IAC) - { - if (!vty->iac) - { - vty->iac = 1; - continue; - } - else - { - vty->iac = 0; - } - } - + { + if (!vty->iac) + { + vty->iac = 1; + continue; + } + else + { + vty->iac = 0; + } + } + if (vty->iac_sb_in_progress && !vty->iac) - { - if (vty->sb_len < sizeof(vty->sb_buf)) - vty->sb_buf[vty->sb_len] = buf[i]; - vty->sb_len++; - continue; - } + { + if (vty->sb_len < sizeof(vty->sb_buf)) + vty->sb_buf[vty->sb_len] = buf[i]; + vty->sb_len++; + continue; + } if (vty->iac) - { - /* In case of telnet command */ - int ret = 0; - ret = vty_telnet_option (vty, buf + i, nbytes - i); - vty->iac = 0; - i += ret; - continue; - } - + { + /* In case of telnet command */ + int ret = 0; + ret = vty_telnet_option (vty, buf + i, nbytes - i); + vty->iac = 0; + i += ret; + continue; + } + if (vty->status == VTY_MORE) - { - switch (buf[i]) - { - case CONTROL('C'): - case 'q': - case 'Q': - vty_buffer_reset (vty); - break; + { + switch (buf[i]) + { + case CONTROL('C'): + case 'q': + case 'Q': + vty_buffer_reset (vty); + break; #if 0 /* More line does not work for "show ip bgp". */ - case '\n': - case '\r': - vty->status = VTY_MORELINE; - break; + case '\n': + case '\r': + vty->status = VTY_MORELINE; + break; #endif - default: - break; - } - continue; - } + default: + break; + } + continue; + } /* Escape character. */ if (vty->escape == VTY_ESCAPE) - { - vty_escape_map (buf[i], vty); - continue; - } + { + vty_escape_map (buf[i], vty); + continue; + } /* Pre-escape status. */ if (vty->escape == VTY_PRE_ESCAPE) - { - switch (buf[i]) - { - case '[': - vty->escape = VTY_ESCAPE; - break; - case 'b': - vty_backward_word (vty); - vty->escape = VTY_NORMAL; - break; - case 'f': - vty_forward_word (vty); - vty->escape = VTY_NORMAL; - break; - case 'd': - vty_forward_kill_word (vty); - vty->escape = VTY_NORMAL; - break; - case CONTROL('H'): - case 0x7f: - vty_backward_kill_word (vty); - vty->escape = VTY_NORMAL; - break; - default: - vty->escape = VTY_NORMAL; - break; - } - continue; - } + { + switch (buf[i]) + { + case '[': + vty->escape = VTY_ESCAPE; + break; + case 'b': + vty_backward_word (vty); + vty->escape = VTY_NORMAL; + break; + case 'f': + vty_forward_word (vty); + vty->escape = VTY_NORMAL; + break; + case 'd': + vty_forward_kill_word (vty); + vty->escape = VTY_NORMAL; + break; + case CONTROL('H'): + case 0x7f: + vty_backward_kill_word (vty); + vty->escape = VTY_NORMAL; + break; + default: + vty->escape = VTY_NORMAL; + break; + } + continue; + } switch (buf[i]) - { - case CONTROL('A'): - vty_beginning_of_line (vty); - break; - case CONTROL('B'): - vty_backward_char (vty); - break; - case CONTROL('C'): - vty_stop_input (vty); - break; - case CONTROL('D'): - vty_delete_char (vty); - break; - case CONTROL('E'): - vty_end_of_line (vty); - break; - case CONTROL('F'): - vty_forward_char (vty); - break; - case CONTROL('H'): - case 0x7f: - vty_delete_backward_char (vty); - break; - case CONTROL('K'): - vty_kill_line (vty); - break; - case CONTROL('N'): - vty_next_line (vty); - break; - case CONTROL('P'): - vty_previous_line (vty); - break; - case CONTROL('T'): - vty_transpose_chars (vty); - break; - case CONTROL('U'): - vty_kill_line_from_beginning (vty); - break; - case CONTROL('W'): - vty_backward_kill_word (vty); - break; - case CONTROL('Z'): - vty_end_config (vty); - break; - case '\n': - case '\r': - vty_out (vty, "%s", VTY_NEWLINE); - vty_execute (vty); - break; - case '\t': - vty_complete_command (vty); - break; - case '?': - if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) - vty_self_insert (vty, buf[i]); - else - vty_describe_command (vty); - break; - case '\033': - if (i + 1 < nbytes && buf[i + 1] == '[') - { - vty->escape = VTY_ESCAPE; - i++; - } - else - vty->escape = VTY_PRE_ESCAPE; - break; - default: - if (buf[i] > 31 && buf[i] < 127) - vty_self_insert (vty, buf[i]); - break; - } + { + case CONTROL('A'): + vty_beginning_of_line (vty); + break; + case CONTROL('B'): + vty_backward_char (vty); + break; + case CONTROL('C'): + vty_stop_input (vty); + break; + case CONTROL('D'): + vty_delete_char (vty); + break; + case CONTROL('E'): + vty_end_of_line (vty); + break; + case CONTROL('F'): + vty_forward_char (vty); + break; + case CONTROL('H'): + case 0x7f: + vty_delete_backward_char (vty); + break; + case CONTROL('K'): + vty_kill_line (vty); + break; + case CONTROL('N'): + vty_next_line (vty); + break; + case CONTROL('P'): + vty_previous_line (vty); + break; + case CONTROL('T'): + vty_transpose_chars (vty); + break; + case CONTROL('U'): + vty_kill_line_from_beginning (vty); + break; + case CONTROL('W'): + vty_backward_kill_word (vty); + break; + case CONTROL('Z'): + vty_end_config (vty); + break; + case '\n': + case '\r': + vty_out (vty, "%s", VTY_NEWLINE); + vty_execute (vty); + break; + case '\t': + vty_complete_command (vty); + break; + case '?': + if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE) + vty_self_insert (vty, buf[i]); + else + vty_describe_command (vty); + break; + case '\033': + if (i + 1 < nbytes && buf[i + 1] == '[') + { + vty->escape = VTY_ESCAPE; + i++; + } + else + vty->escape = VTY_PRE_ESCAPE; + break; + default: + if (buf[i] > 31 && buf[i] < 127) + vty_self_insert (vty, buf[i]); + break; + } } /* Check status. */ @@ -1629,36 +1632,36 @@ vty_flush (struct thread *thread) flushrc = buffer_flush_available(vty->obuf, vty_sock); else if (vty->status == VTY_MORELINE) flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, - 1, erase, 0); + 1, erase, 0); else flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, - vty->lines >= 0 ? vty->lines : - vty->height, - erase, 0); + vty->lines >= 0 ? vty->lines : + vty->height, + erase, 0); switch (flushrc) { case BUFFER_ERROR: vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ zlog_warn("buffer_flush failed on vty client fd %d, closing", - vty->fd); + vty->fd); buffer_reset(vty->obuf); vty_close(vty); return 0; case BUFFER_EMPTY: if (vty->status == VTY_CLOSE) - vty_close (vty); + vty_close (vty); else - { - vty->status = VTY_NORMAL; - if (vty->lines == 0) - vty_event (VTY_READ, vty_sock, vty); - } + { + vty->status = VTY_NORMAL; + if (vty->lines == 0) + vty_event (VTY_READ, vty_sock, vty); + } break; case BUFFER_PENDING: /* There is more data waiting to be written. */ vty->status = VTY_MORE; if (vty->lines == 0) - vty_event (VTY_WRITE, vty_sock, vty); + vty_event (VTY_WRITE, vty_sock, vty); break; } @@ -1711,9 +1714,9 @@ vty_create (int vty_sock, union sockunion *su) if (no_password_check) { if (host.advanced) - vty->node = ENABLE_NODE; + vty->node = ENABLE_NODE; else - vty->node = VIEW_NODE; + vty->node = VIEW_NODE; } if (host.lines >= 0) vty->lines = host.lines; @@ -1722,12 +1725,12 @@ vty_create (int vty_sock, union sockunion *su) { /* Vty is not available if password isn't set. */ if (host.password == NULL && host.password_encrypt == NULL) - { - vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); - vty->status = VTY_CLOSE; - vty_close (vty); - return NULL; - } + { + vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE); + vty->status = VTY_CLOSE; + vty_close (vty); + return NULL; + } } /* Say hello to the world. */ @@ -1848,54 +1851,51 @@ vty_accept (struct thread *thread) if (p.family == AF_INET && vty_accesslist_name) { if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) && - (access_list_apply (acl, &p) == FILTER_DENY)) - { - zlog (NULL, LOG_INFO, "Vty connection refused from %s", - sockunion2str (&su, buf, SU_ADDRSTRLEN)); - close (vty_sock); - - /* continue accepting connections */ - vty_event (VTY_SERV, accept_sock, NULL); - - return 0; - } - } - -#ifdef HAVE_IPV6 + (access_list_apply (acl, &p) == FILTER_DENY)) + { + zlog_info ("Vty connection refused from %s", + sockunion2str (&su, buf, SU_ADDRSTRLEN)); + close (vty_sock); + + /* continue accepting connections */ + vty_event (VTY_SERV, accept_sock, NULL); + + return 0; + } + } + /* VTY's ipv6 accesslist apply. */ if (p.family == AF_INET6 && vty_ipv6_accesslist_name) { if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) && - (access_list_apply (acl, &p) == FILTER_DENY)) - { - zlog (NULL, LOG_INFO, "Vty connection refused from %s", - sockunion2str (&su, buf, SU_ADDRSTRLEN)); - close (vty_sock); - - /* continue accepting connections */ - vty_event (VTY_SERV, accept_sock, NULL); - - return 0; - } - } -#endif /* HAVE_IPV6 */ - + (access_list_apply (acl, &p) == FILTER_DENY)) + { + zlog_info ("Vty connection refused from %s", + sockunion2str (&su, buf, SU_ADDRSTRLEN)); + close (vty_sock); + + /* continue accepting connections */ + vty_event (VTY_SERV, accept_sock, NULL); + + return 0; + } + } + on = 1; - ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, - (char *) &on, sizeof (on)); + ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &on, sizeof (on)); if (ret < 0) - zlog (NULL, LOG_INFO, "can't set sockopt to vty_sock : %s", - safe_strerror (errno)); + zlog_info ("can't set sockopt to vty_sock : %s", + safe_strerror (errno)); - zlog (NULL, LOG_INFO, "Vty connection from %s", - sockunion2str (&su, buf, SU_ADDRSTRLEN)); + zlog_info ("Vty connection from %s", + sockunion2str (&su, buf, SU_ADDRSTRLEN)); vty_create (vty_sock, &su); return 0; } -#ifdef HAVE_IPV6 static void vty_serv_sock_addrinfo (const char *hostname, unsigned short port) { @@ -1926,15 +1926,13 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port) do { if (ainfo->ai_family != AF_INET -#ifdef HAVE_IPV6 - && ainfo->ai_family != AF_INET6 -#endif /* HAVE_IPV6 */ - ) - continue; + && ainfo->ai_family != AF_INET6 + ) + continue; sock = socket (ainfo->ai_family, ainfo->ai_socktype, ainfo->ai_protocol); if (sock < 0) - continue; + continue; sockopt_v6only (ainfo->ai_family, sock); sockopt_reuseaddr (sock); @@ -1943,17 +1941,17 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port) ret = bind (sock, ainfo->ai_addr, ainfo->ai_addrlen); if (ret < 0) - { - close (sock); /* Avoid sd leak. */ - continue; - } + { + close (sock); /* Avoid sd leak. */ + continue; + } ret = listen (sock, 3); - if (ret < 0) - { - close (sock); /* Avoid sd leak. */ - continue; - } + if (ret < 0) + { + close (sock); /* Avoid sd leak. */ + continue; + } vty_event (VTY_SERV, sock, NULL); } @@ -1961,76 +1959,6 @@ vty_serv_sock_addrinfo (const char *hostname, unsigned short port) freeaddrinfo (ainfo_save); } -#else /* HAVE_IPV6 */ - -/* Make vty server socket. */ -static void -vty_serv_sock_family (const char* addr, unsigned short port, int family) -{ - int ret; - union sockunion su; - int accept_sock; - void* naddr=NULL; - - memset (&su, 0, sizeof (union sockunion)); - su.sa.sa_family = family; - if(addr) - switch(family) - { - case AF_INET: - naddr=&su.sin.sin_addr; - break; -#ifdef HAVE_IPV6 - case AF_INET6: - naddr=&su.sin6.sin6_addr; - break; -#endif - } - - if(naddr) - switch(inet_pton(family,addr,naddr)) - { - case -1: - zlog_err("bad address %s",addr); - naddr=NULL; - break; - case 0: - zlog_err("error translating address %s: %s",addr,safe_strerror(errno)); - naddr=NULL; - } - - /* Make new socket. */ - accept_sock = sockunion_stream_socket (&su); - if (accept_sock < 0) - return; - - /* This is server, so reuse address. */ - sockopt_reuseaddr (accept_sock); - sockopt_reuseport (accept_sock); - set_cloexec (accept_sock); - - /* Bind socket to universal address and given port. */ - ret = sockunion_bind (accept_sock, &su, port, naddr); - if (ret < 0) - { - zlog_warn("can't bind socket"); - close (accept_sock); /* Avoid sd leak. */ - return; - } - - /* Listen socket under queue 3. */ - ret = listen (accept_sock, 3); - if (ret < 0) - { - zlog (NULL, LOG_WARNING, "can't listen socket"); - close (accept_sock); /* Avoid sd leak. */ - return; - } - - /* Add vty server event. */ - vty_event (VTY_SERV, accept_sock, NULL); -} -#endif /* HAVE_IPV6 */ #ifdef VTYSH /* For sockaddr_un. */ @@ -2045,7 +1973,7 @@ vty_serv_un (const char *path) struct sockaddr_un serv; mode_t old_mask; struct zprivs_ids_t ids; - + /* First of all, unlink existing socket */ unlink (path); @@ -2063,7 +1991,7 @@ vty_serv_un (const char *path) /* Make server socket. */ memset (&serv, 0, sizeof (struct sockaddr_un)); serv.sun_family = AF_UNIX; - strncpy (serv.sun_path, path, strlen (path)); + strlcpy (serv.sun_path, path, sizeof (serv.sun_path)); #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN len = serv.sun_len = SUN_LEN(&serv); #else @@ -2076,7 +2004,7 @@ vty_serv_un (const char *path) if (ret < 0) { zlog_err("Cannot bind path %s: %s", path, safe_strerror(errno)); - close (sock); /* Avoid sd leak. */ + close (sock); /* Avoid sd leak. */ return; } @@ -2084,7 +2012,7 @@ vty_serv_un (const char *path) if (ret < 0) { zlog_err("listen(fd %d) failed: %s", sock, safe_strerror(errno)); - close (sock); /* Avoid sd leak. */ + close (sock); /* Avoid sd leak. */ return; } @@ -2118,7 +2046,7 @@ vtysh_accept (struct thread *thread) int client_len; struct sockaddr_un client; struct vty *vty; - + accept_sock = THREAD_FD (thread); vty_event (VTYSH_SERV, accept_sock, NULL); @@ -2127,7 +2055,7 @@ vtysh_accept (struct thread *thread) client_len = sizeof (struct sockaddr_un); sock = accept (accept_sock, (struct sockaddr *) &client, - (socklen_t *) &client_len); + (socklen_t *) &client_len); if (sock < 0) { @@ -2198,16 +2126,16 @@ vtysh_read (struct thread *thread) if ((nbytes = read (sock, buf, VTY_READ_BUFSIZ)) <= 0) { if (nbytes < 0) - { - if (ERRNO_IO_RETRY(errno)) - { - vty_event (VTYSH_READ, sock, vty); - return 0; - } - vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ - zlog_warn("%s: read failed on vtysh client fd %d, closing: %s", - __func__, sock, safe_strerror(errno)); - } + { + if (ERRNO_IO_RETRY(errno)) + { + vty_event (VTYSH_READ, sock, vty); + return 0; + } + vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ + zlog_warn("%s: read failed on vtysh client fd %d, closing: %s", + __func__, sock, safe_strerror(errno)); + } buffer_reset(vty->obuf); vty_close (vty); #ifdef VTYSH_DEBUG @@ -2285,14 +2213,7 @@ vty_serv_sock (const char *addr, unsigned short port, const char *path) { /* If port is set to 0, do not listen on TCP/IP at all! */ if (port) - { - -#ifdef HAVE_IPV6 - vty_serv_sock_addrinfo (addr, port); -#else /* ! HAVE_IPV6 */ - vty_serv_sock_family (addr,port, AF_INET); -#endif /* HAVE_IPV6 */ - } + vty_serv_sock_addrinfo (addr, port); #ifdef VTYSH vty_serv_un (path); @@ -2307,6 +2228,7 @@ void vty_close (struct vty *vty) { int i; + bool was_stdio = false; /* Cancel threads.*/ if (vty->t_read) @@ -2341,7 +2263,7 @@ vty_close (struct vty *vty) close (vty->wfd); } else - vty_stdio_reset (); + was_stdio = true; if (vty->buf) XFREE (MTYPE_VTY, vty->buf); @@ -2354,6 +2276,9 @@ vty_close (struct vty *vty) /* OK free vty. */ XFREE (MTYPE_VTY, vty); + + if (was_stdio) + vty_stdio_reset (); } /* When time out occur output message then close connection. */ @@ -2395,45 +2320,51 @@ vty_read_file (FILE *confp) vty->fd = STDIN_FILENO; vty->type = VTY_FILE; vty->node = CONFIG_NODE; - + /* Execute configuration file */ ret = config_from_file (vty, confp, &line_num); /* Flush any previous errors before printing messages below */ buffer_flush_all (vty->obuf, vty->fd); - if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) + if ( !((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO)) ) { + const char *message = NULL; switch (ret) { case CMD_ERR_AMBIGUOUS: - fprintf (stderr, "*** Error reading config: Ambiguous command.\n"); + message = "*** Error reading config: Ambiguous command."; break; case CMD_ERR_NO_MATCH: - fprintf (stderr, "*** Error reading config: There is no such command.\n"); + message = "*** Error reading config: There is no such command."; break; } - fprintf (stderr, "*** Error occured processing line %u, below:\n%s\n", - line_num, vty->error_buf); + fprintf (stderr, "%s\n", message); + zlog_err ("%s", message); + fprintf (stderr, "*** Error occurred processing line %u, below:\n%s\n", + line_num, vty->error_buf); + zlog_err ("*** Error occurred processing line %u, below:\n%s", + line_num, vty->error_buf); } vty_close (vty); } static FILE * -vty_use_backup_config (char *fullpath) +vty_use_backup_config (const char *fullpath) { char *fullpath_sav, *fullpath_tmp; FILE *ret = NULL; - struct stat buf; int tmp, sav; int c; char buffer[512]; - + fullpath_sav = malloc (strlen (fullpath) + strlen (CONF_BACKUP_EXT) + 1); strcpy (fullpath_sav, fullpath); strcat (fullpath_sav, CONF_BACKUP_EXT); - if (stat (fullpath_sav, &buf) == -1) + + sav = open (fullpath_sav, O_RDONLY); + if (sav < 0) { free (fullpath_sav); return NULL; @@ -2441,52 +2372,37 @@ vty_use_backup_config (char *fullpath) fullpath_tmp = malloc (strlen (fullpath) + 8); sprintf (fullpath_tmp, "%s.XXXXXX", fullpath); - + /* Open file to configuration write. */ tmp = mkstemp (fullpath_tmp); if (tmp < 0) - { - free (fullpath_sav); - free (fullpath_tmp); - return NULL; - } + goto out_close_sav; + + if (fchmod (tmp, CONFIGFILE_MASK) != 0) + goto out_close; - sav = open (fullpath_sav, O_RDONLY); - if (sav < 0) - { - unlink (fullpath_tmp); - free (fullpath_sav); - free (fullpath_tmp); - return NULL; - } - while((c = read (sav, buffer, 512)) > 0) { if (write (tmp, buffer, c) <= 0) - { - free (fullpath_sav); - free (fullpath_tmp); - close (sav); - close (tmp); - return NULL; - } + goto out_close; } close (sav); close (tmp); - - if (chmod(fullpath_tmp, CONFIGFILE_MASK) != 0) + + if (rename (fullpath_tmp, fullpath) == 0) + ret = fopen (fullpath, "r"); + else + unlink (fullpath_tmp); + + if (0) { +out_close: + close (tmp); unlink (fullpath_tmp); - free (fullpath_sav); - free (fullpath_tmp); - return NULL; +out_close_sav: + close (sav); } - - if (link (fullpath_tmp, fullpath) == 0) - ret = fopen (fullpath, "r"); - unlink (fullpath_tmp); - free (fullpath_sav); free (fullpath_tmp); return ret; @@ -2494,12 +2410,12 @@ vty_use_backup_config (char *fullpath) /* Read up configuration file from file_name. */ void -vty_read_config (char *config_file, +vty_read_config (const char *config_file, char *config_default_dir) { char cwd[MAXPATHLEN]; FILE *confp = NULL; - char *fullpath; + const char *fullpath; char *tmp = NULL; /* If -f flag specified. */ @@ -2508,12 +2424,12 @@ vty_read_config (char *config_file, if (! IS_DIRECTORY_SEP (config_file[0])) { if (getcwd (cwd, MAXPATHLEN) == NULL) - { - fprintf (stderr, "Failure to determine Current Working Directory %d!\n", errno); - exit (1); - } - tmp = XMALLOC (MTYPE_TMP, - strlen (cwd) + strlen (config_file) + 2); + { + fprintf (stderr, "Failure to determine Current Working Directory %d!\n", errno); + exit (1); + } + tmp = XMALLOC (MTYPE_TMP, + strlen (cwd) + strlen (config_file) + 2); sprintf (tmp, "%s/%s", cwd, config_file); fullpath = tmp; } @@ -2526,14 +2442,14 @@ vty_read_config (char *config_file, { fprintf (stderr, "%s: failed to open configuration file %s: %s\n", __func__, fullpath, safe_strerror (errno)); - + confp = vty_use_backup_config (fullpath); if (confp) fprintf (stderr, "WARNING: using backup configuration file!\n"); else { - fprintf (stderr, "can't open configuration file [%s]\n", - config_file); + fprintf (stderr, "can't open configuration file [%s]\n", + config_file); exit(1); } } @@ -2565,7 +2481,7 @@ vty_read_config (char *config_file, { ret = stat (integrate_default, &conf_stat); if (ret >= 0) - goto tmp_free_and_out; + goto tmp_free_and_out; } #endif /* VTYSH */ confp = fopen (config_default_dir, "r"); @@ -2573,7 +2489,7 @@ vty_read_config (char *config_file, { fprintf (stderr, "%s: failed to open configuration file %s: %s\n", __func__, config_default_dir, safe_strerror (errno)); - + confp = vty_use_backup_config (config_default_dir); if (confp) { @@ -2583,10 +2499,10 @@ vty_read_config (char *config_file, else { fprintf (stderr, "can't open configuration file [%s]\n", - config_default_dir); - goto tmp_free_and_out; + config_default_dir); + goto tmp_free_and_out; } - } + } else fullpath = config_default_dir; } @@ -2599,29 +2515,29 @@ vty_read_config (char *config_file, tmp_free_and_out: if (tmp) - XFREE (MTYPE_TMP, fullpath); + XFREE (MTYPE_TMP, tmp); } /* Small utility function which output log to the VTY. */ void vty_log (const char *level, const char *proto_str, - const char *format, struct timestamp_control *ctl, va_list va) + const char *format, struct timestamp_control *ctl, va_list va) { unsigned int i; struct vty *vty; - + if (!vtyvec) return; for (i = 0; i < vector_active (vtyvec); i++) if ((vty = vector_slot (vtyvec, i)) != NULL) if (vty->monitor) - { - va_list ac; - va_copy(ac, va); - vty_log_out (vty, level, proto_str, format, ctl, ac); - va_end(ac); - } + { + va_list ac; + va_copy(ac, va); + vty_log_out (vty, level, proto_str, format, ctl, ac); + va_end(ac); + } } /* Async-signal-safe version of vty_log for fixed strings. */ @@ -2635,7 +2551,7 @@ vty_log_fixed (char *buf, size_t len) /* vty may not have been initialised */ if (!vtyvec) return; - + iov[0].iov_base = buf; iov[0].iov_len = len; iov[1].iov_base = crlf; @@ -2645,13 +2561,13 @@ vty_log_fixed (char *buf, size_t len) { struct vty *vty; if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor) - /* N.B. We don't care about the return code, since process is - most likely just about to die anyway. */ - if (writev(vty->wfd, iov, 2) == -1) - { - fprintf(stderr, "Failure to writev: %d\n", errno); - exit(-1); - } + /* N.B. We don't care about the return code, since process is + most likely just about to die anyway. */ + if (writev(vty->wfd, iov, 2) == -1) + { + fprintf(stderr, "Failure to writev: %d\n", errno); + exit(-1); + } } } @@ -2718,28 +2634,28 @@ vty_event (enum event event, int sock, struct vty *vty) /* Time out treatment. */ if (vty->v_timeout) - { - if (vty->t_timeout) - thread_cancel (vty->t_timeout); - vty->t_timeout = - thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); - } + { + if (vty->t_timeout) + thread_cancel (vty->t_timeout); + vty->t_timeout = + thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); + } break; case VTY_WRITE: if (! vty->t_write) - vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock); + vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock); break; case VTY_TIMEOUT_RESET: if (vty->t_timeout) - { - thread_cancel (vty->t_timeout); - vty->t_timeout = NULL; - } + { + thread_cancel (vty->t_timeout); + vty->t_timeout = NULL; + } if (vty->v_timeout) - { - vty->t_timeout = - thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); - } + { + vty->t_timeout = + thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); + } break; } } @@ -2755,8 +2671,8 @@ DEFUN (config_who, for (i = 0; i < vector_active (vtyvec); i++) if ((v = vector_slot (vtyvec, i)) != NULL) vty_out (vty, "%svty[%d] connected from %s.%s", - v->config ? "*" : " ", - i, v->address, VTY_NEWLINE); + v->config ? "*" : " ", + i, v->address, VTY_NEWLINE); return CMD_SUCCESS; } @@ -2797,21 +2713,24 @@ exec_timeout (struct vty *vty, const char *min_str, const char *sec_str) DEFUN (exec_timeout_min, exec_timeout_min_cmd, - "exec-timeout <0-35791>", + "exec-timeout (0-35791)", "Set timeout value\n" "Timeout value in minutes\n") { - return exec_timeout (vty, argv[0], NULL); + int idx_number = 1; + return exec_timeout (vty, argv[idx_number]->arg, NULL); } DEFUN (exec_timeout_sec, exec_timeout_sec_cmd, - "exec-timeout <0-35791> <0-2147483>", + "exec-timeout (0-35791) (0-2147483)", "Set the EXEC timeout\n" "Timeout in minutes\n" "Timeout in seconds\n") { - return exec_timeout (vty, argv[0], argv[1]); + int idx_number = 1; + int idx_number_2 = 2; + return exec_timeout (vty, argv[idx_number]->arg, argv[idx_number_2]->arg); } DEFUN (no_exec_timeout, @@ -2830,10 +2749,11 @@ DEFUN (vty_access_class, "Filter connections based on an IP access list\n" "IP access list\n") { + int idx_word = 1; if (vty_accesslist_name) XFREE(MTYPE_VTY, vty_accesslist_name); - vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]); + vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg); return CMD_SUCCESS; } @@ -2846,10 +2766,12 @@ DEFUN (no_vty_access_class, "Filter connections based on an IP access list\n" "IP access list\n") { - if (! vty_accesslist_name || (argc && strcmp(vty_accesslist_name, argv[0]))) + int idx_word = 2; + const char *accesslist = (argc == 3) ? argv[idx_word]->arg : NULL; + if (! vty_accesslist_name || (argc == 3 && strcmp(vty_accesslist_name, accesslist))) { vty_out (vty, "Access-class is not currently applied to vty%s", - VTY_NEWLINE); + VTY_NEWLINE); return CMD_WARNING; } @@ -2860,7 +2782,6 @@ DEFUN (no_vty_access_class, return CMD_SUCCESS; } -#ifdef HAVE_IPV6 /* Set vty access class. */ DEFUN (vty_ipv6_access_class, vty_ipv6_access_class_cmd, @@ -2869,10 +2790,11 @@ DEFUN (vty_ipv6_access_class, "Filter connections based on an IP access list\n" "IPv6 access list\n") { + int idx_word = 2; if (vty_ipv6_accesslist_name) XFREE(MTYPE_VTY, vty_ipv6_accesslist_name); - vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[0]); + vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg); return CMD_SUCCESS; } @@ -2886,11 +2808,14 @@ DEFUN (no_vty_ipv6_access_class, "Filter connections based on an IP access list\n" "IPv6 access list\n") { + int idx_word = 3; + const char *accesslist = (argc == 4) ? argv[idx_word]->arg : NULL; + if (! vty_ipv6_accesslist_name || - (argc && strcmp(vty_ipv6_accesslist_name, argv[0]))) + (argc == 4 && strcmp(vty_ipv6_accesslist_name, accesslist))) { vty_out (vty, "IPv6 access-class is not currently applied to vty%s", - VTY_NEWLINE); + VTY_NEWLINE); return CMD_WARNING; } @@ -2900,7 +2825,6 @@ DEFUN (no_vty_ipv6_access_class, return CMD_SUCCESS; } -#endif /* HAVE_IPV6 */ /* vty login. */ DEFUN (vty_login, @@ -2964,12 +2888,16 @@ DEFUN (terminal_no_monitor, return CMD_SUCCESS; } -ALIAS (terminal_no_monitor, +DEFUN (no_terminal_monitor, no_terminal_monitor_cmd, "no terminal monitor", NO_STR "Set terminal line parameters\n" "Copy debug output to the current terminal line\n") +{ + return terminal_no_monitor (self, vty, argc, argv); +} + DEFUN (show_history, show_history_cmd, @@ -2982,13 +2910,13 @@ DEFUN (show_history, for (index = vty->hindex + 1; index != vty->hindex;) { if (index == VTY_MAXHIST) - { - index = 0; - continue; - } + { + index = 0; + continue; + } if (vty->hist[index] != NULL) - vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE); + vty_out (vty, " %s%s", vty->hist[index], VTY_NEWLINE); index++; } @@ -3015,17 +2943,17 @@ vty_config_write (struct vty *vty) if (vty_accesslist_name) vty_out (vty, " access-class %s%s", - vty_accesslist_name, VTY_NEWLINE); + vty_accesslist_name, VTY_NEWLINE); if (vty_ipv6_accesslist_name) vty_out (vty, " ipv6 access-class %s%s", - vty_ipv6_accesslist_name, VTY_NEWLINE); + vty_ipv6_accesslist_name, VTY_NEWLINE); /* exec-timeout */ if (vty_timeout_val != VTY_TIMEOUT_DEFAULT) - vty_out (vty, " exec-timeout %ld %ld%s", - vty_timeout_val / 60, - vty_timeout_val % 60, VTY_NEWLINE); + vty_out (vty, " exec-timeout %ld %ld%s", + vty_timeout_val / 60, + vty_timeout_val % 60, VTY_NEWLINE); /* login */ if (no_password_check) @@ -3033,7 +2961,7 @@ vty_config_write (struct vty *vty) if (do_log_commands) vty_out (vty, "log commands%s", VTY_NEWLINE); - + vty_out (vty, "!%s", VTY_NEWLINE); return CMD_SUCCESS; @@ -3057,16 +2985,16 @@ vty_reset () for (i = 0; i < vector_active (vtyvec); i++) if ((vty = vector_slot (vtyvec, i)) != NULL) { - buffer_reset (vty->obuf); - vty->status = VTY_CLOSE; - vty_close (vty); + buffer_reset (vty->obuf); + vty->status = VTY_CLOSE; + vty_close (vty); } for (i = 0; i < vector_active (Vvty_serv_thread); i++) if ((vty_serv_thread = vector_slot (Vvty_serv_thread, i)) != NULL) { - thread_cancel (vty_serv_thread); - vector_slot (Vvty_serv_thread, i) = NULL; + thread_cancel (vty_serv_thread); + vector_slot (Vvty_serv_thread, i) = NULL; close (i); } @@ -3101,15 +3029,15 @@ vty_save_cwd (void) * Hence not worrying about it too much. */ if (!chdir (SYSCONFDIR)) - { - fprintf(stderr, "Failure to chdir to %s, errno: %d\n", SYSCONFDIR, errno); - exit(-1); - } + { + fprintf(stderr, "Failure to chdir to %s, errno: %d\n", SYSCONFDIR, errno); + exit(-1); + } if (getcwd (cwd, MAXPATHLEN) == NULL) - { - fprintf(stderr, "Failure to getcwd, errno: %d\n", errno); - exit(-1); - } + { + fprintf(stderr, "Failure to getcwd, errno: %d\n", errno); + exit(-1); + } } vty_cwd = XMALLOC (MTYPE_TMP, strlen (cwd) + 1); @@ -3178,10 +3106,8 @@ vty_init (struct thread_master *master_thread) install_element (VTY_NODE, &no_vty_access_class_cmd); install_element (VTY_NODE, &vty_login_cmd); install_element (VTY_NODE, &no_vty_login_cmd); -#ifdef HAVE_IPV6 install_element (VTY_NODE, &vty_ipv6_access_class_cmd); install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd); -#endif /* HAVE_IPV6 */ } void @@ -29,14 +29,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define VTY_BUFSIZ 4096 #define VTY_MAXHIST 20 -#if defined(VTY_DEPRECATE_INDEX) && defined(__GNUC__) && \ - (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && \ - !defined(__ICC) -#define INDEX_WARNING __attribute__((deprecated)) -#else -#define INDEX_WARNING -#endif - /* VTY struct. */ struct vty { @@ -82,10 +74,6 @@ struct vty /* History insert end point */ int hindex; - /* For current referencing point of interface, route-map, - access-list etc... */ - void *index INDEX_WARNING; - /* qobj object ID (replacement for "index") */ uint64_t qobj_index; @@ -139,32 +127,23 @@ struct vty char address[SU_ADDRSTRLEN]; }; -#undef INDEX_WARNING - static inline void vty_push_context(struct vty *vty, - int node, uint64_t id, void *idx) + int node, uint64_t id) { vty->node = node; vty->qobj_index = id; -#if defined(VTY_DEPRECATE_INDEX) && defined(__GNUC__) && \ - (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - vty->index = idx; -#pragma GCC diagnostic pop -#else - vty->index = idx; -#endif } +/* note: VTY_PUSH_CONTEXT(..., NULL) doesn't work, since it will try to + * dereference "NULL->qobj_node.nid" */ #define VTY_PUSH_CONTEXT(nodeval, ptr) \ - vty_push_context(vty, nodeval, QOBJ_ID(ptr), NULL) -#define VTY_PUSH_CONTEXT_COMPAT(nodeval, ptr) \ - vty_push_context(vty, nodeval, QOBJ_ID(ptr), ptr) + vty_push_context(vty, nodeval, QOBJ_ID_0SAFE(ptr)) +#define VTY_PUSH_CONTEXT_NULL(nodeval) \ + vty_push_context(vty, nodeval, 0ULL) #define VTY_PUSH_CONTEXT_SUB(nodeval, ptr) do { \ vty->node = nodeval; \ /* qobj_index stays untouched */ \ - vty->qobj_index_sub = QOBJ_ID(ptr); \ + vty->qobj_index_sub = QOBJ_ID_0SAFE(ptr); \ } while (0) /* can return NULL if context is invalid! */ @@ -345,7 +324,7 @@ extern void vty_reset (void); extern struct vty *vty_new (void); extern struct vty *vty_stdio (void (*atclose)(void)); extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); -extern void vty_read_config (char *, char *); +extern void vty_read_config (const char *, char *); extern void vty_time_print (struct vty *, int); extern void vty_serv_sock (const char *, unsigned short, const char *); extern void vty_close (struct vty *); diff --git a/lib/wheel.c b/lib/wheel.c new file mode 100644 index 0000000000..fe53dea299 --- /dev/null +++ b/lib/wheel.c @@ -0,0 +1,164 @@ +/* + * Timer Wheel + * Copyright (C) 2016 Cumulus Networks, Inc. + * Donald Sharp + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#include "zebra.h" +#include "linklist.h" +#include "thread.h" +#include "memory.h" +#include "wheel.h" +#include "log.h" + +DEFINE_MTYPE_STATIC(LIB, TIMER_WHEEL, "Timer Wheel") +DEFINE_MTYPE_STATIC(LIB, TIMER_WHEEL_LIST, "Timer Wheel Slot List") + +static int debug_timer_wheel = 0; + +static int +wheel_timer_thread (struct thread *t) +{ + struct listnode *node, *nextnode; + unsigned long long curr_slot; + unsigned int slots_to_skip = 1; + struct timer_wheel *wheel; + void *data; + + wheel = THREAD_ARG(t); + THREAD_OFF(wheel->timer); + + wheel->curr_slot += wheel->slots_to_skip; + + curr_slot = wheel->curr_slot % wheel->slots; + + if (debug_timer_wheel) + zlog_debug ("%s: Wheel Slot: %lld(%lld) count: %d", + __PRETTY_FUNCTION__, + wheel->curr_slot, + curr_slot, listcount(wheel->wheel_slot_lists[curr_slot])); + + for (ALL_LIST_ELEMENTS (wheel->wheel_slot_lists[curr_slot], node, nextnode, data)) + (*wheel->slot_run)(data); + + while (list_isempty(wheel->wheel_slot_lists[(curr_slot + slots_to_skip) % wheel->slots]) && + (curr_slot + slots_to_skip ) % wheel->slots != curr_slot) + slots_to_skip++; + + wheel->slots_to_skip = slots_to_skip; + THREAD_TIMER_MSEC_ON (wheel->master, wheel->timer, + wheel_timer_thread, wheel, + wheel->nexttime * slots_to_skip); + + return 0; +} + +struct timer_wheel * +wheel_init (struct thread_master *master, int period, size_t slots, + unsigned int (*slot_key) (void *), + void (*slot_run) (void *)) +{ + struct timer_wheel *wheel; + size_t i; + + wheel = XCALLOC(MTYPE_TIMER_WHEEL, sizeof (struct timer_wheel)); + + wheel->slot_key = slot_key; + wheel->slot_run = slot_run; + + wheel->period = period; + wheel->slots = slots; + wheel->curr_slot = 0; + wheel->master = master; + wheel->nexttime = period / slots; + + wheel->wheel_slot_lists = XCALLOC(MTYPE_TIMER_WHEEL_LIST, + slots * sizeof (struct listnode *)); + for (i = 0; i < slots; i++) + wheel->wheel_slot_lists[i] = list_new (); + + THREAD_TIMER_MSEC_ON (wheel->master, wheel->timer, + wheel_timer_thread, wheel, + wheel->nexttime); + + return wheel; +} + +void +wheel_delete (struct timer_wheel *wheel) +{ + int i; + + for (i = 0; i < wheel->slots; i++) + { + list_delete(wheel->wheel_slot_lists[i]); + } + + THREAD_OFF(wheel->timer); + XFREE(MTYPE_TIMER_WHEEL_LIST, wheel->wheel_slot_lists); + XFREE(MTYPE_TIMER_WHEEL, wheel); +} + +int +wheel_stop (struct timer_wheel *wheel) +{ + THREAD_OFF(wheel->timer); + return 0; +} + +int +wheel_start (struct timer_wheel *wheel) +{ + if (!wheel->timer) + THREAD_TIMER_MSEC_ON (wheel->master, wheel->timer, + wheel_timer_thread, wheel, + wheel->nexttime); + + return 0; +} + +int +wheel_add_item (struct timer_wheel *wheel, void *item) +{ + long long slot; + + slot = (*wheel->slot_key)(item); + + if (debug_timer_wheel) + zlog_debug ("%s: Inserting %p: %lld %lld", + __PRETTY_FUNCTION__, item, + slot, slot % wheel->slots); + listnode_add (wheel->wheel_slot_lists[slot % wheel->slots], item); + + return 0; +} + +int +wheel_remove_item (struct timer_wheel *wheel, void *item) +{ + long long slot; + + slot = (*wheel->slot_key)(item); + + if (debug_timer_wheel) + zlog_debug ("%s: Removing %p: %lld %lld", + __PRETTY_FUNCTION__, item, + slot, slot % wheel->slots); + listnode_delete (wheel->wheel_slot_lists[slot % wheel->slots], item); + + return 0; +} diff --git a/lib/wheel.h b/lib/wheel.h new file mode 100644 index 0000000000..79d21e124b --- /dev/null +++ b/lib/wheel.h @@ -0,0 +1,118 @@ +/* + * Timer Wheel + * Copyright (C) 2016 Cumulus Networks, Inc. + * Donald Sharp + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +#ifndef __WHEEL_H__ +#define __WHEEL_H__ + +struct timer_wheel +{ + struct thread_master *master; + int slots; + long long curr_slot; + unsigned int period; + unsigned int nexttime; + unsigned int slots_to_skip; + + struct list **wheel_slot_lists; + struct thread *timer; + /* + * Key to determine what slot the item belongs in + */ + unsigned int (*slot_key) (void *); + + void (*slot_run) (void *); +}; + +/* + * Creates a timer wheel + * + * master - Thread master structure for the process + * period - The Time in seconds that the timer wheel will + * take before it starts issuing commands again + * for items in each slot + * slots - The number of slots to have in this particular + * timer wheel + * slot_key - A hashing function of some sort that will allow + * the timer wheel to put items into individual slots + * slot_run - The function to run over each item in a particular slot + * + * Creates a timer wheel that will wake up 'slots' times over the entire + * wheel. Each time the timer wheel wakes up it will iterate through + * and run the slot_run function for each item stored in that particular + * slot. + * + * The timer code is 'intelligent' in that it notices if anything is + * in a particular slot and can schedule the next timer to skip + * the empty slot. + * + * The general purpose of a timer wheel is to reduce events in a system. + * A perfect example of usage for this is say hello packets that need + * to be sent out to all your neighbors. Suppose a large routing protocol + * has to send keepalive packets every Y seconds to each of it's peers. + * At scale we can have a very large number of peers, X. + * This means that we will have X timing events every Y seconds. + * If you replace these events with a timer wheel that has Z slots + * you will have at most Y/Z timer events if each slot has a work item + * in it. + * + * When X is large the number of events in a system can quickly escalate + * and cause significant amount of time handling thread events instead + * of running your code. + */ +struct timer_wheel *wheel_init (struct thread_master *master, int period, size_t slots, + unsigned int (*slot_key) (void *), + void (*slot_run) (void *)); + +/* + * Delete the specified timer wheel created + */ +void wheel_delete (struct timer_wheel *); + +/* + * Pause the Wheel from running + */ +int wheel_stop (struct timer_wheel *wheel); + +/* + * Start the wheel running again + */ +int wheel_start (struct timer_wheel *wheel); + +/* + * wheel - The Timer wheel being modified + * item - The generic data structure that will be handed + * to the slot_run function. + * + * Add item to a slot setup by the slot_key, + * possibly change next time pop. + */ +int wheel_add_item (struct timer_wheel *wheel, void *item); + +/* + * wheel - The Timer wheel being modified. + * item - The item to remove from one of the slots in + * the timer wheel. + * + * Remove a item to a slot setup by the slot_key, + * possibly change next time pop. + */ +int wheel_remove_item (struct timer_wheel *wheel, void *item); + +#endif diff --git a/lib/workqueue.c b/lib/workqueue.c index 549bb2360a..51017b34ea 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -222,6 +222,12 @@ DEFUN (show_work_queues, return CMD_SUCCESS; } +void +workqueue_cmd_init (void) +{ + install_element (VIEW_NODE, &show_work_queues_cmd); +} + /* 'plug' a queue: Stop it from being scheduled, * ie: prevent the queue from draining. */ diff --git a/lib/workqueue.h b/lib/workqueue.h index eaf8574907..548f96d8b0 100644 --- a/lib/workqueue.h +++ b/lib/workqueue.h @@ -129,5 +129,7 @@ bool work_queue_is_scheduled (struct work_queue *); /* Helpers, exported for thread.c and command.c */ extern int work_queue_run (struct thread *); -extern struct cmd_element show_work_queues_cmd; + +extern void workqueue_cmd_init (void); + #endif /* _QUAGGA_WORK_QUEUE_H */ diff --git a/lib/zclient.c b/lib/zclient.c index 440de3635f..859751deb8 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -35,6 +35,7 @@ #include "nexthop.h" DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient") +DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs") /* Zebra client events. */ enum event {ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT}; @@ -106,7 +107,7 @@ redist_add_instance (struct redist_proto *red, u_short instance) if (!red->instances) red->instances = list_new(); - in = calloc (1, sizeof(u_short)); + in = XMALLOC (MTYPE_REDIST_INST, sizeof(u_short)); *in = instance; listnode_add (red->instances, in); } @@ -121,7 +122,7 @@ redist_del_instance (struct redist_proto *red, u_short instance) return; listnode_delete(red->instances, id); - free (id); + XFREE (MTYPE_REDIST_INST, id); if (!red->instances->count) { red->enabled = 0; @@ -786,7 +787,6 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, return zclient_send_message(zclient); } -#ifdef HAVE_IPV6 int zapi_ipv4_route_ipv6_nexthop (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, struct zapi_ipv6 *api) @@ -855,12 +855,15 @@ zapi_ipv4_route_ipv6_nexthop (u_char cmd, struct zclient *zclient, int zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, - struct zapi_ipv6 *api) + struct prefix_ipv6 *src_p, struct zapi_ipv6 *api) { int i; int psize; struct stream *s; + /* either we have !SRCPFX && src_p == NULL, or SRCPFX && src_p != NULL */ + assert (!(api->message & ZAPI_MESSAGE_SRCPFX) == !src_p); + /* Reset stream. */ s = zclient->obuf; stream_reset (s); @@ -879,6 +882,13 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, stream_putc (s, p->prefixlen); stream_write (s, (u_char *)&p->prefix, psize); + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_SRCPFX)) + { + psize = PSIZE (src_p->prefixlen); + stream_putc (s, src_p->prefixlen); + stream_write (s, (u_char *)&src_p->prefix, psize); + } + /* Nexthop, ifindex, distance and metric information. */ if (CHECK_FLAG (api->message, ZAPI_MESSAGE_NEXTHOP)) { @@ -918,7 +928,6 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, return zclient_send_message(zclient); } -#endif /* HAVE_IPV6 */ /* * send a ZEBRA_REDISTRIBUTE_ADD or ZEBRA_REDISTRIBUTE_DELETE @@ -1138,11 +1147,15 @@ struct interface * zebra_interface_link_params_read (struct stream *s) { struct if_link_params *iflp; - uint32_t ifindex = stream_getl (s); + ifindex_t ifindex; + + assert (s); + + ifindex = stream_getl (s); struct interface *ifp = if_lookup_by_index (ifindex); - if (ifp == NULL || s == NULL) + if (ifp == NULL) { zlog_err ("%s: unknown ifindex %u, shouldn't happen", __func__, ifindex); @@ -1628,6 +1641,7 @@ zclient_read (struct thread *thread) case ZEBRA_REDISTRIBUTE_IPV6_DEL: if (zclient->redistribute_route_ipv6_del) (*zclient->redistribute_route_ipv6_del) (command, zclient, length, vrf_id); + break; case ZEBRA_INTERFACE_LINK_PARAMS: if (zclient->interface_link_params) (*zclient->interface_link_params) (command, zclient, length); diff --git a/lib/zclient.h b/lib/zclient.h index 4312cdc83c..89fc865c78 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -37,6 +37,62 @@ /* Zebra header size. */ #define ZEBRA_HEADER_SIZE 8 +/* Zebra message types. */ +typedef enum { + ZEBRA_INTERFACE_ADD, + ZEBRA_INTERFACE_DELETE, + ZEBRA_INTERFACE_ADDRESS_ADD, + ZEBRA_INTERFACE_ADDRESS_DELETE, + ZEBRA_INTERFACE_UP, + ZEBRA_INTERFACE_DOWN, + ZEBRA_IPV4_ROUTE_ADD, + ZEBRA_IPV4_ROUTE_DELETE, + ZEBRA_IPV6_ROUTE_ADD, + ZEBRA_IPV6_ROUTE_DELETE, + ZEBRA_REDISTRIBUTE_ADD, + ZEBRA_REDISTRIBUTE_DELETE, + ZEBRA_REDISTRIBUTE_DEFAULT_ADD, + ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, + ZEBRA_ROUTER_ID_ADD, + ZEBRA_ROUTER_ID_DELETE, + ZEBRA_ROUTER_ID_UPDATE, + ZEBRA_HELLO, + ZEBRA_NEXTHOP_REGISTER, + ZEBRA_NEXTHOP_UNREGISTER, + ZEBRA_NEXTHOP_UPDATE, + ZEBRA_INTERFACE_NBR_ADDRESS_ADD, + ZEBRA_INTERFACE_NBR_ADDRESS_DELETE, + ZEBRA_INTERFACE_BFD_DEST_UPDATE, + ZEBRA_IMPORT_ROUTE_REGISTER, + ZEBRA_IMPORT_ROUTE_UNREGISTER, + ZEBRA_IMPORT_CHECK_UPDATE, + ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD, + ZEBRA_BFD_DEST_REGISTER, + ZEBRA_BFD_DEST_DEREGISTER, + ZEBRA_BFD_DEST_UPDATE, + ZEBRA_BFD_DEST_REPLAY, + ZEBRA_REDISTRIBUTE_IPV4_ADD, + ZEBRA_REDISTRIBUTE_IPV4_DEL, + ZEBRA_REDISTRIBUTE_IPV6_ADD, + ZEBRA_REDISTRIBUTE_IPV6_DEL, + ZEBRA_VRF_UNREGISTER, + ZEBRA_VRF_ADD, + ZEBRA_VRF_DELETE, + ZEBRA_INTERFACE_VRF_UPDATE, + ZEBRA_BFD_CLIENT_REGISTER, + ZEBRA_INTERFACE_ENABLE_RADV, + ZEBRA_INTERFACE_DISABLE_RADV, + ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, + ZEBRA_INTERFACE_LINK_PARAMS, + ZEBRA_MPLS_LABELS_ADD, + ZEBRA_MPLS_LABELS_DELETE, + ZEBRA_IPV4_NEXTHOP_ADD, + ZEBRA_IPV4_NEXTHOP_DELETE, + ZEBRA_IPV6_NEXTHOP_ADD, + ZEBRA_IPV6_NEXTHOP_DELETE, + ZEBRA_IPMR_ROUTE_STATS, +} zebra_message_types_t; + struct redist_proto { u_char enabled; @@ -114,6 +170,7 @@ struct zclient #define ZAPI_MESSAGE_METRIC 0x08 #define ZAPI_MESSAGE_TAG 0x10 #define ZAPI_MESSAGE_MTU 0x20 +#define ZAPI_MESSAGE_SRCPFX 0x40 /* Zserv protocol message header */ struct zserv_header @@ -214,7 +271,6 @@ extern int zapi_ipv4_route (u_char, struct zclient *, struct prefix_ipv4 *, extern struct interface *zebra_interface_link_params_read (struct stream *); extern size_t zebra_interface_link_params_write (struct stream *, struct interface *); -#ifdef HAVE_IPV6 /* IPv6 prefix add and delete function prototype. */ struct zapi_ipv6 @@ -246,9 +302,9 @@ struct zapi_ipv6 }; extern int zapi_ipv6_route (u_char cmd, struct zclient *zclient, - struct prefix_ipv6 *p, struct zapi_ipv6 *api); + struct prefix_ipv6 *p, struct prefix_ipv6 *src_p, + struct zapi_ipv6 *api); extern int zapi_ipv4_route_ipv6_nexthop (u_char, struct zclient *, struct prefix_ipv4 *, struct zapi_ipv6 *); -#endif /* HAVE_IPV6 */ #endif /* _ZEBRA_ZCLIENT_H */ diff --git a/lib/zebra.h b/lib/zebra.h index 420f237176..760264d752 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -352,61 +352,6 @@ struct in_pktinfo /* default zebra TCP port for zclient */ #define ZEBRA_PORT 2600 -/* Zebra message types. */ -typedef enum { - ZEBRA_INTERFACE_ADD, - ZEBRA_INTERFACE_DELETE, - ZEBRA_INTERFACE_ADDRESS_ADD, - ZEBRA_INTERFACE_ADDRESS_DELETE, - ZEBRA_INTERFACE_UP, - ZEBRA_INTERFACE_DOWN, - ZEBRA_IPV4_ROUTE_ADD, - ZEBRA_IPV4_ROUTE_DELETE, - ZEBRA_IPV6_ROUTE_ADD, - ZEBRA_IPV6_ROUTE_DELETE, - ZEBRA_REDISTRIBUTE_ADD, - ZEBRA_REDISTRIBUTE_DELETE, - ZEBRA_REDISTRIBUTE_DEFAULT_ADD, - ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, - ZEBRA_ROUTER_ID_ADD, - ZEBRA_ROUTER_ID_DELETE, - ZEBRA_ROUTER_ID_UPDATE, - ZEBRA_HELLO, - ZEBRA_NEXTHOP_REGISTER, - ZEBRA_NEXTHOP_UNREGISTER, - ZEBRA_NEXTHOP_UPDATE, - ZEBRA_INTERFACE_NBR_ADDRESS_ADD, - ZEBRA_INTERFACE_NBR_ADDRESS_DELETE, - ZEBRA_INTERFACE_BFD_DEST_UPDATE, - ZEBRA_IMPORT_ROUTE_REGISTER, - ZEBRA_IMPORT_ROUTE_UNREGISTER, - ZEBRA_IMPORT_CHECK_UPDATE, - ZEBRA_IPV4_ROUTE_IPV6_NEXTHOP_ADD, - ZEBRA_BFD_DEST_REGISTER, - ZEBRA_BFD_DEST_DEREGISTER, - ZEBRA_BFD_DEST_UPDATE, - ZEBRA_BFD_DEST_REPLAY, - ZEBRA_REDISTRIBUTE_IPV4_ADD, - ZEBRA_REDISTRIBUTE_IPV4_DEL, - ZEBRA_REDISTRIBUTE_IPV6_ADD, - ZEBRA_REDISTRIBUTE_IPV6_DEL, - ZEBRA_VRF_UNREGISTER, - ZEBRA_VRF_ADD, - ZEBRA_VRF_DELETE, - ZEBRA_INTERFACE_VRF_UPDATE, - ZEBRA_BFD_CLIENT_REGISTER, - ZEBRA_INTERFACE_ENABLE_RADV, - ZEBRA_INTERFACE_DISABLE_RADV, - ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB, - ZEBRA_INTERFACE_LINK_PARAMS, - ZEBRA_MPLS_LABELS_ADD, - ZEBRA_MPLS_LABELS_DELETE, - ZEBRA_IPV4_NEXTHOP_ADD, - ZEBRA_IPV4_NEXTHOP_DELETE, - ZEBRA_IPV6_NEXTHOP_ADD, - ZEBRA_IPV6_NEXTHOP_DELETE, -} zebra_message_types_t; - /* Marker value used in new Zserv, in the byte location corresponding * the command value in the old zserv header. To allow old and new * Zserv headers to be distinguished from each other. @@ -435,13 +380,7 @@ extern int proto_redistnum(int afi, const char *s); extern const char *zserv_command_string (unsigned int command); -/* Error codes of zebra. */ -#define ZEBRA_ERR_NOERROR 0 -#define ZEBRA_ERR_RTEXIST -1 -#define ZEBRA_ERR_RTUNREACH -2 -#define ZEBRA_ERR_EPERM -3 -#define ZEBRA_ERR_RTNOEXIST -4 -#define ZEBRA_ERR_KERNEL -5 +#define strmatch(a,b) (!strcmp((a), (b))) /* Zebra message flags */ #define ZEBRA_FLAG_INTERNAL 0x01 @@ -462,17 +401,50 @@ extern const char *zserv_command_string (unsigned int command); typedef enum { AFI_IP = 1, AFI_IP6 = 2, - AFI_ETHER = 3, /* RFC 1700 has "6" for 802.* */ - AFI_MAX = 4 + AFI_L2VPN = 4, + AFI_MAX = 5 } afi_t; /* Subsequent Address Family Identifier. */ #define SAFI_UNICAST 1 #define SAFI_MULTICAST 2 -#define SAFI_RESERVED_3 3 -#define SAFI_MPLS_VPN 4 -#define SAFI_ENCAP 7 /* per IANA */ -#define SAFI_MAX 8 +#define SAFI_MPLS_VPN 3 +#define SAFI_RESERVED_4 4 +#define SAFI_ENCAP 5 +#define SAFI_RESERVED_5 5 +#define SAFI_EVPN 6 +#define SAFI_MAX 7 + +#define IANA_SAFI_RESERVED 0 +#define IANA_SAFI_UNICAST 1 +#define IANA_SAFI_MULTICAST 2 +#define IANA_SAFI_ENCAP 7 +#define IANA_SAFI_MPLS_VPN 128 + +/* + * The above AFI and SAFI definitions are for internal use. The protocol + * definitions (IANA values) as for example used in BGP protocol packets + * are defined below and these will get mapped to/from the internal values + * in the appropriate places. + * The rationale is that the protocol (IANA) values may be sparse and are + * not optimal for use in data-structure sizing. + * Note: Only useful (i.e., supported) values are defined below. + */ +typedef enum { + IANA_AFI_RESERVED = 0, + IANA_AFI_IPV4 = 1, + IANA_AFI_IPV6 = 2, + IANA_AFI_L2VPN = 25, + IANA_AFI_IPMR = 128, + IANA_AFI_IP6MR = 129 +} iana_afi_t; + +#define IANA_SAFI_RESERVED 0 +#define IANA_SAFI_UNICAST 1 +#define IANA_SAFI_MULTICAST 2 +#define IANA_SAFI_ENCAP 7 +#define IANA_SAFI_EVPN 70 +#define IANA_SAFI_MPLS_VPN 128 /* Default Administrative Distance of each protocol. */ #define ZEBRA_KERNEL_DISTANCE_DEFAULT 0 @@ -506,4 +478,56 @@ typedef uint32_t route_tag_t; #define ROUTE_TAG_MAX UINT32_MAX #define ROUTE_TAG_PRI PRIu32 +static inline afi_t afi_iana2int (iana_afi_t afi) +{ + if (afi == IANA_AFI_IPV4) + return AFI_IP; + if (afi == IANA_AFI_IPV6) + return AFI_IP6; + if (afi == IANA_AFI_L2VPN) + return AFI_L2VPN; + return AFI_MAX; +} + +static inline iana_afi_t afi_int2iana (afi_t afi) +{ + if (afi == AFI_IP) + return IANA_AFI_IPV4; + if (afi == AFI_IP6) + return IANA_AFI_IPV6; + if (afi == AFI_L2VPN) + return IANA_AFI_L2VPN; + return IANA_AFI_RESERVED; +} + +static inline safi_t safi_iana2int (safi_t safi) +{ + if (safi == IANA_SAFI_UNICAST) + return SAFI_UNICAST; + if (safi == IANA_SAFI_MULTICAST) + return SAFI_MULTICAST; + if (safi == IANA_SAFI_MPLS_VPN) + return SAFI_MPLS_VPN; + if (safi == IANA_SAFI_ENCAP) + return SAFI_ENCAP; + if (safi == IANA_SAFI_EVPN) + return SAFI_EVPN; + return SAFI_MAX; +} + +static inline safi_t safi_int2iana (safi_t safi) +{ + if (safi == SAFI_UNICAST) + return IANA_SAFI_UNICAST; + if (safi == SAFI_MULTICAST) + return IANA_SAFI_MULTICAST; + if (safi == SAFI_MPLS_VPN) + return IANA_SAFI_MPLS_VPN; + if (safi == SAFI_ENCAP) + return IANA_SAFI_ENCAP; + if (safi == SAFI_EVPN) + return IANA_SAFI_EVPN; + return IANA_SAFI_RESERVED; +} + #endif /* _ZEBRA_H */ |
