diff options
| -rw-r--r-- | bgpd/bgp_aspath.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_attr.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_bfd.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_encap_tlv.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_fsm.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_main.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_route.c | 112 | ||||
| -rw-r--r-- | bgpd/bgp_routemap.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_table.c | 1 | ||||
| -rw-r--r-- | bgpd/bgp_vty.c | 50 | ||||
| -rw-r--r-- | bgpd/bgp_vty.h | 4 | ||||
| -rw-r--r-- | bgpd/bgpd.h | 1 | ||||
| -rw-r--r-- | doc/cli.md | 356 | ||||
| -rw-r--r-- | lib/plist.c | 1 | ||||
| -rw-r--r-- | lib/plist.h | 5 | ||||
| -rw-r--r-- | pimd/pim_cmd.c | 156 | ||||
| -rw-r--r-- | pimd/pim_iface.c | 32 | ||||
| -rw-r--r-- | pimd/pim_mroute.c | 30 | ||||
| -rw-r--r-- | pimd/pim_vty.c | 5 | ||||
| -rw-r--r-- | pimd/pimd.c | 2 | ||||
| -rw-r--r-- | pimd/pimd.h | 3 | ||||
| -rwxr-xr-x | tools/frr-reload.py | 2 |
22 files changed, 479 insertions, 288 deletions
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 2e78c9a3bf..006631c1f0 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -24,7 +24,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "hash.h" #include "memory.h" #include "vector.h" -#include "vty.h" #include "log.h" #include "stream.h" #include "command.h" diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 153187fd43..1fccd25c8a 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -24,7 +24,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "prefix.h" #include "memory.h" #include "vector.h" -#include "vty.h" #include "stream.h" #include "log.h" #include "hash.h" diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index b8158dc31e..0a06a57fe5 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -31,7 +31,6 @@ #include "buffer.h" #include "stream.h" #include "zclient.h" -#include "vty.h" #include "bfd.h" #include "lib/json.h" #include "filter.h" diff --git a/bgpd/bgp_encap_tlv.c b/bgpd/bgp_encap_tlv.c index 8c5ab8d6f6..7acd23b9ae 100644 --- a/bgpd/bgp_encap_tlv.c +++ b/bgpd/bgp_encap_tlv.c @@ -22,7 +22,6 @@ #include "command.h" #include "memory.h" #include "prefix.h" -#include "vty.h" #include "filter.h" #include "bgpd.h" diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index a71364381e..7dc7f053d6 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -23,7 +23,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "linklist.h" #include "prefix.h" -#include "vty.h" #include "sockunion.h" #include "thread.h" #include "log.h" diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index fbbcda5915..2598d66656 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -21,7 +21,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include <zebra.h> #include "vector.h" -#include "vty.h" #include "command.h" #include "getopt.h" #include "thread.h" diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 55823a272f..ced587e738 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -5267,7 +5267,7 @@ bgp_aggregate_increment (struct bgp *bgp, struct prefix *p, struct bgp_table *table; /* MPLS-VPN aggregation is not yet supported. */ - if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi = SAFI_EVPN)) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN)) return; table = bgp->aggregate[afi][safi]; @@ -5304,7 +5304,7 @@ bgp_aggregate_decrement (struct bgp *bgp, struct prefix *p, struct bgp_table *table; /* MPLS-VPN aggregation is not yet supported. */ - if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi = SAFI_EVPN)) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN)) return; table = bgp->aggregate[afi][safi]; @@ -6097,7 +6097,7 @@ route_vty_out (struct vty *vty, struct prefix *p, * neccessarily the same as the prefix address family. * Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field */ - if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN) || (safi = SAFI_EVPN)) + if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) { if (attr->extra) { @@ -6129,7 +6129,7 @@ route_vty_out (struct vty *vty, struct prefix *p, { json_nexthop_global = json_object_new_object(); - if ((safi == SAFI_MPLS_VPN) || (safi = SAFI_EVPN)) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->extra->mp_nexthop_global_in)); else json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->nexthop)); @@ -6139,7 +6139,7 @@ route_vty_out (struct vty *vty, struct prefix *p, } else { - if ((safi == SAFI_MPLS_VPN) || (safi = SAFI_EVPN)) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) vty_out (vty, "%-16s", inet_ntoa (attr->extra->mp_nexthop_global_in)); else @@ -8358,7 +8358,6 @@ DEFUN (show_ip_bgp, "Display route and more specific routes\n" JSON_STR) { - vrf_id_t vrf = VRF_DEFAULT; afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; int exact_match = 0; @@ -8366,23 +8365,13 @@ DEFUN (show_ip_bgp, struct bgp *bgp = NULL; int idx = 0; - bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp); if (!idx) return CMD_WARNING; int uj = use_json (argc, argv); if (uj) argc--; - bgp = bgp_lookup_by_vrf_id (vrf); - if (bgp == NULL) - { - if (vrf == VRF_DEFAULT) - vty_out (vty, "Can't find BGP instance (default)%s", VTY_NEWLINE); - else - vty_out (vty, "Can't find BGP instance %d%s", vrf, VTY_NEWLINE); - return CMD_WARNING; - } - if (argv_find(argv, argc, "cidr-only", &idx)) return bgp_show (vty, bgp, afi, safi, bgp_show_type_cidr_only, NULL, uj); @@ -8465,7 +8454,6 @@ DEFUN (show_ip_bgp_route, afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; - vrf_id_t vrf = VRF_DEFAULT;; char *prefix = NULL; struct bgp *bgp = NULL; enum bgp_path_type path_type; @@ -8473,20 +8461,11 @@ DEFUN (show_ip_bgp_route, int idx = 0; - bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp); if (!idx) return CMD_WARNING; - if (vrf != VRF_ALL) - { - bgp = bgp_lookup_by_vrf_id (vrf); - if (bgp == NULL) - { - vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE); - return CMD_WARNING; - } - } - else + if (!bgp) { vty_out (vty, "Specified 'all' vrf's but this command currently only works per view/vrf%s", VTY_NEWLINE); return CMD_WARNING; @@ -8534,12 +8513,12 @@ DEFUN (show_ip_bgp_regexp, "Display routes matching the AS path regular expression\n" "A regular-expression to match the BGP AS paths\n") { - vrf_id_t vrf = VRF_DEFAULT; afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; + struct bgp *bgp = NULL; int idx = 0; - bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp); if (!idx) return CMD_WARNING; @@ -8564,12 +8543,12 @@ DEFUN (show_ip_bgp_instance_all, BGP_SAFI_HELP_STR JSON_STR) { - vrf_id_t vrf = VRF_DEFAULT; afi_t afi = AFI_IP; safi_t safi = SAFI_UNICAST; + struct bgp *bgp = NULL; int idx = 0; - bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp); if (!idx) return CMD_WARNING; @@ -9253,41 +9232,19 @@ DEFUN (show_ip_bgp_instance_neighbor_prefix_counts, "Display detailed prefix count information\n" JSON_STR) { - vrf_id_t vrf = VRF_DEFAULT; afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; struct peer *peer; int idx = 0; struct bgp *bgp = NULL; - bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp); if (!idx) return CMD_WARNING; int uj = use_json (argc, argv); if (uj) argc--; - if (vrf != VRF_ALL) - { - bgp = bgp_lookup_by_vrf_id (vrf); - if (bgp == NULL) - { - if (uj) - { - json_object *json_no = NULL; - json_no = json_object_new_object(); - json_object_string_add(json_no, "warning", "Can't find BGP view"); - vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE); - json_object_free(json_no); - } - else - vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE); - return CMD_WARNING; - } - } - else - bgp = NULL; - argv_find (argv, argc, "neighbors", &idx); peer = peer_lookup_in_view (vty, bgp, argv[idx+1]->arg, uj); if (! peer) @@ -9629,7 +9586,6 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route, "Name of the route map\n" JSON_STR) { - vrf_id_t vrf = VRF_DEFAULT; afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; char *rmap_name = NULL; @@ -9640,29 +9596,13 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route, int idx = 0; - bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp); if (!idx) return CMD_WARNING; int uj = use_json (argc, argv); if (uj) argc--; - bgp = bgp_lookup_by_vrf_id (vrf); - if (bgp == NULL) - { - if (uj) - { - json_object *json_no = NULL; - json_no = json_object_new_object(); - json_object_string_add(json_no, "warning", "Can't find BGP view"); - vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE); - json_object_free(json_no); - } - else - vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE); - return CMD_WARNING; - } - /* neighbors <A.B.C.D|X:X::X:X|WORD> */ argv_find (argv, argc, "neighbors", &idx); peerstr = argv[++idx]->arg; @@ -9809,7 +9749,6 @@ DEFUN (show_ip_bgp_neighbor_routes, "Display routes learned from neighbor\n" JSON_STR) { - vrf_id_t vrf = VRF_DEFAULT; char *peerstr = NULL; struct bgp *bgp = NULL; afi_t afi = AFI_IP6; @@ -9819,34 +9758,13 @@ DEFUN (show_ip_bgp_neighbor_routes, int idx = 0; - bgp_vty_find_and_parse_afi_safi_vrf (vty, argv, argc, &idx, &afi, &safi, &vrf); + bgp_vty_find_and_parse_afi_safi_bgp (vty, argv, argc, &idx, &afi, &safi, &bgp); if (!idx) return CMD_WARNING; int uj = use_json (argc, argv); if (uj) argc--; - if (vrf != VRF_ALL) - { - bgp = bgp_lookup_by_vrf_id (vrf); - if (bgp == NULL) - { - if (uj) - { - json_object *json_no = NULL; - json_no = json_object_new_object(); - json_object_string_add(json_no, "warning", "Can't find BGP view"); - vty_out (vty, "%s%s", json_object_to_json_string(json_no), VTY_NEWLINE); - json_object_free(json_no); - } - else - vty_out (vty, "Can't find BGP instance %s%s", argv[5]->arg, VTY_NEWLINE); - return CMD_WARNING; - } - } - else - bgp = NULL; - /* neighbors <A.B.C.D|X:X::X:X|WORD> */ argv_find (argv, argc, "neighbors", &idx); peerstr = argv[++idx]->arg; diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index c7bcfe25f9..bafc81eaf8 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -22,7 +22,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "prefix.h" #include "filter.h" -#include "vty.h" #include "routemap.h" #include "command.h" #include "linklist.h" diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index 884523919e..06e443b25a 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -23,7 +23,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "prefix.h" #include "memory.h" #include "sockunion.h" -#include "vty.h" #include "queue.h" #include "filter.h" #include "command.h" diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index ae3571e83d..34c7691c26 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -214,10 +214,10 @@ argv_find_and_parse_safi (struct cmd_token **argv, int argc, int *index, safi_t } /* - * bgp_vty_find_and_parse_afi_safi_vrf + * bgp_vty_find_and_parse_afi_safi_bgp * - * For a given 'show ...' command, correctly parse the afi/safi/vrf out from it - * This function *assumes* that the calling function pre-sets the afi/safi/vrf + * For a given 'show ...' command, correctly parse the afi/safi/bgp out from it + * This function *assumes* that the calling function pre-sets the afi/safi/bgp * to appropriate values for the calling function. This is to allow the * calling function to make decisions appropriate for the show command * that is being parsed. @@ -238,7 +238,7 @@ argv_find_and_parse_safi (struct cmd_token **argv, int argc, int *index, safi_t * idx -> The current place in the command, generally should be 0 for this function * afi -> The parsed afi if it was included in the show command, returned here * safi -> The parsed safi if it was included in the show command, returned here - * vrf -> The parsed vrf id if it was included in the show command, returned here + * bgp -> Pointer to the bgp data structure we need to fill in. * * The function returns the correct location in the parse tree for the * last token found. @@ -247,14 +247,14 @@ argv_find_and_parse_safi (struct cmd_token **argv, int argc, int *index, safi_t * it found the last token. */ int -bgp_vty_find_and_parse_afi_safi_vrf (struct vty *vty, struct cmd_token **argv, int argc, int *idx, - afi_t *afi, safi_t *safi, vrf_id_t *vrf) +bgp_vty_find_and_parse_afi_safi_bgp (struct vty *vty, struct cmd_token **argv, int argc, int *idx, + afi_t *afi, safi_t *safi, struct bgp **bgp) { char *vrf_name = NULL; assert (afi); assert (safi); - assert (vrf && *vrf != VRF_UNKNOWN); + assert (bgp); if (argv_find (argv, argc, "ip", idx)) *afi = AFI_IP; @@ -263,26 +263,34 @@ bgp_vty_find_and_parse_afi_safi_vrf (struct vty *vty, struct cmd_token **argv, i { vrf_name = argv[*idx + 1]->arg; *idx += 2; - } - - if (argv_find_and_parse_afi (argv, argc, idx, afi)) - argv_find_and_parse_safi (argv, argc, idx, safi); - if (vrf_name) - { - if (strmatch(vrf_name, "all")) - *vrf = VRF_ALL; + if (strmatch (vrf_name, "all")) + *bgp = NULL; else - *vrf = vrf_name_to_id (vrf_name); + { + *bgp = bgp_lookup_by_name (vrf_name); + if (!*bgp) + { + vty_out (vty, "View/Vrf specified is unknown: %s%s", vrf_name, VTY_NEWLINE); + *idx = 0; + return 0; + } + } } - - if (*vrf == VRF_UNKNOWN) + else { - vty_out (vty, "View/Vrf specified is unknown: %s", vrf_name); - *idx = 0; - return 0; + *bgp = bgp_get_default (); + if (!*bgp) + { + vty_out (vty, "Unable to find default BGP instance%s", VTY_NEWLINE); + *idx = 0; + return 0; + } } + if (argv_find_and_parse_afi (argv, argc, idx, afi)) + argv_find_and_parse_safi (argv, argc, idx, safi); + *idx += 1; return *idx; } diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 13e67d112e..33d24d530e 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -64,6 +64,6 @@ extern int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, safi_t *safi); extern int -bgp_vty_find_and_parse_afi_safi_vrf (struct vty *vty, struct cmd_token **argv, int argc, int *idx, - afi_t *afi, safi_t *safi, vrf_id_t *vrf); +bgp_vty_find_and_parse_afi_safi_bgp (struct vty *vty, struct cmd_token **argv, int argc, int *idx, + afi_t *afi, safi_t *safi, struct bgp **bgp); #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index f0f2ed87bd..eba030e6f9 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "qobj.h" #include "lib/json.h" #include "vrf.h" +#include "vty.h" /* For union sockunion. */ #include "queue.h" diff --git a/doc/cli.md b/doc/cli.md new file mode 100644 index 0000000000..559f75a740 --- /dev/null +++ b/doc/cli.md @@ -0,0 +1,356 @@ +FRR Command Line Interface +========================== + +Definition Grammar +------------------ + +This is a reference for the syntax used when defining new CLI commands. An +example definition is: + +DEFUN (command_name, + command_name_cmd, +--> "example <command|line [interface]> DEFINITION...", + <..doc strings..>) + +The arrowed part is the definition string. + +Explicit syntax rules in Flex and Bison may be found in lib/command_lex.l and +lib/command_parse.y, respectively. If you can read BNF and regex those will be +more useful than this document. + +If the parser is throwing syntax or other errors and you can't figure out why, +it's unlikely to be a bug in the parser. If the error message is not useful, +please file a bug for a better error message. If all else fails, read the token +definitions in the lexer source and the Bison BNF in the parser source. + +Characters allowed in each token type: + +Tokens +------ +* WORD -- A token that begins with +, -, or a lowercase letter. It is + an unchanging part of the command and will only match itself. + Example: "show ip bgp", every token is a WORD. +* IPV4 -- 'A.B.C.D', matches an IPv4 address. +* IPV6 -- 'X:X::X:X', matches an IPv6 address. +* IPV4_PREFIX -- 'A.B.C.D/M', matches an IPv4 prefix in CIDR notation. +* IPV6_PREFIX -- 'X:X::X:X/M', matches an IPv6 prefix in CIDR notation. +* VARIABLE -- Begins with a capital letter. Matches any input. +* RANGE -- Numeric range delimited by parentheses, e.g. (-100 - 100) or + (10-20). Will only match numbers in the range. + +Rules +----- +* <angle|brackets> -- Contain sequences of tokens separated by pipes and + provide mutual exclusion. Sequences may contain + <mutual|exclusion> but not as the first token. + Disallowed: "example <<a|b> c|d>" + Allowed: "example <a c|b c|d> +* [square brackets] -- Contains sequences of tokens that are optional (can be + omitted). +* {curly|braces} -- similar to angle brackets, but instead of mutual + exclusion, curly braces indicate that one or more of the + pipe-separated sequences may be provided in any order. +* VARIADICS... -- Any token which accepts input (so anything except WORD) + and that occurs as the last token of a line may be + followed by an ellipsis, which indicates that input + matching the token may be repeated an unlimited number + of times. + +Some general notes: + +* Options are allowed at the beginning of the command. The developer is + entreated to use these extremely sparingly. They are most useful for + implementing the 'no' form of configuration commands. Please think carefully + before using them for anything else. There is usually a better solution, even + if it is just separating out the command definition into separate ones. + +* The developer should judiciously apply separation of concerns when defining + CLI. CLI definitions for two unrelated or vaguely related commands or + configuration items should be defined in separate commands. Clarity is + preferred over LOC (within reason). + +Doc Strings +----------- +Each token in a command definition should be documented with a brief doc +string that informs a user of the meaning and/or purpose of the subsequent +command tree. These strings are provided as the last parameter to DEFUN macros, +concatenated together and separated by an escaped newline ('\n'). These are +best explained by example. + +DEFUN (config_terminal, + config_terminal_cmd, + "configure terminal", + "Configuration from vty interface\n" + "Configuration terminal\n") + +The last parameter is split into two lines for readability. Two newline +delimited doc strings are present, one for each token in the command. The +second string documents the functionality of the 'terminal' command in the +'configure' tree. + +Note that the first string, for 'configure' does not contain documentation for +'terminal'. This is because the CLI is best envisioned as a tree, with tokens +defining branches. An imaginary 'start' token is the root of every command in a +CLI node. Each subsequent written token descends into a subtree, so the +documentation for that token ideally summarizes all the functionality contained +in the subtree. + +A consequence of this structure is that the developer must be careful to use +the same doc strings when defining multiple commands that are part of the same +tree. Commands which share prefixes must share the same doc strings for those +prefixes. On startup the parser will generate warnings if it notices +inconsistent doc strings. Behavior is undefined; the same token may show up +twice in completions, with different doc strings, or it may show up once with a +random doc string. Parser warnings should be heeded and fixed to avoid +confusing users. + +The number of doc strings provided must be equal to the amount of tokens +present in the command definition, read left to right, ignoring any special +constructs. + +In the examples below, each arrowed token needs a doc string. + + "show ip bgp" + ^ ^ ^ + + "command <foo|bar> [example]" + ^ ^ ^ ^ + +Data Structures +--------------- +On startup, the CLI parser sequentially parses each command string definition +and constructs a directed graph with each token forming a node. This graph is +the basis of the entire CLI system. It is used to match user input in order to +generate command completions and match commands to functions. + +There is one graph per CLI node (not the same as a graph node in the CLI +graph). The CLI node struct keeps a reference to its graph (see lib/command.h). + +While most of the graph maintains the form of a tree, special constructs +outlined in the Rules section introduce some quirks. <>, [] and {} form +self-contained 'subgraphs'. Each subgraph is a tree except that all of the +'leaves' actually share a child node. This helps with minimizing graph size and +debugging. + +As an example, the subgraph generated by <foo|bar> looks like this: + + . + . + | + +----+---+ + +--- -+ FORK +----+ + | +--------+ | + +--v---+ +--v---+ + | foo | | bar | + +--+---+ +--+---+ + | +------+ | + +------> JOIN <-----+ + +---+--+ + | + . + . + +FORK and JOIN nodes are plumbing nodes that don't correspond to user input. +They're necessary in order to deduplicate these constructs where applicable. + +Options follow the same form, except that there is an edge from the FORK node +to the JOIN node. + +Keywords follow the same form, except that there is an edge from JOIN to FORK. +Because of this the CLI graph cannot be called acyclic. There is special logic +in the input matching code that keeps a stack of paths already taken through +the node in order to disallow following the same path more than once. + +Variadics are a bit special; they have an edge back to themselves, which allows +repeating the same input indefinitely. + +The leaves of the graph are nodes that have no out edges. These nodes are +special; their data section does not contain a token, as most nodes do, or +NULL, as in FORK/JOIN nodes, but instead has a pointer to a cmd_element. All +paths through the graph that terminate on a leaf are guaranteed to be defined +by that command. When a user enters a complete command, the command matcher +tokenizes the input and executes a DFS on the CLI graph. If it is +simultaneously able to exhaust all input (one input token per graph node), and +then find exactly one leaf connected to the last node it reaches, then the +input has matched the corresponding command and the command is executed. If it +finds more than one node, then the command is ambiguous (more on this in +deduplication). If it cannot exhaust all input, the command is unknown. If it +exhausts all input but does not find an edge node, the command is incomplete. + +The parser uses an incremental strategy to build the CLI graph for a node. Each +command is parsed into its own graph, and then this graph is merged into the +overall graph. During this merge step, the parser makes a best-effort attempt +to remove duplicate nodes. If it finds a node in the overall graph that is +equal to a node in the corresponding position in the command graph, it will +intelligently merge the properties from the node in the command graph into the +already-existing node. Subgraphs are also checked for isomorphism and merged +where possible. The definition of whether two nodes are 'equal' is based on the +equality of some set of token properties; read the parser source for the most +up-to-date definition of equality. + +When the parser is unable to deduplicate some complicated constructs, this +can result in two identical paths through separate parts of the graph. If +this occurs and the user enters input that matches these paths, they will +receive an 'ambiguous command' error and will be unable to execute the command. +Most of the time the parser can detect and warn about duplicate commands, but +it will not always be able to do this. Hence care should be taken before +defining a new command to ensure it is not defined elsewhere. + + +Command handlers +---------------- +The block that follows a CLI definition is executed when a user enters input +that matches the definition. Its function signature looks like this: + +int (*func) (const struct cmd_element *, struct vty *, int, struct cmd_token *[]); + +The first argument is the command definition struct. The last argument is an +ordered array of tokens that correspond to the path taken through the graph, +and the argument just prior to that is the length of the array. + +The arrangement of the token array has changed from the prior incarnation of +the CLI system. In the old system, missing arguments were padded with NULLs so +that the same parts of a command would show up at the same indices regardless +of what was entered. The new system does not perform such padding and therefore +it is generally _incorrect_ to assume consistent indices in this array. As a +simple example: + +Command definition: + command [foo] <bar|baz> + +User enters: + command foo bar + +Array: + [0] -> command + [1] -> foo + [2] -> bar + +User enters: + command baz + +Array: + [0] -> command + [1] -> baz + + + +Command abbreviation & matching priority +---------------------------------------- +As in the prior implementation, it is possible for users to elide parts of +tokens when the CLI matcher does not need them to make an unambiguous match. +This is best explained by example. + +Command definitions: + command dog cow + command dog crow + +User input: + c d c -> ambiguous command + c d co -> match "command dog cow" + +In the new implementation, this functionality has improved. Where previously +the parser would stop at the first ambiguous token, it will now look ahead and +attempt to disambiguate based on tokens later on in the input string. + +Command definitions: + show ip bgp A.B.C.D + show ipv6 bgp X:X::X:X + +User enters: + s i b 4.3.2.1 -> match "show ip bgp A.B.C.D" + s i b ::e0 -> match "show ipv6 bgp X:X::X:X" + +Previously both of these commands would be ambiguous since 'i' does not +explicitly select either 'ip' or 'ipv6'. However, since the user later provides +a token that matches only one of the commands (an IPv4 or IPv6 address) the +parser is able to look ahead and select the appropriate command. This has some +implications for parsing the argv*[] that is passed to the command handler. + +Now consider a command definition such as: + command <foo|VAR> + +'foo' only matches the string 'foo', but 'VAR' matches any input, including +'foo'. Who wins? In situations like this the matcher will always choose the +'better' match, so 'foo' will win. + +Consider also: + show <ip|ipv6> foo + +User input: + show ip foo + +'ip' partially matches 'ipv6' but exactly matches 'ip', so 'ip' will win. + + +struct cmd_token +---------------- + +/* Command token struct. */ +struct cmd_token +{ + enum cmd_token_type type; // token type + u_char attr; // token attributes + bool allowrepeat; // matcher allowed to match token repetitively? + + char *text; // token text + char *desc; // token description + long long min, max; // for ranges + char *arg; // user input that matches this token +}; + +This struct is used in the CLI graph to match input against. It is also used to +pass user input to command handler functions, as it is frequently useful for +handlers to have access to that information. When a command is matched, the +sequence of cmd_tokens that form the matching path are duplicated and placed in +order into argv*[]. Before this happens the ->arg field is set to point at the +snippet of user input that matched it. + +For most nontrivial commands the handler function will need to determine which +of the possible matching inputs was entered. Previously this was done by +looking at the first few characters of input. This is now considered an +anti-pattern and should be avoided. Instead, the ->type or ->text fields for +this logic. The ->type field can be used when the possible inputs differ in +type. When the possible types are the same, use the ->text field. This field +has the full text of the corresponding token in the definition string and using +it makes for much more readable code. An example is helpful. + +Command definition: + command <(1-10)|foo|BAR> + +In this example, the user may enter any one of: + * an integer between 1 and 10 + * "foo" + * anything at all + +If the user enters "command f", then: + +argv[1]->type == WORD_TKN +argv[1]->arg == "f" +argv[1]->text == "foo" + +Range tokens have some special treatment; a token with ->type == RANGE_TKN will +have the ->min and ->max fields set to the bounding values of the range. + + +Permutations +------------ +Finally, it is sometimes useful to check all the possible combinations of input +that would match an arbitrary definition string. There is a tool in tools/ +called 'permutations' that reads CLI definition strings on stdin and prints out +all matching input permutations. It also dumps a text representation of the +graph, which is more useful for debugging than anything else. It looks like +this: + +$ ./permutations "show [ip] bgp [<view|vrf> WORD]" + +show ip bgp view WORD +show ip bgp vrf WORD +show ip bgp +show bgp view WORD +show bgp vrf WORD +show bgp + +This functionality is also built into VTY/VTYSH; the 'list permutations' +command will list all possible matching input permutations in the current CLI +node. diff --git a/lib/plist.c b/lib/plist.c index 9a2fc4af09..3ed5c8fc5c 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -27,7 +27,6 @@ #include "plist.h" #include "sockunion.h" #include "buffer.h" -#include "stream.h" #include "log.h" #include "routemap.h" #include "lib/json.h" 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/pimd/pim_cmd.c b/pimd/pim_cmd.c index 62d8ad8e07..3236eb07b6 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -2342,44 +2342,6 @@ static void mroute_del_all() } } -static void static_mroute_add_all() -{ - struct listnode *node; - struct static_route *s_route; - - for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { - if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure writing MFC", - __FILE__, __PRETTY_FUNCTION__, - source_str, group_str); - } - } -} - -static void static_mroute_del_all() -{ - struct listnode *node; - struct static_route *s_route; - - for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { - if (pim_mroute_del(&s_route->c_oil, __PRETTY_FUNCTION__)) { - /* just log warning */ - char source_str[INET_ADDRSTRLEN]; - char group_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", s_route->c_oil.oil.mfcc_origin, source_str, sizeof(source_str)); - pim_inet4_dump("<group?>", s_route->c_oil.oil.mfcc_mcastgrp, group_str, sizeof(group_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) failure clearing MFC", - __FILE__, __PRETTY_FUNCTION__, - source_str, group_str); - } - } -} - DEFUN (clear_ip_mroute, clear_ip_mroute_cmd, "clear ip mroute", @@ -2670,12 +2632,12 @@ DEFUN (show_ip_pim_state, if (uj) argc--; - if (argc == 5) + if (argc == 6) { src_or_group = argv[4]->arg; group = argv[5]->arg; } - else if (argc == 4) + else if (argc == 5) src_or_group = argv[4]->arg; pim_show_state(vty, src_or_group, group, uj); @@ -2815,22 +2777,16 @@ DEFUN (show_ip_multicast, { time_t now = pim_time_monotonic_sec(); - if (PIM_MROUTE_IS_ENABLED) { - char uptime[10]; + char uptime[10]; - vty_out(vty, "Mroute socket descriptor: %d%s", - qpim_mroute_socket_fd, - VTY_NEWLINE); + vty_out(vty, "Mroute socket descriptor: %d%s", + qpim_mroute_socket_fd, + VTY_NEWLINE); - pim_time_uptime(uptime, sizeof(uptime), now - qpim_mroute_socket_creation); - vty_out(vty, "Mroute socket uptime: %s%s", - uptime, - VTY_NEWLINE); - } - else { - vty_out(vty, "Multicast disabled%s", - VTY_NEWLINE); - } + pim_time_uptime(uptime, sizeof(uptime), now - qpim_mroute_socket_creation); + vty_out(vty, "Mroute socket uptime: %s%s", + uptime, + VTY_NEWLINE); vty_out(vty, "%s", VTY_NEWLINE); @@ -3570,31 +3526,24 @@ DEFUN (no_ip_pim_rp_prefix_list, return pim_no_rp_cmd_worker (vty, argv[4]->arg, NULL, argv[6]->arg); } -DEFUN (ip_multicast_routing, - ip_multicast_routing_cmd, - "ip multicast-routing", - IP_STR - "Enable IP multicast forwarding\n") +DEFUN_HIDDEN (ip_multicast_routing, + ip_multicast_routing_cmd, + "ip multicast-routing", + IP_STR + "Enable IP multicast forwarding\n") { - pim_mroute_socket_enable(); - pim_if_add_vif_all(); - mroute_add_all(); - static_mroute_add_all(); return CMD_SUCCESS; } -DEFUN (no_ip_multicast_routing, - no_ip_multicast_routing_cmd, - "no ip multicast-routing", - NO_STR - IP_STR - "Global IP configuration subcommands\n" - "Enable IP multicast forwarding\n") +DEFUN_HIDDEN (no_ip_multicast_routing, + no_ip_multicast_routing_cmd, + "no ip multicast-routing", + NO_STR + IP_STR + "Global IP configuration subcommands\n" + "Enable IP multicast forwarding\n") { - mroute_del_all(); - static_mroute_del_all(); - pim_if_del_vif_all(); - pim_mroute_socket_disable(); + vty_out (vty, "Command is Disabled and will be removed in a future version%s", VTY_NEWLINE); return CMD_SUCCESS; } @@ -3657,13 +3606,9 @@ DEFUN (no_ip_ssmpingd, return CMD_SUCCESS; } -DEFUN (interface_ip_igmp, - interface_ip_igmp_cmd, - "ip igmp", - IP_STR - IFACE_IGMP_STR) +static int +pim_cmd_igmp_start (struct vty *vty, struct interface *ifp) { - VTY_DECLVAR_CONTEXT(interface, ifp); struct pim_interface *pim_ifp; pim_ifp = ifp->info; @@ -3686,6 +3631,17 @@ DEFUN (interface_ip_igmp, return CMD_SUCCESS; } +DEFUN (interface_ip_igmp, + interface_ip_igmp_cmd, + "ip igmp", + IP_STR + IFACE_IGMP_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + return pim_cmd_igmp_start(vty, ifp); +} + DEFUN (interface_no_ip_igmp, interface_no_ip_igmp_cmd, "no ip igmp", @@ -3949,15 +3905,15 @@ DEFUN (interface_ip_igmp_query_interval, struct pim_interface *pim_ifp; int query_interval; int query_interval_dsec; + int ret; pim_ifp = ifp->info; if (!pim_ifp) { - vty_out(vty, - "IGMP not enabled on interface %s. Please enable IGMP first.%s", - ifp->name, - VTY_NEWLINE); - return CMD_WARNING; + ret = pim_cmd_igmp_start(vty, ifp); + if (ret != CMD_SUCCESS) + return ret; + pim_ifp = ifp->info; } query_interval = atoi(argv[3]->arg); @@ -4038,15 +3994,15 @@ DEFUN (interface_ip_igmp_version, VTY_DECLVAR_CONTEXT(interface,ifp); struct pim_interface *pim_ifp; int igmp_version; + int ret; pim_ifp = ifp->info; if (!pim_ifp) { - vty_out(vty, - "IGMP not enabled on interface %s. Please enable IGMP first.%s", - ifp->name, - VTY_NEWLINE); - return CMD_WARNING; + ret = pim_cmd_igmp_start(vty, ifp); + if (ret != CMD_SUCCESS) + return ret; + pim_ifp = ifp->info; } igmp_version = atoi(argv[3]->arg); @@ -4091,15 +4047,15 @@ DEFUN (interface_ip_igmp_query_max_response_time, VTY_DECLVAR_CONTEXT(interface, ifp); struct pim_interface *pim_ifp; int query_max_response_time; + int ret; pim_ifp = ifp->info; if (!pim_ifp) { - vty_out(vty, - "IGMP not enabled on interface %s. Please enable IGMP first.%s", - ifp->name, - VTY_NEWLINE); - return CMD_WARNING; + ret = pim_cmd_igmp_start(vty, ifp); + if (ret != CMD_SUCCESS) + return ret; + pim_ifp = ifp->info; } query_max_response_time = atoi(argv[3]->arg); @@ -4154,15 +4110,15 @@ DEFUN_HIDDEN (interface_ip_igmp_query_max_response_time_dsec, struct pim_interface *pim_ifp; int query_max_response_time_dsec; int default_query_interval_dsec; + int ret; pim_ifp = ifp->info; if (!pim_ifp) { - vty_out(vty, - "IGMP not enabled on interface %s. Please enable IGMP first.%s", - ifp->name, - VTY_NEWLINE); - return CMD_WARNING; + ret = pim_cmd_igmp_start(vty, ifp); + if (ret != CMD_SUCCESS) + return ret; + pim_ifp = ifp->info; } query_max_response_time_dsec = atoi(argv[4]->arg); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 8f7d40bb36..c545b56330 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -173,9 +173,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) pim_sock_reset(ifp); - if (PIM_MROUTE_IS_ENABLED) { - pim_if_add_vif(ifp); - } + pim_if_add_vif(ifp); return pim_ifp; } @@ -197,9 +195,7 @@ void pim_if_delete(struct interface *ifp) pim_neighbor_delete_all (ifp, "Interface removed from configuration"); - if (PIM_MROUTE_IS_ENABLED) { - pim_if_del_vif(ifp); - } + pim_if_del_vif(ifp); list_delete(pim_ifp->igmp_socket_list); list_delete(pim_ifp->pim_neighbor_list); @@ -591,16 +587,14 @@ void pim_if_addr_add(struct connected *ifc) } } /* pim */ - if (PIM_MROUTE_IS_ENABLED) { /* PIM or IGMP is enabled on interface, and there is at least one address assigned, then try to create a vif_index. */ - if (pim_ifp->mroute_vif_index < 0) { - pim_if_add_vif(ifp); - } - pim_ifchannel_scan_forward_start (ifp); + if (pim_ifp->mroute_vif_index < 0) { + pim_if_add_vif(ifp); } + pim_ifchannel_scan_forward_start (ifp); } static void pim_if_addr_del_igmp(struct connected *ifc) @@ -730,16 +724,14 @@ void pim_if_addr_add_all(struct interface *ifp) } } /* pim */ } - if (PIM_MROUTE_IS_ENABLED) { - /* - * PIM or IGMP is enabled on interface, and there is at least one - * address assigned, then try to create a vif_index. - */ - if (pim_ifp->mroute_vif_index < 0) { - pim_if_add_vif(ifp); - } - pim_ifchannel_scan_forward_start (ifp); + /* + * PIM or IGMP is enabled on interface, and there is at least one + * address assigned, then try to create a vif_index. + */ + if (pim_ifp->mroute_vif_index < 0) { + pim_if_add_vif(ifp); } + pim_ifchannel_scan_forward_start (ifp); pim_rp_setup(); pim_rp_check_on_if_add(pim_ifp); diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index ae5d0e9891..334e0ce06e 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -608,7 +608,6 @@ static int mroute_read(struct thread *t) static void mroute_read_on() { zassert(!qpim_mroute_socket_reader); - zassert(PIM_MROUTE_IS_ENABLED); THREAD_READ_ON(master, qpim_mroute_socket_reader, mroute_read, 0, qpim_mroute_socket_fd); @@ -623,9 +622,6 @@ int pim_mroute_socket_enable() { int fd; - if (PIM_MROUTE_IS_ENABLED) - return -1; - if ( pimd_privs.change (ZPRIVS_RAISE) ) zlog_err ("pim_mroute_socket_enable: could not raise privs, %s", safe_strerror (errno) ); @@ -659,9 +655,6 @@ int pim_mroute_socket_enable() int pim_mroute_socket_disable() { - if (PIM_MROUTE_IS_DISABLED) - return -1; - if (pim_mroute_set(qpim_mroute_socket_fd, 0)) { zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s", qpim_mroute_socket_fd, errno, safe_strerror(errno)); @@ -691,12 +684,6 @@ int pim_mroute_add_vif(struct interface *ifp, struct in_addr ifaddr, unsigned ch struct vifctl vc; int err; - if (PIM_MROUTE_IS_DISABLED) { - zlog_warn("%s: global multicast is disabled", - __PRETTY_FUNCTION__); - return -1; - } - memset(&vc, 0, sizeof(vc)); vc.vifc_vifi = pim_ifp->mroute_vif_index; #ifdef VIFF_USE_IFINDEX @@ -740,12 +727,6 @@ int pim_mroute_del_vif(int vif_index) struct vifctl vc; int err; - if (PIM_MROUTE_IS_DISABLED) { - zlog_warn("%s: global multicast is disabled", - __PRETTY_FUNCTION__); - return -1; - } - if (PIM_DEBUG_MROUTE) { struct interface *ifp = pim_if_find_by_vif_index (vif_index); @@ -777,11 +758,6 @@ int pim_mroute_add(struct channel_oil *c_oil, const char *name) qpim_mroute_add_last = pim_time_monotonic_sec(); ++qpim_mroute_add_events; - if (PIM_MROUTE_IS_DISABLED) { - zlog_warn("%s: global multicast is disabled", - __PRETTY_FUNCTION__); - return -1; - } /* Do not install route if incoming interface is undefined. */ if (c_oil->oil.mfcc_parent == MAXVIFS) { @@ -858,12 +834,6 @@ int pim_mroute_del (struct channel_oil *c_oil, const char *name) qpim_mroute_del_last = pim_time_monotonic_sec(); ++qpim_mroute_del_events; - if (PIM_MROUTE_IS_DISABLED) { - zlog_warn("%s: global multicast is disabled", - __PRETTY_FUNCTION__); - return -1; - } - if (!c_oil->installed) { if (PIM_DEBUG_MROUTE) diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 03ee7e92ec..5b6a79b95a 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -148,11 +148,6 @@ int pim_global_config_write(struct vty *vty) writes += pim_msdp_config_write (vty); - if (PIM_MROUTE_IS_ENABLED) { - vty_out(vty, "ip multicast-routing%s", VTY_NEWLINE); - ++writes; - } - writes += pim_rp_config_write (vty); if (qpim_register_suppress_time != PIM_REGISTER_SUPPRESSION_TIME_DEFAULT) diff --git a/pimd/pimd.c b/pimd/pimd.c index 2316cd08f5..aa863fd47f 100644 --- a/pimd/pimd.c +++ b/pimd/pimd.c @@ -124,7 +124,7 @@ void pim_init() } qpim_static_route_list->del = (void (*)(void *)) pim_static_route_free; - qpim_mroute_socket_fd = -1; /* mark mroute as disabled */ + pim_mroute_socket_enable(); qpim_inaddr_any.s_addr = PIM_NET_INADDR_ANY; diff --git a/pimd/pimd.h b/pimd/pimd.h index 0da8452ab5..0906016300 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -154,9 +154,6 @@ extern int qpim_packet_process; #define PIM_JP_HOLDTIME (qpim_t_periodic * 7 / 2) -#define PIM_MROUTE_IS_ENABLED (qpim_mroute_socket_fd >= 0) -#define PIM_MROUTE_IS_DISABLED (qpim_mroute_socket_fd < 0) - /* * Register-Stop Timer (RST(S,G)) * Default values diff --git a/tools/frr-reload.py b/tools/frr-reload.py index bf2b006963..4c26300099 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -350,6 +350,8 @@ end "ip ", "ipv6 ", "log ", + "mpls", + "no ", "password ", "ptm-enable", "router-id ", |
