diff --git a/lib/command.h b/lib/command.h index dc1ff5a7d149..3edb9d4685a2 100644 --- a/lib/command.h +++ b/lib/command.h @@ -147,7 +147,9 @@ enum node_type { PW_NODE, /* Pseudowire config node */ SEGMENT_LIST_NODE, /* SR segment list config node */ SR_POLICY_NODE, /* SR policy config node */ - PCC_NODE, /* PCC config node */ + PCEP_CONFIG_GROUP_NODE, /* PCEP peer Configuration Group node */ + PCC_PEER_NODE, /* PCC Peer (PCE) config node */ + PCC_NODE, /* PCC config node */ VTY_NODE, /* Vty node. */ FPM_NODE, /* Dataplane FPM node. */ LINK_PARAMS_NODE, /* Link-parameters node */ diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c index 64d77158c6e0..ecf13a7b8179 100644 --- a/pathd/path_pcep.c +++ b/pathd/path_pcep.c @@ -35,6 +35,7 @@ #include "pathd/path_errors.h" #include "pathd/path_pcep_memory.h" #include "pathd/path_pcep.h" +#include "pathd/path_pcep_cli.h" #include "pathd/path_pcep_controller.h" #include "pathd/path_pcep_lib.h" #include "pathd/path_pcep_nb.h" @@ -58,25 +59,11 @@ static int pathd_candidate_created_handler(struct srte_candidate *candidate); static int pathd_candidate_updated_handler(struct srte_candidate *candidate); static int pathd_candidate_removed_handler(struct srte_candidate *candidate); -/* CLI Functions */ -static int pcep_cli_debug_config_write(struct vty *vty); -static int pcep_cli_debug_set_all(uint32_t flags, bool set); -static int pcep_cli_pcc_config_write(struct vty *vty); -static void pcep_cli_init(void); - /* Module Functions */ static int pcep_module_finish(void); static int pcep_module_late_init(struct thread_master *tm); static int pcep_module_init(void); -static struct cmd_node pcc_node = { - .name = "pcc", - .node = PCC_NODE, - .parent_node = CONFIG_NODE, - .prompt = "%s(config-pcc)# ", - .config_write = pcep_cli_pcc_config_write, -}; - /* ------------ Path Helper Functions ------------ */ struct path *pcep_new_path(void) @@ -240,426 +227,6 @@ int pathd_candidate_removed_handler(struct srte_candidate *candidate) } -/* ------------ CLI Functions ------------ */ - -DEFUN(show_pcep_counters, show_pcep_counters_cmd, "show pcep counters", - SHOW_STR - "PCEP info\n" - "PCEP counters\n") -{ - int i, j, row; - time_t diff_time; - struct tm *tm_info; - char tm_buffer[26]; - struct counters_group *group; - struct counters_subgroup *subgroup; - struct counter *counter; - const char *group_name, *empty_string = ""; - struct ttable *tt; - char *table; - - group = pcep_ctrl_get_counters(pcep_g->fpt, 1); - - if (group == NULL) { - vty_out(vty, "No counters to display.\n\n"); - return CMD_SUCCESS; - } - - diff_time = time(NULL) - group->start_time; - tm_info = localtime(&group->start_time); - strftime(tm_buffer, sizeof(tm_buffer), "%Y-%m-%d %H:%M:%S", tm_info); - - vty_out(vty, "PCEP counters since %s (%luh %lum %lus):\n", tm_buffer, - diff_time / 3600, (diff_time / 60) % 60, diff_time % 60); - - /* Prepare table. */ - tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); - ttable_add_row(tt, "Group|Name|Value"); - tt->style.cell.rpad = 2; - tt->style.corner = '+'; - ttable_restyle(tt); - ttable_rowseps(tt, 0, BOTTOM, true, '-'); - - for (row = 0, i = 0; i <= group->num_subgroups; i++) { - subgroup = group->subgroups[i]; - if (subgroup != NULL) { - group_name = subgroup->counters_subgroup_name; - for (j = 0; j <= subgroup->num_counters; j++) { - counter = subgroup->counters[j]; - if (counter != NULL) { - ttable_add_row(tt, "%s|%s|%u", - group_name, - counter->counter_name, - counter->counter_value); - row++; - group_name = empty_string; - } - } - ttable_rowseps(tt, row, BOTTOM, true, '-'); - } - } - - /* Dump the generated table. */ - table = ttable_dump(tt, "\n"); - vty_out(vty, "%s\n", table); - XFREE(MTYPE_TMP, table); - - ttable_del(tt); - - pcep_lib_free_counters(group); - - return CMD_SUCCESS; -} - -DEFUN_NOSH( - pcep_cli_pcc, pcep_cli_pcc_cmd, - "pcc [{ip A.B.C.D | ipv6 X:X::X:X}] [port (1024-65535)] [msd (1-16)]", - "PCC configuration\n" - "PCC source ip\n" - "PCC source IPv4 address\n" - "PCC source ip\n" - "PCC source IPv6 address\n" - "PCC source port\n" - "PCC source port value\n" - "PCC maximum SID depth \n" - "PCC maximum SID depth value\n") -{ - uint16_t pcc_flags; - struct in_addr pcc_addr_v4; - struct in6_addr pcc_addr_v6; - uint32_t pcc_port = PCEP_DEFAULT_PORT; - uint32_t pcc_msd = PCC_DEFAULT_MSD; - struct pcc_opts *opts, *opts_copy; - int i = 1; - - memset(&pcc_addr_v4, 0, sizeof(pcc_addr_v4)); - memset(&pcc_addr_v6, 0, sizeof(pcc_addr_v6)); - - /* Handle the rest of the arguments */ - while (i < argc) { - if (strcmp("ip", argv[i]->arg) == 0) { - SET_FLAG(pcc_flags, F_PCC_OPTS_IPV4); - i++; - if (i >= argc) - return CMD_ERR_NO_MATCH; - if (!inet_pton(AF_INET, argv[i]->arg, &pcc_addr_v4)) - return CMD_ERR_INCOMPLETE; - i++; - continue; - - } - - if (strcmp("ipv6", argv[i]->arg) == 0) { - SET_FLAG(pcc_flags, F_PCC_OPTS_IPV6); - i++; - if (i >= argc) - return CMD_ERR_NO_MATCH; - if (!inet_pton(AF_INET6, argv[i]->arg, &pcc_addr_v6)) - return CMD_ERR_INCOMPLETE; - i++; - continue; - } - - if (strcmp("port", argv[i]->arg) == 0) { - i++; - if (i >= argc) - return CMD_ERR_NO_MATCH; - pcc_port = atoi(argv[i]->arg); - if (pcc_port == 0) - return CMD_ERR_INCOMPLETE; - i++; - continue; - } - if (strcmp("msd", argv[i]->arg) == 0) { - i++; - if (i >= argc) - return CMD_ERR_NO_MATCH; - pcc_msd = atoi(argv[i]->arg); - if (pcc_msd <= 0 || pcc_msd >= 16) - return CMD_ERR_INCOMPLETE; - i++; - continue; - } - return CMD_ERR_NO_MATCH; - } - - opts = XCALLOC(MTYPE_PCEP, sizeof(*opts)); - opts->flags = pcc_flags; - opts->addr_v4 = pcc_addr_v4; - opts->addr_v6 = pcc_addr_v6; - opts->port = pcc_port; - opts->msd = pcc_msd; - - if (pcep_ctrl_update_pcc_options(pcep_g->fpt, opts)) - return CMD_WARNING; - - if (pcep_g->pcc_opts != NULL) - XFREE(MTYPE_PCEP, pcep_g->pcc_opts); - opts_copy = XCALLOC(MTYPE_PCEP, sizeof(*opts)); - opts_copy = memcpy(opts_copy, opts, sizeof(*opts)); - pcep_g->pcc_opts = opts_copy; - - VTY_PUSH_CONTEXT_NULL(PCC_NODE); - - return CMD_SUCCESS; -} - -DEFUN(pcep_cli_no_pcc, pcep_cli_no_pcc_cmd, "no pcc", - NO_STR "PCC configuration\n") -{ - pcep_ctrl_remove_pcc(pcep_g->fpt, 1); - if (pcep_g->pce_opts[0] != NULL) { - XFREE(MTYPE_PCEP, pcep_g->pce_opts[0]); - pcep_g->pce_opts[0] = NULL; - } - if (pcep_g->pcc_opts != NULL) { - XFREE(MTYPE_PCEP, pcep_g->pcc_opts); - pcep_g->pcc_opts = NULL; - } - return CMD_SUCCESS; -} - -DEFUN(pcep_cli_pce, pcep_cli_pce_cmd, - "pce [port (1024-65535)] [sr-draft07]", - "PCE configuration\n" - "PCE IPv4 address\n" - "Remote PCE server IPv4 address\n" - "PCE IPv6 address\n" - "Remote PCE server IPv6 address\n" - "Remote PCE server port\n" - "Remote PCE server port value\n" - "Use the draft 07 of PCEP segemnt routing\n") -{ - /* TODO: Add support for multiple PCE */ - - struct ipaddr pce_addr; - uint32_t pce_port = PCEP_DEFAULT_PORT; - struct pce_opts *pce_opts, *pce_opts_copy; - bool draft07 = false; - int i = 1; - - /* Get the first argument, should be either ip or ipv6 */ - pce_addr.ipa_type = IPADDR_V4; - if (strcmp("ipv6", argv[i]->arg) == 0) { - pce_addr.ipa_type = IPADDR_V6; - } else if (strcmp("ip", argv[i]->arg) != 0) { - return CMD_ERR_NO_MATCH; - } - - /* Get the first argument value */ - i++; - if (i >= argc) { - return CMD_ERR_NO_MATCH; - } - if (IS_IPADDR_V6(&pce_addr)) { - if (!inet_pton(AF_INET6, argv[i]->arg, &pce_addr.ipaddr_v6)) { - return CMD_ERR_INCOMPLETE; - } - } else { - if (!inet_pton(AF_INET, argv[i]->arg, &pce_addr.ipaddr_v4)) { - return CMD_ERR_INCOMPLETE; - } - } - - /* Handle the rest of the arguments */ - i++; - while (i < argc) { - if (strcmp("port", argv[i]->arg) == 0) { - i++; - if (i >= argc) - return CMD_ERR_NO_MATCH; - pce_port = atoi(argv[i]->arg); - if (pce_port == 0) - return CMD_ERR_INCOMPLETE; - i++; - continue; - } - if (strcmp("sr-draft07", argv[i]->arg) == 0) { - draft07 = true; - i++; - continue; - } - return CMD_ERR_NO_MATCH; - } - - pce_opts = XCALLOC(MTYPE_PCEP, sizeof(*pce_opts)); - IPADDR_COPY(&pce_opts->addr, &pce_addr); - pce_opts->port = pce_port; - pce_opts->draft07 = draft07; - - if (pcep_ctrl_update_pce_options(pcep_g->fpt, 1, pce_opts)) - return CMD_WARNING; - - if (pcep_g->pce_opts[0] != NULL) - XFREE(MTYPE_PCEP, pcep_g->pce_opts[0]); - pce_opts_copy = XCALLOC(MTYPE_PCEP, sizeof(*pce_opts)); - pce_opts_copy = memcpy(pce_opts_copy, pce_opts, sizeof(*pce_opts)); - pcep_g->pce_opts[0] = pce_opts_copy; - - return CMD_SUCCESS; -} - -DEFUN(pcep_cli_no_pce, pcep_cli_no_pce_cmd, - "no pce [port (1024-65535)]", - NO_STR - "PCE configuration\n" - "PCE IPv4 address\n" - "Remote PCE server IPv4 address\n" - "PCE IPv6 address\n" - "Remote PCE server IPv6 address\n" - "Remote PCE server port\n" - "Remote PCE server port value\n") -{ - /* TODO: Add support for multiple PCE */ - - pcep_ctrl_remove_pcc(pcep_g->fpt, 1); - if (pcep_g->pce_opts[0] != NULL) { - XFREE(MTYPE_PCEP, pcep_g->pce_opts[0]); - pcep_g->pce_opts[0] = NULL; - } - return CMD_SUCCESS; -} - -DEFUN(pcep_cli_debug, pcep_cli_debug_cmd, - "[no] debug pathd pcep [basic] [path] [message] [pceplib]", - NO_STR DEBUG_STR - "pathd debugging\n" - "pcep module debugging\n" - "module basic debugging\n" - "path structures debugging\n" - "pcep message debugging\n" - "pceplib debugging\n") -{ - uint32_t mode = DEBUG_NODE2MODE(vty->node); - bool no = strmatch(argv[0]->text, "no"); - int i; - - DEBUG_MODE_SET(&pcep_g->dbg, mode, !no); - - if (3 < argc) { - for (i = (3 + no); i < argc; i++) { - if (strcmp("basic", argv[i]->arg) == 0) { - DEBUG_FLAGS_SET(&pcep_g->dbg, - PCEP_DEBUG_MODE_BASIC, !no); - } else if (strcmp("path", argv[i]->arg) == 0) { - DEBUG_FLAGS_SET(&pcep_g->dbg, - PCEP_DEBUG_MODE_PATH, !no); - } else if (strcmp("message", argv[i]->arg) == 0) { - DEBUG_FLAGS_SET(&pcep_g->dbg, - PCEP_DEBUG_MODE_PCEP, !no); - } else if (strcmp("pceplib", argv[i]->arg) == 0) { - DEBUG_FLAGS_SET(&pcep_g->dbg, - PCEP_DEBUG_MODE_PCEPLIB, !no); - } - } - } - - return CMD_SUCCESS; -} - -int pcep_cli_debug_config_write(struct vty *vty) -{ - char buff[128] = ""; - - if (DEBUG_MODE_CHECK(&pcep_g->dbg, DEBUG_MODE_CONF)) { - if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC)) - csnprintfrr(buff, sizeof(buff), " basic"); - if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH)) - csnprintfrr(buff, sizeof(buff), " path"); - if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP)) - csnprintfrr(buff, sizeof(buff), " message"); - if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEPLIB)) - csnprintfrr(buff, sizeof(buff), " pceplib"); - vty_out(vty, "debug pathd pcep%s\n", buff); - buff[0] = 0; - return 1; - } - - return 0; -} - -int pcep_cli_debug_set_all(uint32_t flags, bool set) -{ - DEBUG_FLAGS_SET(&pcep_g->dbg, flags, set); - - /* If all modes have been turned off, don't preserve options. */ - if (!DEBUG_MODE_CHECK(&pcep_g->dbg, DEBUG_MODE_ALL)) - DEBUG_CLEAR(&pcep_g->dbg); - - return 0; -} - -int pcep_cli_pcc_config_write(struct vty *vty) -{ - struct pcc_opts *pcc_opts = pcep_g->pcc_opts; - struct pce_opts *pce_opts; - char buff[128] = ""; - int lines = 0; - - if (pcep_g->pcc_opts != NULL) { - if (CHECK_FLAG(pcc_opts->flags, F_PCC_OPTS_IPV4)) { - csnprintfrr(buff, sizeof(buff), " ip %pI4", - &pcc_opts->addr_v4); - } else if (CHECK_FLAG(pcc_opts->flags, F_PCC_OPTS_IPV4)) { - csnprintfrr(buff, sizeof(buff), " ipv6 %pI6", - &pcc_opts->addr_v6); - } - if (pcc_opts->port != PCEP_DEFAULT_PORT) - csnprintfrr(buff, sizeof(buff), " port %d", - pcc_opts->port); - if (pcc_opts->msd != PCC_DEFAULT_MSD) - csnprintfrr(buff, sizeof(buff), " msd %d", - pcc_opts->msd); - vty_out(vty, "pcc%s\n", buff); - buff[0] = 0; - lines++; - - for (int i = 0; i < MAX_PCC; i++) { - pce_opts = pcep_g->pce_opts[i]; - if (pce_opts != NULL) { - if (pce_opts->port != PCEP_DEFAULT_PORT) { - csnprintfrr(buff, sizeof(buff), - " port %d", pce_opts->port); - } - if (pce_opts->draft07 == true) { - csnprintfrr(buff, sizeof(buff), - " sr-draft07"); - } - if (IS_IPADDR_V6(&pce_opts->addr)) { - vty_out(vty, " pce ipv6 %pI6%s\n", - &pce_opts->addr.ipaddr_v6, - buff); - } else { - vty_out(vty, " pce ip %pI4%s\n", - &pce_opts->addr.ipaddr_v4, - buff); - } - buff[0] = 0; - lines++; - } - } - } - - return lines; -} - -void pcep_cli_init(void) -{ - hook_register(nb_client_debug_config_write, - pcep_cli_debug_config_write); - hook_register(nb_client_debug_set_all, pcep_cli_debug_set_all); - - install_node(&pcc_node); - install_default(PCC_NODE); - install_element(CONFIG_NODE, &pcep_cli_debug_cmd); - install_element(ENABLE_NODE, &pcep_cli_debug_cmd); - install_element(ENABLE_NODE, &show_pcep_counters_cmd); - install_element(CONFIG_NODE, &pcep_cli_pcc_cmd); - install_element(CONFIG_NODE, &pcep_cli_no_pcc_cmd); - install_element(PCC_NODE, &pcep_cli_pce_cmd); - install_element(PCC_NODE, &pcep_cli_no_pce_cmd); -} - /* ------------ Module Functions ------------ */ int pcep_module_late_init(struct thread_master *tm) @@ -697,8 +264,8 @@ int pcep_module_finish(void) if (pcep_g->pcc_opts != NULL) XFREE(MTYPE_PCEP, pcep_g->pcc_opts); for (int i = 0; i < MAX_PCC; i++) - if (pcep_g->pce_opts[i] != NULL) - XFREE(MTYPE_PCEP, pcep_g->pce_opts[i]); + if (pcep_g->pce_opts_cli[i] != NULL) + XFREE(MTYPE_PCEP, pcep_g->pce_opts_cli[i]); return 0; } @@ -706,8 +273,12 @@ int pcep_module_finish(void) int pcep_module_init(void) { pcep_g->pcc_opts = NULL; - for (int i = 0; i < MAX_PCC; i++) - pcep_g->pce_opts[i] = NULL; + pcep_g->num_pce_opts_cli = 0; + for (int i = 0; i < MAX_PCE; i++) + pcep_g->pce_opts_cli[i] = NULL; + pcep_g->num_config_group_opts = 0; + for (int i = 0; i < MAX_PCE; i++) + pcep_g->config_group_opts[i] = NULL; hook_register(frr_late_init, pcep_module_late_init); return 0; diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h index 7fc1e9400d44..32359e6fb9ad 100644 --- a/pathd/path_pcep.h +++ b/pathd/path_pcep.h @@ -22,15 +22,16 @@ #include #include +#include #include #include #include "mpls.h" #include "pathd/pathd.h" #include "pathd/path_pcep_memory.h" -#define PCC_DEFAULT_MSD 4 #define PCEP_DEFAULT_PORT 4189 #define MAX_PCC 1 +#define MAX_PCE 100 #define MAX_TAG_SIZE 50 #define PCEP_DEBUG_MODE_BASIC 0x01 #define PCEP_DEBUG_MODE_PATH 0x02 @@ -80,18 +81,47 @@ } \ } while (0) +struct pcep_config_group_opts { + char name[64]; + char tcp_md5_auth[TCP_MD5SIG_MAXKEYLEN]; + bool draft07; + bool pce_initiated; + int keep_alive_seconds; + int min_keep_alive_seconds; + int max_keep_alive_seconds; + int dead_timer_seconds; + int min_dead_timer_seconds; + int max_dead_timer_seconds; + int pcep_request_time_seconds; + int state_timeout_inteval_seconds; +}; + struct pce_opts { struct ipaddr addr; short port; - bool draft07; + char pce_name[64]; + struct pcep_config_group_opts config_opts; + uint8_t precedence; /* Multi-PCE precedence */ +}; + +/* Encapsulate the pce_opts with needed CLI information */ +struct pce_opts_cli { + struct pce_opts pce_opts; + char config_group_name[64]; + /* These are the values configured in the pcc-peer sub-commands. + * These need to be stored for later merging. Notice, it could + * be that not all of them are set. */ + struct pcep_config_group_opts pce_config_group_opts; + /* The pce_opts->config_opts will be a merge of the default values, + * optional config_group values (which overwrite default values), + * and any values configured in the pce sub-commands (which overwrite + * both default and config_group values). This flag indicates of the + * values need to be merged or not. */ + bool merged; }; struct pcc_opts { - uint16_t flags; -#define F_PCC_OPTS_IPV4 0x0002 -#define F_PCC_OPTS_IPV6 0x0004 - struct in_addr addr_v4; - struct in6_addr addr_v6; + struct ipaddr addr; short port; short msd; }; @@ -245,7 +275,10 @@ struct pcep_glob { struct frr_pthread *fpt; /* Copy of the PCC/PCE configurations for display purpose */ struct pcc_opts *pcc_opts; - struct pce_opts *pce_opts[MAX_PCC]; + uint8_t num_pce_opts_cli; + struct pce_opts_cli *pce_opts_cli[MAX_PCE]; + uint8_t num_config_group_opts; + struct pcep_config_group_opts *config_group_opts[MAX_PCE]; }; extern struct pcep_glob *pcep_g; diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c new file mode 100644 index 000000000000..c09e0b9965f2 --- /dev/null +++ b/pathd/path_pcep_cli.c @@ -0,0 +1,1416 @@ +/* + * Copyright (C) 2020 Volta Networks, Inc + * Brady Johnson + * + * 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 +#include + +#include "log.h" +#include "command.h" +#include "libfrr.h" +#include "printfrr.h" +#include "version.h" +#include "northbound.h" +#include "frr_pthread.h" +#include "jhash.h" +#include "termtable.h" + +#include "pathd/pathd.h" +#include "pathd/path_util.h" +#include "pathd/path_errors.h" +#include "pathd/path_pcep_memory.h" +#include "pathd/path_pcep.h" +#include "pathd/path_pcep_cli.h" +#include "pathd/path_pcep_controller.h" +#include "pathd/path_pcep_lib.h" +#include "pathd/path_pcep_nb.h" +#include "pathd/path_pcep_cli_clippy.c" + +#define DEFAULT_PCC_MSD 4 +#define DEFAULT_SR_DRAFT07 false +#define DEFAULT_PCE_INITIATED false +#define DEFAULT_TIMER_KEEP_ALIVE 30 +#define DEFAULT_TIMER_KEEP_ALIVE_MIN 1 +#define DEFAULT_TIMER_KEEP_ALIVE_MAX 120 +#define DEFAULT_TIMER_DEADTIMER 120 +#define DEFAULT_TIMER_DEADTIMER_MIN 60 +#define DEFAULT_TIMER_DEADTIMER_MAX 240 +#define DEFAULT_TIMER_PCEP_REQUEST 30 +#define DEFAULT_TIMER_TIMEOUT_INTERVAL 30 + +/* CLI Function declarations */ +static int pcep_cli_debug_config_write(struct vty *vty); +static int pcep_cli_debug_set_all(uint32_t flags, bool set); +static int pcep_cli_pcc_config_write(struct vty *vty); +static int pcep_cli_pcc_peer_config_write(struct vty *vty); +static int pcep_cli_pcep_config_group_write(struct vty *vty); + +/* Internal Util Function declarations */ +static struct pce_opts_cli *pcep_cli_find_pce(const char *pce_name); +static bool pcep_cli_add_pce(struct pce_opts_cli *pce_opts_cli); +static struct pce_opts_cli *pcep_cli_create_pce_opts(); +static void pcep_cli_delete_pce(const char *pce_name); +static void +pcep_cli_merge_pcep_config_group_options(struct pce_opts_cli *pce_opts_cli); +static struct pcep_config_group_opts * +pcep_cli_find_pcep_config_group(const char *group_name); +static bool pcep_cli_add_pcep_config_group( + struct pcep_config_group_opts *config_group_opts); +static struct pcep_config_group_opts * +pcep_cli_create_pcep_config_group(const char *group_name); +static bool pcep_cli_is_pcep_config_group_used(const char *group_name); +static void pcep_cli_delete_pcep_config_group(const char *group_name); +static int +pcep_cli_print_config_group(struct pcep_config_group_opts *group_opts, + char *buf); + +/* + * Globals. + */ + +static const char PCEP_VTYSH_ARG_ADDRESS[] = "address"; +static const char PCEP_VTYSH_ARG_IP[] = "ip"; +static const char PCEP_VTYSH_ARG_IPV6[] = "ipv6"; +static const char PCEP_VTYSH_ARG_PORT[] = "port"; +static const char PCEP_VTYSH_ARG_PRECEDENCE[] = "precedence"; +static const char PCEP_VTYSH_ARG_MSD[] = "msd"; +static const char PCEP_VTYSH_ARG_KEEP_ALIVE[] = "keep-alive"; +static const char PCEP_VTYSH_ARG_KEEP_ALIVE_MIN[] = "min-peer-keep-alive"; +static const char PCEP_VTYSH_ARG_KEEP_ALIVE_MAX[] = "max-peer-keep-alive"; +static const char PCEP_VTYSH_ARG_DEAD_TIMER[] = "dead-timer"; +static const char PCEP_VTYSH_ARG_DEAD_TIMER_MIN[] = "min-peer-dead-timer"; +static const char PCEP_VTYSH_ARG_DEAD_TIMER_MAX[] = "max-peer-dead-timer"; +static const char PCEP_VTYSH_ARG_PCEP_REQUEST[] = "pcep-request"; +static const char PCEP_VTYSH_ARG_STATE_TIMEOUT[] = "state-timeout-interval"; +static const char PCEP_VTYSH_ARG_SR_DRAFT07[] = "sr-draft07"; +static const char PCEP_VTYSH_ARG_PCE_INIT[] = "pce-inititated"; +static const char PCEP_VTYSH_ARG_TCP_MD5[] = "tcp-md5-auth"; +static const char PCEP_VTYSH_ARG_BASIC[] = "basic"; +static const char PCEP_VTYSH_ARG_PATH[] = "path"; +static const char PCEP_VTYSH_ARG_MESSAGE[] = "message"; +static const char PCEP_VTYSH_ARG_PCEPLIB[] = "pceplib"; + +/* Default PCE group that all PCE-Groups and PCEs will inherit from */ +struct pcep_config_group_opts default_pcep_config_group_opts = { + .name = "default", + .tcp_md5_auth = "\0", + .draft07 = DEFAULT_SR_DRAFT07, + .pce_initiated = DEFAULT_PCE_INITIATED, + .keep_alive_seconds = DEFAULT_TIMER_KEEP_ALIVE, + .min_keep_alive_seconds = DEFAULT_TIMER_KEEP_ALIVE_MIN, + .max_keep_alive_seconds = DEFAULT_TIMER_KEEP_ALIVE_MAX, + .dead_timer_seconds = DEFAULT_TIMER_DEADTIMER, + .min_dead_timer_seconds = DEFAULT_TIMER_DEADTIMER_MIN, + .max_dead_timer_seconds = DEFAULT_TIMER_DEADTIMER_MAX, + .pcep_request_time_seconds = DEFAULT_TIMER_PCEP_REQUEST, + .state_timeout_inteval_seconds = DEFAULT_TIMER_TIMEOUT_INTERVAL, +}; + +/* Used by PCE_GROUP_NODE sub-commands to operate on the current pce group */ +struct pcep_config_group_opts *current_pcep_config_group_opts_g = NULL; +/* Used by PCC_PEER_NODE sub-commands to operate on the current pce opts */ +struct pce_opts_cli *current_pce_opts_g = NULL; + +static struct cmd_node pcc_node = {.name = "pcep_pcc_node", + .node = PCC_NODE, + .parent_node = CONFIG_NODE, + .config_write = pcep_cli_pcc_config_write, + .prompt = "%s(config-pcc)# "}; +static struct cmd_node pcc_peer_node = {.name = "pcep_pcc_peer_node", + .node = PCC_PEER_NODE, + .parent_node = CONFIG_NODE, + .config_write = + pcep_cli_pcc_peer_config_write, + .prompt = "%s(config-pcc-peer)# "}; +static struct cmd_node pcep_config_group_node = { + .name = "pcep_pcep_config_group_node", + .node = PCEP_CONFIG_GROUP_NODE, + .parent_node = CONFIG_NODE, + .config_write = pcep_cli_pcep_config_group_write, + .prompt = "%s(pce-config-group)# "}; + +/* Common code used in VTYSH processing for int values */ +#define PCEP_VTYSH_INT_ARG_CHECK(arg_str, arg_val, arg_store, min_value, \ + max_value) \ + if (arg_str != NULL) { \ + if (arg_val <= min_value || arg_val >= max_value) { \ + vty_out(vty, \ + "%% Invalid value %ld in range [%d - %d]", \ + arg_val, min_value, max_value); \ + return CMD_WARNING; \ + } \ + arg_store = arg_val; \ + } + +#define MERGE_COMPARE_CONFIG_GROUP_VALUE(config_param, not_set_value) \ + pce_opts_cli->pce_opts.config_opts.config_param = \ + pce_opts_cli->pce_config_group_opts.config_param; \ + if (pce_opts_cli->pce_config_group_opts.config_param \ + == not_set_value) { \ + pce_opts_cli->pce_opts.config_opts.config_param = \ + ((config_group != NULL \ + && config_group->config_param != not_set_value) \ + ? config_group->config_param \ + : default_pcep_config_group_opts \ + .config_param); \ + } + +/* + * Internal Util functions + */ + +/* Check if a pce_opts_cli already exists based on its name and return it, + * return NULL otherwise */ +static struct pce_opts_cli *pcep_cli_find_pce(const char *pce_name) +{ + for (int i = 0; i < MAX_PCE; i++) { + struct pce_opts_cli *pce_rhs_cli = pcep_g->pce_opts_cli[i]; + if (pce_rhs_cli != NULL) { + if (strcmp(pce_name, pce_rhs_cli->pce_opts.pce_name) + == 0) { + return pce_rhs_cli; + } + } + } + + return NULL; +} + +/* Add a new pce_opts_cli to pcep_g, return false if MAX_PCES, true otherwise */ +static bool pcep_cli_add_pce(struct pce_opts_cli *pce_opts_cli) +{ + for (int i = 0; i < MAX_PCE; i++) { + if (pcep_g->pce_opts_cli[i] == NULL) { + pcep_g->pce_opts_cli[i] = pce_opts_cli; + pcep_g->num_pce_opts_cli++; + return true; + } + } + + return false; +} + +/* Create a new pce opts_cli */ +static struct pce_opts_cli *pcep_cli_create_pce_opts(const char *name) +{ + struct pce_opts_cli *pce_opts_cli = + XCALLOC(MTYPE_PCEP, sizeof(struct pce_opts_cli)); + strcpy(pce_opts_cli->pce_opts.pce_name, name); + pce_opts_cli->pce_opts.port = PCEP_DEFAULT_PORT; + + return pce_opts_cli; +} + +static void pcep_cli_delete_pce(const char *pce_name) +{ + for (int i = 0; i < MAX_PCE; i++) { + if (pcep_g->pce_opts_cli[i] != NULL) { + if (strcmp(pcep_g->pce_opts_cli[i]->pce_opts.pce_name, + pce_name) + == 0) { + XFREE(MTYPE_PCEP, pcep_g->pce_opts_cli[i]); + pcep_g->pce_opts_cli[i] = NULL; + pcep_g->num_pce_opts_cli--; + return; + } + } + } +} + +static void +pcep_cli_merge_pcep_config_group_options(struct pce_opts_cli *pce_opts_cli) +{ + if (pce_opts_cli->merged == true) { + return; + } + + struct pcep_config_group_opts *config_group = + pcep_cli_find_pcep_config_group( + pce_opts_cli->config_group_name); + + /* Configuration priorities: + * 1) pce_opts->config_opts, if present, overwrite config_group + * config_opts 2) config_group config_opts, if present, overwrite + * default config_opts 3) If neither pce_opts->config_opts nor + * config_group config_opts are set, then the default config_opts value + * will be used. + */ + + const char *tcp_md5_auth_str = + pce_opts_cli->pce_opts.config_opts.tcp_md5_auth; + if (pce_opts_cli->pce_opts.config_opts.tcp_md5_auth[0] == '\0') { + if (config_group != NULL + && config_group->tcp_md5_auth[0] != '\0') { + tcp_md5_auth_str = config_group->tcp_md5_auth; + } else { + tcp_md5_auth_str = + default_pcep_config_group_opts.tcp_md5_auth; + } + } + strncpy(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth, + tcp_md5_auth_str, TCP_MD5SIG_MAXKEYLEN); + + MERGE_COMPARE_CONFIG_GROUP_VALUE(draft07, false); + MERGE_COMPARE_CONFIG_GROUP_VALUE(pce_initiated, false); + MERGE_COMPARE_CONFIG_GROUP_VALUE(keep_alive_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(min_keep_alive_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(max_keep_alive_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(dead_timer_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(min_dead_timer_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(max_dead_timer_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(pcep_request_time_seconds, 0); + MERGE_COMPARE_CONFIG_GROUP_VALUE(state_timeout_inteval_seconds, 0); + + pce_opts_cli->merged = true; +} + +/* Check if a pcep_config_group_opts already exists based on its name and return + * it, return NULL otherwise */ +static struct pcep_config_group_opts * +pcep_cli_find_pcep_config_group(const char *group_name) +{ + for (int i = 0; i < MAX_PCE; i++) { + struct pcep_config_group_opts *pcep_config_group_rhs = + pcep_g->config_group_opts[i]; + if (pcep_config_group_rhs != NULL) { + if (strcmp(group_name, pcep_config_group_rhs->name) + == 0) { + return pcep_config_group_rhs; + } + } + } + + return NULL; +} + +/* Add a new pcep_config_group_opts to pcep_g, return false if MAX_PCE, + * true otherwise */ +static bool pcep_cli_add_pcep_config_group( + struct pcep_config_group_opts *pcep_config_group_opts) +{ + for (int i = 0; i < MAX_PCE; i++) { + if (pcep_g->config_group_opts[i] == NULL) { + pcep_g->config_group_opts[i] = pcep_config_group_opts; + pcep_g->num_config_group_opts++; + return true; + } + } + + return false; +} + +/* Create a new pce group, inheriting its values from the default pce group */ +static struct pcep_config_group_opts * +pcep_cli_create_pcep_config_group(const char *group_name) +{ + struct pcep_config_group_opts *pcep_config_group_opts = + XCALLOC(MTYPE_PCEP, sizeof(struct pcep_config_group_opts)); + strcpy(pcep_config_group_opts->name, group_name); + + return pcep_config_group_opts; +} + +/* Iterate the pce_opts and return true if the pce-group-name is referenced, + * false otherwise. */ +static bool pcep_cli_is_pcep_config_group_used(const char *group_name) +{ + for (int i = 0; i < MAX_PCE; i++) { + if (pcep_g->pce_opts_cli[i] != NULL) { + if (strcmp(pcep_g->pce_opts_cli[i]->config_group_name, + group_name) + == 0) { + return true; + } + } + } + + return false; +} + +static void pcep_cli_delete_pcep_config_group(const char *group_name) +{ + for (int i = 0; i < MAX_PCE; i++) { + if (pcep_g->config_group_opts[i] != NULL) { + if (strcmp(pcep_g->config_group_opts[i]->name, + group_name) + == 0) { + XFREE(MTYPE_PCEP, pcep_g->config_group_opts[i]); + pcep_g->config_group_opts[i] = NULL; + pcep_g->num_config_group_opts--; + return; + } + } + } +} + +/* + * VTY command implementations + */ + +static int path_pcep_cli_debug(struct vty *vty, const char *no_str, + const char *basic_str, const char *path_str, + const char *message_str, const char *pceplib_str) +{ + uint32_t mode = DEBUG_NODE2MODE(vty->node); + bool no = (no_str != NULL); + + DEBUG_MODE_SET(&pcep_g->dbg, mode, !no); + + if (basic_str != NULL) { + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC, !no); + } + if (path_str != NULL) { + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH, !no); + } + if (message_str != NULL) { + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP, !no); + } + if (pceplib_str != NULL) { + DEBUG_FLAGS_SET(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEPLIB, !no); + } + + return CMD_SUCCESS; +} + +static int path_pcep_cli_show_pcep_counters(struct vty *vty) +{ + int i, j, row; + time_t diff_time; + struct tm *tm_info; + char tm_buffer[26]; + struct counters_group *group; + struct counters_subgroup *subgroup; + struct counter *counter; + const char *group_name, *empty_string = ""; + struct ttable *tt; + char *table; + + group = pcep_ctrl_get_counters(pcep_g->fpt, 1); + + if (group == NULL) { + vty_out(vty, "No counters to display.\n\n"); + return CMD_SUCCESS; + } + + diff_time = time(NULL) - group->start_time; + tm_info = localtime(&group->start_time); + strftime(tm_buffer, sizeof(tm_buffer), "%Y-%m-%d %H:%M:%S", tm_info); + + vty_out(vty, "PCEP counters since %s (%luh %lum %lus):\n", tm_buffer, + diff_time / 3600, (diff_time / 60) % 60, diff_time % 60); + + /* Prepare table. */ + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "Group|Name|Value"); + tt->style.cell.rpad = 2; + tt->style.corner = '+'; + ttable_restyle(tt); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + for (row = 0, i = 0; i <= group->num_subgroups; i++) { + subgroup = group->subgroups[i]; + if (subgroup != NULL) { + group_name = subgroup->counters_subgroup_name; + for (j = 0; j <= subgroup->num_counters; j++) { + counter = subgroup->counters[j]; + if (counter != NULL) { + ttable_add_row(tt, "%s|%s|%u", + group_name, + counter->counter_name, + counter->counter_value); + row++; + group_name = empty_string; + } + } + ttable_rowseps(tt, row, BOTTOM, true, '-'); + } + } + + /* Dump the generated table. */ + table = ttable_dump(tt, "\n"); + vty_out(vty, "%s\n", table); + XFREE(MTYPE_TMP, table); + + ttable_del(tt); + + pcep_lib_free_counters(group); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcep_config_group(struct vty *vty, + const char *pcep_config_group) +{ + struct pcep_config_group_opts *config_group = + pcep_cli_find_pcep_config_group(pcep_config_group); + if (config_group == NULL) { + config_group = + pcep_cli_create_pcep_config_group(pcep_config_group); + } else { + vty_out(vty, + "Notice: changes to this pce-config-group will not affect PCEs already configured with this group\n"); + } + + if (pcep_cli_add_pcep_config_group(config_group) == false) { + vty_out(vty, + "%% Cannot create pce-config-group, as the Maximum limit of %d pce-config-groups has been reached.\n", + MAX_PCE); + XFREE(MTYPE_PCEP, config_group); + return CMD_WARNING; + } + + current_pcep_config_group_opts_g = config_group; + vty->node = PCEP_CONFIG_GROUP_NODE; + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcep_config_group_delete(struct vty *vty, + const char *pcep_config_group) +{ + struct pcep_config_group_opts *config_group = + pcep_cli_find_pcep_config_group(pcep_config_group); + if (config_group == NULL) { + vty_out(vty, + "%% Cannot delete pce-config-group, since it does not exist.\n"); + return CMD_WARNING; + } + + if (pcep_cli_is_pcep_config_group_used(config_group->name)) { + vty_out(vty, + "%% Cannot delete pce-config-group, since it is in use by a peer.\n"); + return CMD_WARNING; + } + + pcep_cli_delete_pcep_config_group(config_group->name); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_show_pcep_config_group(struct vty *vty, + const char *pcep_config_group) +{ + char buf[1024] = ""; + + /* Only show 1 Peer config group */ + struct pcep_config_group_opts *group_opts; + if (pcep_config_group != NULL) { + if (strcmp(pcep_config_group, "default") == 0) { + group_opts = &default_pcep_config_group_opts; + } else { + group_opts = pcep_cli_find_pcep_config_group( + pcep_config_group); + } + if (group_opts == NULL) { + vty_out(vty, + "%% peer-config-group [%s] does not exist.\n", + pcep_config_group); + return CMD_WARNING; + } + + vty_out(vty, "peer-config-group: %s\n", group_opts->name); + pcep_cli_print_config_group(group_opts, buf); + vty_out(vty, "%s", buf); + return CMD_SUCCESS; + } + + /* Show all Peer config groups */ + for (int i = 0; i < MAX_PCE; i++) { + group_opts = pcep_g->config_group_opts[i]; + if (group_opts == NULL) { + continue; + } + + vty_out(vty, "peer-config-group: %s\n", group_opts->name); + pcep_cli_print_config_group(group_opts, buf); + vty_out(vty, "%s", buf); + buf[0] = 0; + } + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc_peer(struct vty *vty, const char *pcc_peer_name) +{ + /* If it already exists, it will be updated in the sub-commands */ + struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(pcc_peer_name); + if (pce_opts_cli == NULL) { + pce_opts_cli = pcep_cli_create_pce_opts(pcc_peer_name); + + if (!pcep_cli_add_pce(pce_opts_cli)) { + vty_out(vty, + "%% Cannot create PCE, as the Maximum limit of %d PCEs has been reached.\n", + MAX_PCE); + XFREE(MTYPE_PCEP, pce_opts_cli); + return CMD_WARNING; + } + } + + current_pce_opts_g = pce_opts_cli; + vty->node = PCC_PEER_NODE; + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc_peer_delete(struct vty *vty, + const char *pcc_peer_name) +{ + struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(pcc_peer_name); + if (pce_opts_cli == NULL) { + vty_out(vty, "%% PCC peer does not exist.\n"); + return CMD_WARNING; + } + + if (pcep_ctrl_pcc_has_pce(pcep_g->fpt, pcc_peer_name)) { + vty_out(vty, + "%% Cannot delete PCC peer, since it is in use by a PCC.\n"); + return CMD_WARNING; + } + + pcep_cli_delete_pce(pcc_peer_name); + + return CMD_SUCCESS; +} + +/* Internal Util func to show an individual PCE, + * only used by path_pcep_cli_show_pcc_peer() */ +static void show_pcc_peer(struct vty *vty, struct pce_opts_cli *pce_opts_cli) +{ + struct pce_opts *pce_opts = &pce_opts_cli->pce_opts; + vty_out(vty, "PCC Peer: %s\n", pce_opts->pce_name); + if (IS_IPADDR_V6(&pce_opts->addr)) { + vty_out(vty, " %s %s %pI6 %s %d\n", PCEP_VTYSH_ARG_ADDRESS, + PCEP_VTYSH_ARG_IPV6, &pce_opts->addr.ipaddr_v6, + PCEP_VTYSH_ARG_PORT, pce_opts->port); + } else { + vty_out(vty, " %s %s %pI4 %s %d\n", PCEP_VTYSH_ARG_ADDRESS, + PCEP_VTYSH_ARG_IP, &pce_opts->addr.ipaddr_v4, + PCEP_VTYSH_ARG_PORT, pce_opts->port); + } + if (pce_opts_cli->config_group_name[0] != '\0') { + vty_out(vty, " peer-config-group: %s\n", + pce_opts_cli->config_group_name); + } + + char buf[1024] = ""; + pcep_cli_print_config_group(&pce_opts->config_opts, buf); + vty_out(vty, "%s", buf); +} + +static int path_pcep_cli_show_pcc_peer(struct vty *vty, const char *pcc_peer) +{ + /* Only show 1 PCE */ + struct pce_opts_cli *pce_opts_cli; + if (pcc_peer != NULL) { + pce_opts_cli = pcep_cli_find_pce(pcc_peer); + if (pce_opts_cli == NULL) { + vty_out(vty, "%% PCE [%s] does not exist.\n", pcc_peer); + return CMD_WARNING; + } + + pcep_cli_merge_pcep_config_group_options(pce_opts_cli); + show_pcc_peer(vty, pce_opts_cli); + + return CMD_SUCCESS; + } + + /* Show all PCEs */ + for (int i = 0; i < MAX_PCE; i++) { + pce_opts_cli = pcep_g->pce_opts_cli[i]; + if (pce_opts_cli == NULL) { + continue; + } + + pcep_cli_merge_pcep_config_group_options(pce_opts_cli); + show_pcc_peer(vty, pce_opts_cli); + } + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_sr_draft07(struct vty *vty) +{ + struct pcep_config_group_opts *config_group = NULL; + + if (vty->node == PCC_PEER_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + config_group = ¤t_pce_opts_g->pce_opts.config_opts; + current_pce_opts_g->merged = false; + } else if (vty->node == PCEP_CONFIG_GROUP_NODE) { + config_group = current_pcep_config_group_opts_g; + } else { + return CMD_ERR_NO_MATCH; + } + + config_group->draft07 = true; + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_pce_initiated(struct vty *vty) +{ + struct pcep_config_group_opts *config_group = NULL; + + if (vty->node == PCC_PEER_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + config_group = ¤t_pce_opts_g->pce_opts.config_opts; + current_pce_opts_g->merged = false; + } else if (vty->node == PCEP_CONFIG_GROUP_NODE) { + config_group = current_pcep_config_group_opts_g; + } else { + return CMD_ERR_NO_MATCH; + } + + config_group->pce_initiated = true; + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_tcp_md5_auth(struct vty *vty, + const char *tcp_md5_auth) +{ + struct pcep_config_group_opts *config_group = NULL; + + if (vty->node == PCC_PEER_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + config_group = ¤t_pce_opts_g->pce_opts.config_opts; + current_pce_opts_g->merged = false; + } else if (vty->node == PCEP_CONFIG_GROUP_NODE) { + config_group = current_pcep_config_group_opts_g; + } else { + return CMD_ERR_NO_MATCH; + } + + strncpy(config_group->tcp_md5_auth, tcp_md5_auth, TCP_MD5SIG_MAXKEYLEN); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_address(struct vty *vty, const char *ip_str, + struct in_addr *ip, const char *ipv6_str, + struct in6_addr *ipv6, + const char *port_str, long port) +{ + struct pce_opts *pce_opts = NULL; + if (vty->node == PCC_PEER_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + pce_opts = ¤t_pce_opts_g->pce_opts; + current_pce_opts_g->merged = false; + } else { + return CMD_ERR_NO_MATCH; + } + + if (ipv6_str != NULL) { + pce_opts->addr.ipa_type = IPADDR_V6; + memcpy(&pce_opts->addr.ipaddr_v6, ipv6, + sizeof(struct in6_addr)); + } else if (ip_str != NULL) { + pce_opts->addr.ipa_type = IPADDR_V4; + memcpy(&pce_opts->addr.ipaddr_v4, ip, sizeof(struct in_addr)); + } else { + return CMD_ERR_NO_MATCH; + } + + /* Handle the optional port */ + pce_opts->port = PCEP_DEFAULT_PORT; + PCEP_VTYSH_INT_ARG_CHECK(port_str, port, pce_opts->port, 0, 65535); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_pcep_config_group(struct vty *vty, + const char *config_group_name) +{ + if (vty->node == PCC_PEER_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + current_pce_opts_g->merged = false; + } else { + return CMD_ERR_NO_MATCH; + } + + struct pcep_config_group_opts *config_group = + pcep_cli_find_pcep_config_group(config_group_name); + if (config_group == NULL) { + vty_out(vty, "%% pce-config-group [%s] does not exist.\n", + config_group_name); + return CMD_WARNING; + } + + strcpy(current_pce_opts_g->config_group_name, config_group_name); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_peer_timers( + struct vty *vty, const char *keep_alive_str, long keep_alive, + const char *min_peer_keep_alive_str, long min_peer_keep_alive, + const char *max_peer_keep_alive_str, long max_peer_keep_alive, + const char *dead_timer_str, long dead_timer, + const char *min_peer_dead_timer_str, long min_peer_dead_timer, + const char *max_peer_dead_timer_str, long max_peer_dead_timer, + const char *pcep_request_str, long pcep_request, + const char *state_timeout_interval_str, long state_timeout_interval) +{ + struct pcep_config_group_opts *config_group = NULL; + if (vty->node == PCC_PEER_NODE) { + /* TODO need to see if the pce is in use, and reset the + * connection */ + config_group = ¤t_pce_opts_g->pce_config_group_opts; + current_pce_opts_g->merged = false; + } else if (vty->node == PCEP_CONFIG_GROUP_NODE) { + config_group = current_pcep_config_group_opts_g; + } else { + return CMD_ERR_NO_MATCH; + } + + /* Handle the arguments */ + PCEP_VTYSH_INT_ARG_CHECK(keep_alive_str, keep_alive, + config_group->keep_alive_seconds, 0, 241); + PCEP_VTYSH_INT_ARG_CHECK(min_peer_keep_alive_str, min_peer_keep_alive, + config_group->min_keep_alive_seconds, 0, 61); + PCEP_VTYSH_INT_ARG_CHECK(max_peer_keep_alive_str, max_peer_keep_alive, + config_group->max_keep_alive_seconds, 59, 241); + PCEP_VTYSH_INT_ARG_CHECK(dead_timer_str, dead_timer, + config_group->dead_timer_seconds, 0, 241); + PCEP_VTYSH_INT_ARG_CHECK(min_peer_dead_timer_str, min_peer_dead_timer, + config_group->min_dead_timer_seconds, 0, 61); + PCEP_VTYSH_INT_ARG_CHECK(max_peer_dead_timer_str, max_peer_dead_timer, + config_group->max_dead_timer_seconds, 59, 241); + PCEP_VTYSH_INT_ARG_CHECK(pcep_request_str, pcep_request, + config_group->pcep_request_time_seconds, 0, + 121); + PCEP_VTYSH_INT_ARG_CHECK( + state_timeout_interval_str, state_timeout_interval, + config_group->state_timeout_inteval_seconds, 0, 121); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc(struct vty *vty, const char *ip_str, + struct in_addr *ip, const char *ipv6_str, + struct in6_addr *ipv6, const char *port_str, + long port, const char *msd_str, long msd) +{ + struct pcc_opts local_opts, *opts, *opts_copy; + + memset(&local_opts, 0, sizeof(local_opts)); + local_opts.port = PCEP_DEFAULT_PORT; + local_opts.msd = DEFAULT_PCC_MSD; + + /* Handle the rest of the arguments */ + if (ip_str != NULL) { + SET_IPADDR_V4(&local_opts.addr); + memcpy(&local_opts.addr.ipaddr_v4, ip, sizeof(struct in_addr)); + } else if (ipv6_str != NULL) { + SET_IPADDR_V6(&local_opts.addr); + memcpy(&local_opts.addr.ipaddr_v6, ipv6, + sizeof(struct in6_addr)); + } + + PCEP_VTYSH_INT_ARG_CHECK(port_str, port, local_opts.port, 0, 65535); + PCEP_VTYSH_INT_ARG_CHECK(msd_str, msd, local_opts.msd, 0, 16); + + /* This copy of the opts is sent to the pcep controller thread */ + opts = XCALLOC(MTYPE_PCEP, sizeof(*opts)); + memcpy(opts, &local_opts, sizeof(*opts)); + + if (pcep_ctrl_update_pcc_options(pcep_g->fpt, opts)) { + return CMD_WARNING; + } + + /* This copy of the opts is stored in the global opts */ + if (pcep_g->pcc_opts != NULL) { + XFREE(MTYPE_PCEP, pcep_g->pcc_opts); + } + opts_copy = XCALLOC(MTYPE_PCEP, sizeof(*opts)); + opts_copy = memcpy(opts_copy, opts, sizeof(*opts)); + pcep_g->pcc_opts = opts_copy; + + VTY_PUSH_CONTEXT_NULL(PCC_NODE); + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc_delete(struct vty *vty, const char *ip_str, + struct in_addr *ip, const char *ipv6_str, + struct in6_addr *ipv6, const char *port_str, + long port, const char *msd_str, long msd) +{ + pcep_ctrl_remove_pcc(pcep_g->fpt, 1); + if (pcep_g->pcc_opts != NULL) { + XFREE(MTYPE_PCEP, pcep_g->pcc_opts); + pcep_g->pcc_opts = NULL; + } + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc_pcc_peer(struct vty *vty, const char *peer_name, + const char *precedence_str, + long precedence) +{ + /* Check if the pcc-peer exists */ + struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(peer_name); + if (pce_opts_cli == NULL) { + vty_out(vty, "%% PCE [%s] does not exist.\n", peer_name); + return CMD_WARNING; + } + struct pce_opts *pce_opts = &pce_opts_cli->pce_opts; + + /* Check if the pcc-peer is duplicated */ + if (pcep_ctrl_pcc_has_pce(pcep_g->fpt, peer_name)) { + vty_out(vty, "%% The peer [%s] has already been configured.\n", + peer_name); + return CMD_WARNING; + } + + /* Get the optional precedence argument */ + PCEP_VTYSH_INT_ARG_CHECK(precedence_str, precedence, + pce_opts->precedence, 1, 65535); + + /* Finalize the pce_opts config values */ + pcep_cli_merge_pcep_config_group_options(pce_opts_cli); + + /* Verify the PCE has the IP set */ + struct in6_addr zero_v6_addr; + memset(&zero_v6_addr, 0, sizeof(struct in6_addr)); + if (memcmp(&pce_opts->addr.ip, &zero_v6_addr, IPADDRSZ(&pce_opts->addr)) + == 0) { + vty_out(vty, + "%% The peer [%s] does not have an IP set and cannot be used until it does.\n", + peer_name); + return CMD_WARNING; + } + + int num_pce = pcep_ctrl_pcc_num_pce(pcep_g->fpt); + /* TODO when Multi-PCE is added, remove this check */ + if (num_pce > 0) { + vty_out(vty, "%% Multi-pce is not supported yet"); + return CMD_WARNING; + } + + /* The PCC will use a copy of the pce_opts, which is used for CLI only + */ + struct pce_opts *pce_opts_copy = + XMALLOC(MTYPE_PCEP, sizeof(struct pce_opts)); + memcpy(pce_opts_copy, pce_opts, sizeof(struct pce_opts)); + if (pcep_ctrl_update_pce_options(pcep_g->fpt, num_pce + 1, + pce_opts_copy)) { + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +static int path_pcep_cli_pcc_pcc_peer_delete(struct vty *vty, + const char *peer_name, + const char *precedence_str, + long precedence) +{ + /* Check if the pcc-peer is connected to the PCC */ + if (!pcep_ctrl_pcc_has_pce(pcep_g->fpt, peer_name)) { + vty_out(vty, "%% The peer [%s] is not connected to the PCC.\n", + peer_name); + return CMD_WARNING; + } + + /* TODO when Multi-PCE is added, the PCC Id needs to be set + * correctly based on the pcc-peer being removed */ + pcep_ctrl_remove_pcc(pcep_g->fpt, 1); + + return CMD_SUCCESS; +} + +/* + * Config Write functions + */ + +int pcep_cli_debug_config_write(struct vty *vty) +{ + char buff[128] = ""; + + if (DEBUG_MODE_CHECK(&pcep_g->dbg, DEBUG_MODE_CONF)) { + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC)) + csnprintfrr(buff, sizeof(buff), " %s", + PCEP_VTYSH_ARG_BASIC); + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH)) + csnprintfrr(buff, sizeof(buff), " %s", + PCEP_VTYSH_ARG_PATH); + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP)) + csnprintfrr(buff, sizeof(buff), " %s", + PCEP_VTYSH_ARG_MESSAGE); + if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEPLIB)) + csnprintfrr(buff, sizeof(buff), " %s", + PCEP_VTYSH_ARG_PCEPLIB); + vty_out(vty, "debug pathd pcep%s\n", buff); + buff[0] = 0; + return 1; + } + + return 0; +} + +int pcep_cli_debug_set_all(uint32_t flags, bool set) +{ + DEBUG_FLAGS_SET(&pcep_g->dbg, flags, set); + + /* If all modes have been turned off, don't preserve options. */ + if (!DEBUG_MODE_CHECK(&pcep_g->dbg, DEBUG_MODE_ALL)) + DEBUG_CLEAR(&pcep_g->dbg); + + return 0; +} + +int pcep_cli_pcc_config_write(struct vty *vty) +{ + struct pcc_opts *pcc_opts = pcep_g->pcc_opts; + struct pce_opts_cli *pce_opts_cli; + char buf[128] = ""; + int lines = 0; + int index = 0; + + /* There is nothing configured for the PCC */ + if (pcep_g->pcc_opts == NULL) { + return lines; + } + /* No PCE peers have been configured on the PCC */ + if (pcep_g->num_pce_opts_cli == 0) { + return lines; + } + + if (pcc_opts->port != PCEP_DEFAULT_PORT) { + index += sprintf(buf + index, " %s %d", PCEP_VTYSH_ARG_PORT, + pcc_opts->port); + } + if (pcc_opts->msd != DEFAULT_PCC_MSD) { + index += sprintf(buf + index, " %s %d", PCEP_VTYSH_ARG_MSD, + pcc_opts->msd); + } + + if (IS_IPADDR_V4(&pcc_opts->addr)) { + vty_out(vty, "pcc %s %pI4 %s\n", PCEP_VTYSH_ARG_IP, + &pcc_opts->addr.ipaddr_v4, buf); + } else if (IS_IPADDR_V6(&pcc_opts->addr)) { + vty_out(vty, "pcc %s %pI6 %s\n", PCEP_VTYSH_ARG_IPV6, + &pcc_opts->addr.ipaddr_v6, buf); + } else { + vty_out(vty, "pcc\n"); + } + buf[0] = 0; + lines++; + + for (int i = 0; i < MAX_PCE; i++) { + pce_opts_cli = pcep_g->pce_opts_cli[i]; + if (pce_opts_cli == NULL) { + continue; + } + + index = sprintf(buf, " peer %s", + pce_opts_cli->pce_opts.pce_name); + if (pce_opts_cli->pce_opts.precedence > 0) { + sprintf(buf + index, " %s %d", + PCEP_VTYSH_ARG_PRECEDENCE, + pce_opts_cli->pce_opts.precedence); + } + vty_out(vty, "%s\n", buf); + lines++; + } + + return lines; +} + +/* Internal function used by pcep_cli_pcc_peer_config_write() + * and pcep_cli_pcep_config_group_write() */ +static int +pcep_cli_print_config_group(struct pcep_config_group_opts *group_opts, + char *buf) +{ + int lines = 0; + int index = 0; + + if (group_opts->keep_alive_seconds > 0) { + index += sprintf(buf + index, " %s %d\n", + PCEP_VTYSH_ARG_KEEP_ALIVE, + group_opts->keep_alive_seconds); + lines++; + } + if (group_opts->min_keep_alive_seconds > 0) { + index += sprintf(buf + index, " %s %d\n", + PCEP_VTYSH_ARG_KEEP_ALIVE_MIN, + group_opts->min_keep_alive_seconds); + lines++; + } + if (group_opts->max_keep_alive_seconds > 0) { + index += sprintf(buf + index, " %s %d\n", + PCEP_VTYSH_ARG_KEEP_ALIVE_MAX, + group_opts->max_keep_alive_seconds); + lines++; + } + if (group_opts->dead_timer_seconds > 0) { + index += sprintf(buf + index, " %s %d\n", + PCEP_VTYSH_ARG_DEAD_TIMER, + group_opts->dead_timer_seconds); + lines++; + } + if (group_opts->min_dead_timer_seconds > 0) { + index += sprintf(buf + index, " %s %d\n", + PCEP_VTYSH_ARG_DEAD_TIMER_MIN, + group_opts->min_dead_timer_seconds); + lines++; + } + if (group_opts->max_dead_timer_seconds > 0) { + index += sprintf(buf + index, " %s %d\n", + PCEP_VTYSH_ARG_DEAD_TIMER_MAX, + group_opts->max_dead_timer_seconds); + lines++; + } + if (group_opts->pcep_request_time_seconds > 0) { + index += sprintf(buf + index, " %s %d\n", + PCEP_VTYSH_ARG_PCEP_REQUEST, + group_opts->pcep_request_time_seconds); + lines++; + } + if (group_opts->state_timeout_inteval_seconds > 0) { + index += sprintf(buf + index, " %s %d\n", + PCEP_VTYSH_ARG_STATE_TIMEOUT, + group_opts->state_timeout_inteval_seconds); + lines++; + } + if (group_opts->tcp_md5_auth[0] != '\0') { + index += sprintf(buf + index, " %s %s\n", + PCEP_VTYSH_ARG_TCP_MD5, + group_opts->tcp_md5_auth); + lines++; + } + if (group_opts->draft07) { + index += sprintf(buf + index, " %s\n", + PCEP_VTYSH_ARG_SR_DRAFT07); + lines++; + } + if (group_opts->pce_initiated) { + index += + sprintf(buf + index, " %s\n", PCEP_VTYSH_ARG_PCE_INIT); + lines++; + } + + return lines; +} + +int pcep_cli_pcc_peer_config_write(struct vty *vty) +{ + int lines = 0; + char buf[1024] = ""; + + for (int i = 0; i < MAX_PCE; i++) { + struct pce_opts_cli *pce_opts_cli = pcep_g->pce_opts_cli[i]; + if (pce_opts_cli == NULL) { + continue; + } + struct pce_opts *pce_opts = &pce_opts_cli->pce_opts; + + vty_out(vty, "pcc-peer %s\n", pce_opts->pce_name); + if (IS_IPADDR_V6(&pce_opts->addr)) { + vty_out(vty, " %s %s %pI6", PCEP_VTYSH_ARG_ADDRESS, + PCEP_VTYSH_ARG_IPV6, &pce_opts->addr.ipaddr_v6); + } else { + vty_out(vty, " address %s %pI4", PCEP_VTYSH_ARG_IP, + &pce_opts->addr.ipaddr_v4); + } + if (pce_opts->port != PCEP_DEFAULT_PORT) { + vty_out(vty, " %s %d", PCEP_VTYSH_ARG_PORT, + pce_opts->port); + } + vty_out(vty, "%s\n", buf); + lines += 2; + + if (pce_opts_cli->config_group_name[0] != '\0') { + vty_out(vty, " config-group %s\n", + pce_opts_cli->config_group_name); + lines++; + } + + /* Only display the values configured on the PCE, not the values + * from its optional pce-config-group, nor the default values */ + lines += pcep_cli_print_config_group( + &pce_opts_cli->pce_config_group_opts, buf); + + vty_out(vty, "%s", buf); + buf[0] = '\0'; + } + + return lines; +} + +int pcep_cli_pcep_config_group_write(struct vty *vty) +{ + int lines = 0; + char buf[1024] = ""; + + for (int i = 0; i < MAX_PCE; i++) { + struct pcep_config_group_opts *group_opts = + pcep_g->config_group_opts[i]; + if (group_opts == NULL) { + continue; + } + + vty_out(vty, "pce-config-group %s\n", group_opts->name); + lines += 1; + + lines += pcep_cli_print_config_group(group_opts, buf); + vty_out(vty, "%s", buf); + buf[0] = 0; + } + + return lines; +} + +/* + * VTYSH command syntax definitions + * The param names are taken from the path_pcep_cli_clippy.c generated file. + */ + +DEFPY(pcep_cli_debug, pcep_cli_debug_cmd, + "[no] debug pathd pcep [basic]$basic_str [path]$path_str [message]$message_str [pceplib]$pceplib_str", + NO_STR DEBUG_STR + "pathd debugging\n" + "pcep module debugging\n" + "module basic debugging\n" + "path structures debugging\n" + "pcep message debugging\n" + "pceplib debugging\n") +{ + return path_pcep_cli_debug(vty, no, basic_str, path_str, message_str, + pceplib_str); +} + +DEFPY(pcep_cli_show_pcep_counters, pcep_cli_show_pcep_counters_cmd, + "show pcep counters", + SHOW_STR + "PCEP info\n" + "PCEP counters\n") +{ + return path_pcep_cli_show_pcep_counters(vty); +} + +DEFPY_NOSH(pcep_cli_pcep_config_group, pcep_cli_pcep_config_group_cmd, + "[no] pcep-config-group WORD", + NO_STR + "Peer Configuration Group\n" + "Peer Configuration Group name\n") +{ + if (no != NULL) { + return path_pcep_cli_pcep_config_group_delete( + vty, pcep_config_group); + } else { + return path_pcep_cli_pcep_config_group(vty, pcep_config_group); + } +} + +DEFPY(pcep_cli_show_pcep_config_group, pcep_cli_show_pcep_config_group_cmd, + "show pcep-config-group [$config_group]", + SHOW_STR + "Show detailed peer-config-group values\n" + "Show default hard-coded peer-config-group values\n" + "peer-config-group to show\n") +{ + return path_pcep_cli_show_pcep_config_group(vty, config_group); +} + +DEFPY_NOSH(pcep_cli_pcc_peer, pcep_cli_pcc_peer_cmd, "[no] pcc-peer WORD", + NO_STR + "PCC Peer configuration, address sub-config is mandatory\n" + "PCE name\n") +{ + if (no != NULL) { + return path_pcep_cli_pcc_peer_delete(vty, pcc_peer); + } else { + return path_pcep_cli_pcc_peer(vty, pcc_peer); + } +} + +DEFPY(pcep_cli_show_pcc_peer, pcep_cli_show_pcc_peer_cmd, + "show pcc-peer [WORD]", + SHOW_STR + "Show detailed pcc-peer (PCE) values\n" + "pcc-peer to show\n") +{ + return path_pcep_cli_show_pcc_peer(vty, pcc_peer); +} + +DEFPY(pcep_cli_peer_sr_draft07, pcep_cli_peer_sr_draft07_cmd, "sr-draft07", + "Configure PCC to send PCEP Open with SR draft07\n") +{ + return path_pcep_cli_peer_sr_draft07(vty); +} + +DEFPY(pcep_cli_peer_pce_initiated, pcep_cli_peer_pce_initiated_cmd, + "pce-initiated", "Configure PCC to accept PCE initiated LSPs\n") +{ + return path_pcep_cli_peer_pce_initiated(vty); +} + +DEFPY(pcep_cli_peer_tcp_md5_auth, pcep_cli_peer_tcp_md5_auth_cmd, + "tcp-md5-auth WORD", + "Configure PCC TCP-MD5 RFC2385 Authentication\n" + "TCP-MD5 Authentication string\n") +{ + return path_pcep_cli_peer_tcp_md5_auth(vty, tcp_md5_auth); +} + +DEFPY(pcep_cli_peer_address, pcep_cli_peer_address_cmd, + "address [port (1024-65535)]", + "PCE IP Address configuration, mandatory configuration\n" + "PCE IPv4 address\n" + "Remote PCE server IPv4 address\n" + "PCE IPv6 address\n" + "Remote PCE server IPv6 address\n" + "Remote PCE server port\n" + "Remote PCE server port value\n") +{ + return path_pcep_cli_peer_address(vty, ip_str, &ip, ipv6_str, &ipv6, + port_str, port); +} + +DEFPY(pcep_cli_peer_pcep_config_group, pcep_cli_peer_pcep_config_group_cmd, + "config-group WORD", + "PCE Configuration Group\n" + "PCE Configuration Group name\n") +{ + return path_pcep_cli_peer_pcep_config_group(vty, config_group); +} + +DEFPY(pcep_cli_peer_timers, pcep_cli_peer_timers_cmd, + "timer [keep-alive (1-240)] [min-peer-keep-alive (1-60)] [max-peer-keep-alive (60-240)] " + "[dead-timer (1-240)] [min-peer-dead-timer (1-60)] [max-peer-dead-timer (60-240)] " + "[pcep-request (1-120)] [state-timeout-interval (1-120)]", + "PCE PCEP Session Timers configuration\n" + "PCC Keep Alive Timer\n" + "PCC Keep Alive Timer value in seconds\n" + "Min Acceptable PCE Keep Alive Timer\n" + "Min Acceptable PCE Keep Alive Timer value in seconds\n" + "Max Acceptable PCE Keep Alive Timer\n" + "Max Acceptable PCE Keep Alive Timer value in seconds\n" + "PCC Dead Timer\n" + "PCC Dead Timer value in seconds\n" + "Min Acceptable PCE Dead Timer\n" + "Min Acceptable PCE Dead Timer value in seconds\n" + "Max Acceptable PCE Dead Timer\n" + "Max Acceptable PCE Dead Timer value in seconds\n" + "PCC PCEP Request Timer\n" + "PCC PCEP Request Timer value in seconds\n" + "PCC State Timeout Interval\n" + "PCC State Timeout Interval value in seconds\n") +{ + return path_pcep_cli_peer_timers( + vty, keep_alive_str, keep_alive, min_peer_keep_alive_str, + min_peer_keep_alive, max_peer_keep_alive_str, + max_peer_keep_alive, dead_timer_str, dead_timer, + min_peer_dead_timer_str, min_peer_dead_timer, + max_peer_dead_timer_str, max_peer_dead_timer, pcep_request_str, + pcep_request, state_timeout_interval_str, + state_timeout_interval); +} + +DEFPY_NOSH( + pcep_cli_pcc, pcep_cli_pcc_cmd, + "[no] pcc [{ip A.B.C.D | ipv6 X:X::X:X}] [port (1024-65535)] [msd (1-16)]", + NO_STR + "PCC configuration\n" + "PCC source ip\n" + "PCC source IPv4 address\n" + "PCC source ip\n" + "PCC source IPv6 address\n" + "PCC source port\n" + "PCC source port value\n" + "PCC maximum SID depth \n" + "PCC maximum SID depth value\n") +{ + if (no != NULL) { + return path_pcep_cli_pcc_delete(vty, ip_str, &ip, ipv6_str, + &ipv6, port_str, port, msd_str, + msd); + } else { + return path_pcep_cli_pcc(vty, ip_str, &ip, ipv6_str, &ipv6, + port_str, port, msd_str, msd); + } +} + +DEFPY(pcep_cli_pcc_pcc_peer, pcep_cli_pcc_pcc_peer_cmd, + "[no] peer WORD [precedence (1-255)]", + NO_STR + "PCC PCE peer\n" + "PCC PCE name\n" + "PCC Multi-PCE precedence\n" + "PCE precedence\n") +{ + if (no != NULL) { + return path_pcep_cli_pcc_pcc_peer_delete( + vty, peer, precedence_str, precedence); + } else { + return path_pcep_cli_pcc_pcc_peer(vty, peer, precedence_str, + precedence); + } +} + +void pcep_cli_init(void) +{ + hook_register(nb_client_debug_config_write, + pcep_cli_debug_config_write); + hook_register(nb_client_debug_set_all, pcep_cli_debug_set_all); + + install_node(&pcc_node); + install_node(&pcc_peer_node); + install_node(&pcep_config_group_node); + + install_default(PCEP_CONFIG_GROUP_NODE); + install_default(PCC_PEER_NODE); + install_default(PCC_NODE); + + install_element(CONFIG_NODE, &pcep_cli_debug_cmd); + install_element(ENABLE_NODE, &pcep_cli_debug_cmd); + install_element(ENABLE_NODE, &pcep_cli_show_pcep_counters_cmd); + + /* PCE-Group related commands */ + install_element(CONFIG_NODE, &pcep_cli_pcep_config_group_cmd); + install_element(ENABLE_NODE, &pcep_cli_show_pcep_config_group_cmd); + install_element(PCEP_CONFIG_GROUP_NODE, &pcep_cli_peer_timers_cmd); + install_element(PCEP_CONFIG_GROUP_NODE, &pcep_cli_peer_sr_draft07_cmd); + install_element(PCEP_CONFIG_GROUP_NODE, + &pcep_cli_peer_pce_initiated_cmd); + install_element(PCEP_CONFIG_GROUP_NODE, + &pcep_cli_peer_tcp_md5_auth_cmd); + + /* PCC-PEER (PCE) related commands */ + install_element(CONFIG_NODE, &pcep_cli_pcc_peer_cmd); + install_element(ENABLE_NODE, &pcep_cli_show_pcc_peer_cmd); + install_element(PCC_PEER_NODE, &pcep_cli_peer_address_cmd); + install_element(PCC_PEER_NODE, &pcep_cli_peer_pcep_config_group_cmd); + install_element(PCC_PEER_NODE, &pcep_cli_peer_timers_cmd); + install_element(PCC_PEER_NODE, &pcep_cli_peer_sr_draft07_cmd); + install_element(PCC_PEER_NODE, &pcep_cli_peer_pce_initiated_cmd); + install_element(PCC_PEER_NODE, &pcep_cli_peer_tcp_md5_auth_cmd); + + /* PCC related commands */ + install_element(CONFIG_NODE, &pcep_cli_pcc_cmd); + install_element(PCC_NODE, &pcep_cli_pcc_pcc_peer_cmd); +} diff --git a/pathd/path_pcep_cli.h b/pathd/path_pcep_cli.h new file mode 100644 index 000000000000..0b101ab2153c --- /dev/null +++ b/pathd/path_pcep_cli.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 Volta Networks, Inc + * Brady Johnson + * + * 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 _PATH_PCEP_CLI_H_ +#define _PATH_PCEP_CLI_H_ + + +/* PCEP CLI Functions */ +void pcep_cli_init(void); + +#endif // _PATH_PCEP_CLI_H_ diff --git a/pathd/path_pcep_controller.c b/pathd/path_pcep_controller.c index d8779e270613..39f3c3c27c3a 100644 --- a/pathd/path_pcep_controller.c +++ b/pathd/path_pcep_controller.c @@ -195,8 +195,7 @@ int pcep_ctrl_initialize(struct thread_master *main_thread, ctrl_state->pcc_opts = XCALLOC(MTYPE_PCEP, sizeof(*ctrl_state->pcc_opts)); /* Default to no PCC address defined */ - UNSET_FLAG(ctrl_state->pcc_opts->flags, F_PCC_OPTS_IPV4); - UNSET_FLAG(ctrl_state->pcc_opts->flags, F_PCC_OPTS_IPV6); + ctrl_state->pcc_opts->addr.ipa_type = IPADDR_NONE; ctrl_state->pcc_opts->port = PCEP_DEFAULT_PORT; /* Keep the state reference for events */ @@ -270,6 +269,30 @@ struct counters_group *pcep_ctrl_get_counters(struct frr_pthread *fpt, return args.counters; } +int pcep_ctrl_pcc_num_pce(struct frr_pthread *fpt) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + return ctrl_state->pcc_count; +} + +bool pcep_ctrl_pcc_has_pce(struct frr_pthread *fpt, const char *pce_name) +{ + struct ctrl_state *ctrl_state = get_ctrl_state(fpt); + int i = 0; + for (; i < MAX_PCE; i++) { + if (ctrl_state->pcc[i] == NULL) { + continue; + } + + if (strcmp(ctrl_state->pcc[i]->pce_opts->pce_name, pce_name) + == 0) { + return true; + } + } + + return false; +} + void pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id, struct path *path) { @@ -330,7 +353,7 @@ void pcep_thread_cancel_pceplib_timer(struct thread **thread) { PCEP_DEBUG("Cancel pceplib timer"); - if (thread == NULL) { + if (thread == NULL || *thread == NULL) { return; } diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h index 0cf58e37d92c..59a17bb611f8 100644 --- a/pathd/path_pcep_controller.h +++ b/pathd/path_pcep_controller.h @@ -45,7 +45,7 @@ struct ctrl_state { pcep_main_event_handler_t main_event_handler; struct pcc_opts *pcc_opts; int pcc_count; - struct pcc_state *pcc[MAX_PCC]; + struct pcc_state *pcc[MAX_PCE]; }; /* Timer handling data structures */ @@ -90,6 +90,8 @@ int pcep_ctrl_sync_path(struct frr_pthread *fpt, int pcc_id, struct path *path); int pcep_ctrl_sync_done(struct frr_pthread *fpt, int pcc_id); struct counters_group *pcep_ctrl_get_counters(struct frr_pthread *fpt, int pcc_id); +int pcep_ctrl_pcc_num_pce(struct frr_pthread *fpt); +bool pcep_ctrl_pcc_has_pce(struct frr_pthread *fpt, const char *pce_name); /* Synchronously send a report, the caller is responsible to free the path, * If `pcc_id` is `0` the report is sent by all PCCs */ void pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id, diff --git a/pathd/path_pcep_debug.c b/pathd/path_pcep_debug.c index 3fa9384004ec..a560ee0871da 100644 --- a/pathd/path_pcep_debug.c +++ b/pathd/path_pcep_debug.c @@ -655,15 +655,15 @@ void _format_pcc_opts(int ps, struct pcc_opts *opts) } else { int ps2 = ps + DEBUG_IDENT_SIZE; PCEP_FORMAT("\n"); - if (CHECK_FLAG(opts->flags, F_PCC_OPTS_IPV4)) { + if (IS_IPADDR_V4(&opts->addr)) { PCEP_FORMAT("%*saddr_v4: %pI4\n", ps2, "", - &opts->addr_v4); + &opts->addr.ipaddr_v4); } else { PCEP_FORMAT("%*saddr_v4: undefined", ps2, ""); } - if (CHECK_FLAG(opts->flags, F_PCC_OPTS_IPV6)) { + if (IS_IPADDR_V6(&opts->addr)) { PCEP_FORMAT("%*saddr_v6: %pI6\n", ps2, "", - &opts->addr_v6); + &opts->addr.ipaddr_v6); } else { PCEP_FORMAT("%*saddr_v6: undefined", ps2, ""); } diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c index b76758fceb68..d74ffbe86fc6 100644 --- a/pathd/path_pcep_lib.c +++ b/pathd/path_pcep_lib.c @@ -130,9 +130,10 @@ void pcep_lib_finalize(void) } -pcep_session *pcep_lib_connect(struct ipaddr *src_addr, int src_port, - struct ipaddr *dst_addr, int dst_port, - bool draft07, short msd) +pcep_session * +pcep_lib_connect(struct ipaddr *src_addr, int src_port, struct ipaddr *dst_addr, + int dst_port, short msd, + const struct pcep_config_group_opts *pcep_options) { pcep_configuration *config; pcep_session *sess; @@ -158,8 +159,18 @@ pcep_session *pcep_lib_connect(struct ipaddr *src_addr, int src_port, config->support_sr_te_pst = true; config->pcc_can_resolve_nai_to_sid = false; - config->pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = draft07; config->max_sid_depth = msd; + config->pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = + pcep_options->draft07; + config->keep_alive_seconds = pcep_options->keep_alive_seconds; + config->min_keep_alive_seconds = pcep_options->min_keep_alive_seconds; + config->max_keep_alive_seconds = pcep_options->max_keep_alive_seconds; + config->dead_timer_seconds = pcep_options->dead_timer_seconds; + config->min_dead_timer_seconds = pcep_options->min_dead_timer_seconds; + config->max_dead_timer_seconds = pcep_options->max_dead_timer_seconds; + config->request_time_seconds = pcep_options->pcep_request_time_seconds; + /* TODO when available in the pceplib, set it here + pcep_options->state_timeout_inteval_seconds;*/ if (IS_IPADDR_V6(dst_addr)) { sess = connect_pce_ipv6(config, &dst_addr->ipaddr_v6); diff --git a/pathd/path_pcep_lib.h b/pathd/path_pcep_lib.h index 6e97626223b5..9691a16d2621 100644 --- a/pathd/path_pcep_lib.h +++ b/pathd/path_pcep_lib.h @@ -27,9 +27,10 @@ int pcep_lib_initialize(struct frr_pthread *fpt); void pcep_lib_finalize(void); -pcep_session *pcep_lib_connect(struct ipaddr *src_addr, int src_port, - struct ipaddr *dst_addr, int dst_port, - bool draft07, short msd); +pcep_session * +pcep_lib_connect(struct ipaddr *src_addr, int src_port, struct ipaddr *dst_addr, + int dst_port, short msd, + const struct pcep_config_group_opts *pcep_options); void pcep_lib_disconnect(pcep_session *sess); struct pcep_message *pcep_lib_format_report(struct path *path); struct pcep_message *pcep_lib_format_request(uint32_t reqid, struct ipaddr *src, diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c index 61126cde2d3e..913655db847e 100644 --- a/pathd/path_pcep_pcc.c +++ b/pathd/path_pcep_pcc.c @@ -173,11 +173,6 @@ int compare_pcc_opts(struct pcc_opts *lhs, struct pcc_opts *rhs) return -1; } - retval = lhs->flags != rhs->flags; - if (retval != 0) { - return retval; - } - retval = lhs->port - rhs->port; if (retval != 0) { return retval; @@ -188,17 +183,15 @@ int compare_pcc_opts(struct pcc_opts *lhs, struct pcc_opts *rhs) return retval; } - if (CHECK_FLAG(lhs->flags, F_PCC_OPTS_IPV4)) { - retval = memcmp(&lhs->addr_v4, &rhs->addr_v4, - sizeof(lhs->addr_v4)); + if (IS_IPADDR_V4(&lhs->addr)) { + retval = memcmp(&lhs->addr.ipaddr_v4, &rhs->addr.ipaddr_v4, + sizeof(lhs->addr.ipaddr_v4)); if (retval != 0) { return retval; } - } - - if (CHECK_FLAG(lhs->flags, F_PCC_OPTS_IPV6)) { - retval = memcmp(&lhs->addr_v6, &rhs->addr_v6, - sizeof(lhs->addr_v6)); + } else if (IS_IPADDR_V6(&lhs->addr)) { + retval = memcmp(&lhs->addr.ipaddr_v6, &rhs->addr.ipaddr_v6, + sizeof(lhs->addr.ipaddr_v6)); if (retval != 0) { return retval; } @@ -222,8 +215,9 @@ int compare_pce_opts(struct pce_opts *lhs, struct pce_opts *rhs) return retval; } - if (lhs->draft07 != rhs->draft07) { - return 1; + retval = strcmp(lhs->pce_name, rhs->pce_name); + if (retval != 0) { + return retval; } retval = memcmp(&lhs->addr, &rhs->addr, sizeof(lhs->addr)); @@ -261,15 +255,16 @@ int pcep_pcc_update(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, pcc_state->pcc_opts = pcc_opts; pcc_state->pce_opts = pce_opts; - if (CHECK_FLAG(pcc_opts->flags, F_PCC_OPTS_IPV4)) { - pcc_state->pcc_addr_v4 = pcc_opts->addr_v4; + if (IS_IPADDR_V4(&pcc_opts->addr)) { + pcc_state->pcc_addr_v4 = pcc_opts->addr.ipaddr_v4; SET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4); } else { UNSET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4); } - if (CHECK_FLAG(pcc_opts->flags, F_PCC_OPTS_IPV6)) { - pcc_state->pcc_addr_v6 = pcc_opts->addr_v6; + if (IS_IPADDR_V6(&pcc_opts->addr)) { + memcpy(&pcc_state->pcc_addr_v6, &pcc_opts->addr.ipaddr_v6, + sizeof(struct in6_addr)); SET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6); } else { UNSET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6); @@ -335,7 +330,7 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) pcc_state->sess = pcep_lib_connect( &pcc_state->pcc_addr_tr, pcc_state->pcc_opts->port, &pcc_state->pce_opts->addr, pcc_state->pce_opts->port, - pcc_state->pce_opts->draft07, pcc_state->pcc_opts->msd); + pcc_state->pcc_opts->msd, &pcc_state->pce_opts->config_opts); if (pcc_state->sess == NULL) { flog_warn(EC_PATH_PCEP_LIB_CONNECT, diff --git a/pathd/pathd.conf.sample b/pathd/pathd.conf.sample index 7e9a2925525f..356470e11262 100644 --- a/pathd/pathd.conf.sample +++ b/pathd/pathd.conf.sample @@ -25,3 +25,28 @@ sr-policy color 2 endpoint 2.2.2.2 candidate-path preference 100 name def explicit segment-list test2 candidate-path preference 200 name dyn dynamic metrics bound abc 16 te ! +!! Simplest PCEP config without pcep-config-group +! +!pcc-peer pce1 +! address ip 10.0.12.1 +!pcc +! peer pce1 +! +!! A more complex PCEP config with a pcep-config-group +! +!pcep-config-group group1 +! timer keep-alive 35 +! +!pcc-peer pce1 +! config-group group1 +! address ip 10.0.12.1 +! timer dead-timer 140 +! +!pcc +! peer pce1 +! +!show pcep-config-group +!show pcep-config-group default +!show pcep-config-group group1 +!show pcc-peer +!show pcc-peer pce1 diff --git a/pathd/subdir.am b/pathd/subdir.am index db5438cec96b..6ef28469d83f 100644 --- a/pathd/subdir.am +++ b/pathd/subdir.am @@ -9,7 +9,7 @@ dist_examples_DATA += pathd/pathd.conf.sample vtysh_scan += $(top_srcdir)/pathd/path_cli.c if PCEP -vtysh_scan += $(top_srcdir)/pathd/path_pcep.c +vtysh_scan += $(top_srcdir)/pathd/path_pcep_cli.c module_LTLIBRARIES += pathd/pathd_pcep.la endif @@ -33,11 +33,18 @@ clippy_scan += \ pathd/path_cli.c \ # end +if PCEP +clippy_scan += \ + pathd/path_pcep_cli.c \ + # end +endif + noinst_HEADERS += \ pathd/path_errors.h \ pathd/path_memory.h \ pathd/path_nb.h \ pathd/path_pcep.h \ + pathd/path_pcep_cli.h \ pathd/path_pcep_controller.h \ pathd/path_pcep_debug.h \ pathd/path_pcep_lib.h \ @@ -49,9 +56,6 @@ noinst_HEADERS += \ pathd/pathd.h \ # end -pathd/path_cli_clippy.c: $(CLIPPY_DEPS) -pathd/path_cli.$(OBJEXT): pathd/path_cli_clippy.c - pathd_pathd_SOURCES = pathd/path_main.c nodist_pathd_pathd_SOURCES = \ yang/frr-pathd.yang.c \ @@ -60,6 +64,7 @@ pathd_pathd_LDADD = pathd/libpath.a lib/libfrr.la -lm $(LIBCAP) pathd_pathd_pcep_la_SOURCES = \ pathd/path_pcep.c \ + pathd/path_pcep_cli.c \ pathd/path_pcep_controller.c \ pathd/path_pcep_debug.c \ pathd/path_pcep_lib.c \ diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 0d24d3e6e669..4d248624e5b7 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1234,6 +1234,20 @@ static struct cmd_node pcc_node = { .prompt = "%s(config-pcc)# ", }; +static struct cmd_node pcc_peer_node = { + .name = "pcc-peer", + .node = PCC_PEER_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-pcc-peer)# ", +}; + +static struct cmd_node pcep_config_group_node = { + .name = "pcep-config-group", + .node = PCEP_CONFIG_GROUP_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(pcep-config-group)# ", +}; + static struct cmd_node vrf_node = { .name = "vrf", .node = VRF_NODE, @@ -1952,7 +1966,8 @@ DEFUNSH(VTYSH_PATHD, te_path_sr_policy, te_path_sr_policy_cmd, } DEFUNSH(VTYSH_PATHD, pcep_cli_pcc, pcep_cli_pcc_cmd, - "pcc [{ip A.B.C.D | ipv6 X:X::X:X}] [port (1024-65535)] [msd (1-16)]", + "[no] pcc [{ip A.B.C.D | ipv6 X:X::X:X}] [port (1024-65535)] [msd (1-16)]", + NO_STR "PCC configuration\n" "PCC source ip\n" "PCC source IPv4 address\n" @@ -1967,6 +1982,26 @@ DEFUNSH(VTYSH_PATHD, pcep_cli_pcc, pcep_cli_pcc_cmd, return CMD_SUCCESS; } +DEFUNSH(VTYSH_PATHD, pcep_cli_pcc_peer, pcep_cli_pcc_peer_cmd, + "[no] pcc-peer WORD", + NO_STR + "PCC Peer configuration\n" + "Peer name\n") +{ + vty->node = PCC_PEER_NODE; + return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_PATHD, pcep_cli_pcep_config_group, pcep_cli_pcep_config_group_cmd, + "[no] pcep-config-group WORD", + NO_STR + "PCEP peer Configuration Group\n" + "PCEP peer Configuration Group name\n") +{ + vty->node = PCEP_CONFIG_GROUP_NODE; + return CMD_SUCCESS; +} + DEFUNSH(VTYSH_RMAP, vtysh_route_map, vtysh_route_map_cmd, "route-map WORD (1-65535)", "Create route-map or enter route-map command mode\n" @@ -3849,6 +3884,8 @@ void vtysh_init_vty(void) install_node(&segment_list_node); install_node(&sr_policy_node); install_node(&pcc_node); + install_node(&pcc_peer_node); + install_node(&pcep_config_group_node); install_node(&link_params_node); install_node(&vrf_node); install_node(&nh_group_node); @@ -3993,6 +4030,10 @@ void vtysh_init_vty(void) install_element(SR_POLICY_NODE, &vtysh_quit_pathd_cmd); install_element(PCC_NODE, &vtysh_exit_pathd_cmd); install_element(PCC_NODE, &vtysh_quit_pathd_cmd); + install_element(PCC_PEER_NODE, &vtysh_exit_pathd_cmd); + install_element(PCC_PEER_NODE, &vtysh_quit_pathd_cmd); + install_element(PCEP_CONFIG_GROUP_NODE, &vtysh_exit_pathd_cmd); + install_element(PCEP_CONFIG_GROUP_NODE, &vtysh_quit_pathd_cmd); install_element(RMAP_NODE, &vtysh_exit_rmap_cmd); install_element(RMAP_NODE, &vtysh_quit_rmap_cmd); install_element(PBRMAP_NODE, &vtysh_exit_pbr_map_cmd); @@ -4059,6 +4100,8 @@ void vtysh_init_vty(void) install_element(SEGMENT_LIST_NODE, &vtysh_end_all_cmd); install_element(SR_POLICY_NODE, &vtysh_end_all_cmd); install_element(PCC_NODE, &vtysh_end_all_cmd); + install_element(PCC_PEER_NODE, &vtysh_end_all_cmd); + install_element(PCEP_CONFIG_GROUP_NODE, &vtysh_end_all_cmd); install_element(RMAP_NODE, &vtysh_end_all_cmd); install_element(PBRMAP_NODE, &vtysh_end_all_cmd); install_element(VTY_NODE, &vtysh_end_all_cmd); @@ -4160,6 +4203,8 @@ void vtysh_init_vty(void) install_element(CONFIG_NODE, &te_path_segment_list_cmd); install_element(CONFIG_NODE, &te_path_sr_policy_cmd); install_element(CONFIG_NODE, &pcep_cli_pcc_cmd); + install_element(CONFIG_NODE, &pcep_cli_pcep_config_group_cmd); + install_element(CONFIG_NODE, &pcep_cli_pcc_peer_cmd); install_element(CONFIG_NODE, &vtysh_route_map_cmd); install_element(CONFIG_NODE, &vtysh_pbr_map_cmd); install_element(CONFIG_NODE, &vtysh_no_pbr_map_cmd);