summaryrefslogtreecommitdiff
path: root/pathd
diff options
context:
space:
mode:
Diffstat (limited to 'pathd')
-rw-r--r--pathd/.gitignore2
-rw-r--r--pathd/Makefile10
-rw-r--r--pathd/path_cli.c1108
-rw-r--r--pathd/path_debug.c121
-rw-r--r--pathd/path_debug.h44
-rw-r--r--pathd/path_errors.c134
-rw-r--r--pathd/path_errors.h45
-rw-r--r--pathd/path_main.c154
-rw-r--r--pathd/path_memory.c25
-rw-r--r--pathd/path_memory.h26
-rw-r--r--pathd/path_nb.c351
-rw-r--r--pathd/path_nb.h130
-rw-r--r--pathd/path_nb_config.c731
-rw-r--r--pathd/path_nb_state.c189
-rw-r--r--pathd/path_pcep.c339
-rw-r--r--pathd/path_pcep.h326
-rw-r--r--pathd/path_pcep_cli.c2033
-rw-r--r--pathd/path_pcep_cli.h27
-rw-r--r--pathd/path_pcep_config.c435
-rw-r--r--pathd/path_pcep_config.h44
-rw-r--r--pathd/path_pcep_controller.c1078
-rw-r--r--pathd/path_pcep_controller.h166
-rw-r--r--pathd/path_pcep_debug.c1771
-rw-r--r--pathd/path_pcep_debug.h56
-rw-r--r--pathd/path_pcep_lib.c1146
-rw-r--r--pathd/path_pcep_lib.h48
-rw-r--r--pathd/path_pcep_memory.c27
-rw-r--r--pathd/path_pcep_memory.h28
-rw-r--r--pathd/path_pcep_pcc.c1818
-rw-r--r--pathd/path_pcep_pcc.h141
-rw-r--r--pathd/path_zebra.c294
-rw-r--r--pathd/path_zebra.h34
-rw-r--r--pathd/pathd.c1128
-rw-r--r--pathd/pathd.conf.sample41
-rw-r--r--pathd/pathd.h412
-rw-r--r--pathd/subdir.am74
36 files changed, 14536 insertions, 0 deletions
diff --git a/pathd/.gitignore b/pathd/.gitignore
new file mode 100644
index 0000000000..95f4a99794
--- /dev/null
+++ b/pathd/.gitignore
@@ -0,0 +1,2 @@
+libpath.a
+pathd
diff --git a/pathd/Makefile b/pathd/Makefile
new file mode 100644
index 0000000000..b681a9ab1c
--- /dev/null
+++ b/pathd/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. pathd/pathd
+%: ALWAYS
+ @$(MAKE) -s -C .. pathd/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/pathd/path_cli.c b/pathd/path_cli.c
new file mode 100644
index 0000000000..8beb428135
--- /dev/null
+++ b/pathd/path_cli.c
@@ -0,0 +1,1108 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <float.h>
+#include <math.h>
+#include <zebra.h>
+
+#include "log.h"
+#include "command.h"
+#include "mpls.h"
+#include "northbound_cli.h"
+#include "termtable.h"
+
+#include "pathd/pathd.h"
+#include "pathd/path_nb.h"
+#include "pathd/path_memory.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "pathd/path_cli_clippy.c"
+#endif
+
+#define XPATH_MAXATTRSIZE 64
+#define XPATH_MAXKEYSIZE 42
+#define XPATH_POLICY_BASELEN 100
+#define XPATH_POLICY_MAXLEN (XPATH_POLICY_BASELEN + XPATH_MAXATTRSIZE)
+#define XPATH_CANDIDATE_BASELEN (XPATH_POLICY_BASELEN + XPATH_MAXKEYSIZE)
+#define XPATH_CANDIDATE_MAXLEN (XPATH_CANDIDATE_BASELEN + XPATH_MAXATTRSIZE)
+
+
+static int config_write_segment_routing(struct vty *vty);
+static int config_write_traffic_eng(struct vty *vty);
+static int config_write_segment_lists(struct vty *vty);
+static int config_write_sr_policies(struct vty *vty);
+
+DEFINE_MTYPE_STATIC(PATHD, PATH_CLI, "Client")
+
+/* Vty node structures. */
+static struct cmd_node segment_routing_node = {
+ .name = "segment-routing",
+ .node = SEGMENT_ROUTING_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-sr)# ",
+ .config_write = config_write_segment_routing,
+};
+
+static struct cmd_node sr_traffic_eng_node = {
+ .name = "sr traffic-eng",
+ .node = SR_TRAFFIC_ENG_NODE,
+ .parent_node = SEGMENT_ROUTING_NODE,
+ .prompt = "%s(config-sr-te)# ",
+ .config_write = config_write_traffic_eng,
+};
+
+static struct cmd_node srte_segment_list_node = {
+ .name = "srte segment-list",
+ .node = SR_SEGMENT_LIST_NODE,
+ .parent_node = SR_TRAFFIC_ENG_NODE,
+ .prompt = "%s(config-sr-te-segment-list)# ",
+ .config_write = config_write_segment_lists,
+};
+
+static struct cmd_node srte_policy_node = {
+ .name = "srte policy",
+ .node = SR_POLICY_NODE,
+ .parent_node = SR_TRAFFIC_ENG_NODE,
+ .prompt = "%s(config-sr-te-policy)# ",
+ .config_write = config_write_sr_policies,
+};
+
+static struct cmd_node srte_candidate_dyn_node = {
+ .name = "srte candidate-dyn",
+ .node = SR_CANDIDATE_DYN_NODE,
+ .parent_node = SR_POLICY_NODE,
+ .prompt = "%s(config-sr-te-candidate)# ",
+};
+
+
+/*
+ * Show SR-TE info
+ */
+DEFPY(show_srte_policy,
+ show_srte_policy_cmd,
+ "show sr-te policy",
+ SHOW_STR
+ "SR-TE info\n"
+ "SR-TE Policy\n")
+{
+ struct ttable *tt;
+ struct srte_policy *policy;
+ char *table;
+
+ if (RB_EMPTY(srte_policy_head, &srte_policies)) {
+ vty_out(vty, "No SR Policies to display.\n\n");
+ return CMD_SUCCESS;
+ }
+
+ /* Prepare table. */
+ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+ ttable_add_row(tt, "Endpoint|Color|Name|BSID|Status");
+ tt->style.cell.rpad = 2;
+ tt->style.corner = '+';
+ ttable_restyle(tt);
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
+
+ RB_FOREACH (policy, srte_policy_head, &srte_policies) {
+ char endpoint[46];
+ char binding_sid[16] = "-";
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ if (policy->binding_sid != MPLS_LABEL_NONE)
+ snprintf(binding_sid, sizeof(binding_sid), "%u",
+ policy->binding_sid);
+
+ ttable_add_row(tt, "%s|%u|%s|%s|%s", endpoint, policy->color,
+ policy->name, binding_sid,
+ policy->status == SRTE_POLICY_STATUS_UP
+ ? "Active"
+ : "Inactive");
+ }
+
+ /* Dump the generated table. */
+ table = ttable_dump(tt, "\n");
+ vty_out(vty, "%s\n", table);
+ XFREE(MTYPE_TMP, table);
+
+ ttable_del(tt);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Show detailed SR-TE info
+ */
+DEFPY(show_srte_policy_detail,
+ show_srte_policy_detail_cmd,
+ "show sr-te policy detail",
+ SHOW_STR
+ "SR-TE info\n"
+ "SR-TE Policy\n"
+ "Show a detailed summary\n")
+{
+ struct srte_policy *policy;
+
+ if (RB_EMPTY(srte_policy_head, &srte_policies)) {
+ vty_out(vty, "No SR Policies to display.\n\n");
+ return CMD_SUCCESS;
+ }
+
+ vty_out(vty, "\n");
+ RB_FOREACH (policy, srte_policy_head, &srte_policies) {
+ struct srte_candidate *candidate;
+ char endpoint[46];
+ char binding_sid[16] = "-";
+ char *segment_list_info;
+ static char undefined_info[] = "(undefined)";
+ static char created_by_pce_info[] = "(created by PCE)";
+
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ if (policy->binding_sid != MPLS_LABEL_NONE)
+ snprintf(binding_sid, sizeof(binding_sid), "%u",
+ policy->binding_sid);
+ vty_out(vty,
+ "Endpoint: %s Color: %u Name: %s BSID: %s Status: %s\n",
+ endpoint, policy->color, policy->name, binding_sid,
+ policy->status == SRTE_POLICY_STATUS_UP ? "Active"
+ : "Inactive");
+
+ RB_FOREACH (candidate, srte_candidate_head,
+ &policy->candidate_paths) {
+ struct srte_segment_list *segment_list;
+
+ segment_list = candidate->lsp->segment_list;
+ if (segment_list == NULL)
+ segment_list_info = undefined_info;
+ else if (segment_list->protocol_origin
+ == SRTE_ORIGIN_PCEP)
+ segment_list_info = created_by_pce_info;
+ else
+ segment_list_info =
+ candidate->lsp->segment_list->name;
+
+ vty_out(vty,
+ " %s Preference: %d Name: %s Type: %s Segment-List: %s Protocol-Origin: %s\n",
+ CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST)
+ ? "*"
+ : " ",
+ candidate->preference, candidate->name,
+ candidate->type == SRTE_CANDIDATE_TYPE_EXPLICIT
+ ? "explicit"
+ : "dynamic",
+ segment_list_info,
+ srte_origin2str(
+ candidate->lsp->protocol_origin));
+ }
+
+ vty_out(vty, "\n");
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY_NOSH(
+ segment_routing_list,
+ segment_routing_cmd,
+ "segment-routing",
+ "Configure segment routing\n")
+{
+ VTY_PUSH_CONTEXT_NULL(SEGMENT_ROUTING_NODE);
+ return CMD_SUCCESS;
+}
+
+DEFPY_NOSH(
+ sr_traffic_eng_list,
+ sr_traffic_eng_cmd,
+ "traffic-eng",
+ "Configure SR traffic engineering\n")
+{
+ VTY_PUSH_CONTEXT_NULL(SR_TRAFFIC_ENG_NODE);
+ return CMD_SUCCESS;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list
+ */
+DEFPY_NOSH(
+ srte_segment_list,
+ srte_segment_list_cmd,
+ "segment-list WORD$name",
+ "Segment List\n"
+ "Segment List Name\n")
+{
+ char xpath[XPATH_MAXLEN];
+ int ret;
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/segment-list[name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/segment-list[name='%s']/protocol-origin",
+ name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "local");
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/segment-list[name='%s']/originator", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "config");
+
+ ret = nb_cli_apply_changes(vty, NULL);
+ if (ret == CMD_SUCCESS) {
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/segment-list[name='%s']", name);
+ VTY_PUSH_XPATH(SR_SEGMENT_LIST_NODE, xpath);
+ }
+
+ return ret;
+}
+
+DEFPY(srte_no_segment_list,
+ srte_no_segment_list_cmd,
+ "no segment-list WORD$name",
+ NO_STR
+ "Segment List\n"
+ "Segment List Name\n")
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/segment-list[name='%s']", name);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " segment-list %s\n",
+ yang_dnode_get_string(dnode, "./name"));
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list/segment
+ */
+DEFPY(srte_segment_list_segment,
+ srte_segment_list_segment_cmd,
+ "index (0-4294967295)$index mpls label (16-1048575)$label "
+ "[nai$has_nai <"
+ "node <A.B.C.D$node_ipv4|X:X::X:X$node_ipv6>"
+ ">]",
+ "Index\n"
+ "Index Value\n"
+ "MPLS or IP Label\n"
+ "Label\n"
+ "Label Value\n"
+ "Segment NAI\n"
+ "NAI node identifier\n"
+ "NAI IPv4 node identifier\n"
+ "NAI IPv6 node identifier\n")
+{
+ char xpath[XPATH_MAXLEN];
+ const char *node_id;
+
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']/sid-value",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, label_str);
+
+ if (has_nai != NULL) {
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai/type",
+ index_str);
+ if (node_ipv4_str != NULL) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ "ipv4_node");
+ node_id = node_ipv4_str;
+ } else if (node_ipv6_str != NULL) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ "ipv6_node");
+ node_id = node_ipv6_str;
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+ snprintf(xpath, sizeof(xpath),
+ "./segment[index='%s']/nai/local-address", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, node_id);
+ } else {
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']/nai",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_segment_list_no_segment,
+ srte_segment_list_no_segment_cmd,
+ "no index (0-4294967295)$index",
+ NO_STR
+ "Index\n"
+ "Index Value\n")
+{
+ char xpath[XPATH_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_srte_segment_list_segment(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " index %s mpls label %s",
+ yang_dnode_get_string(dnode, "./index"),
+ yang_dnode_get_string(dnode, "./sid-value"));
+ if (yang_dnode_exists(dnode, "./nai")) {
+ struct ipaddr addr;
+ switch (yang_dnode_get_enum(dnode, "./nai/type")) {
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
+ yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
+ vty_out(vty, " nai node %pI4", &addr.ipaddr_v4);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
+ yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
+ vty_out(vty, " nai node %pI6", &addr.ipaddr_v6);
+ break;
+ default:
+ break;
+ }
+ }
+ vty_out(vty, "\n");
+}
+
+/*
+ * XPath: /frr-pathd:pathd/policy
+ */
+DEFPY_NOSH(
+ srte_policy,
+ srte_policy_cmd,
+ "policy color (0-4294967295)$num endpoint <A.B.C.D|X:X::X:X>$endpoint",
+ "Segment Routing Policy\n"
+ "SR Policy color\n"
+ "SR Policy color value\n"
+ "SR Policy endpoint\n"
+ "SR Policy endpoint IPv4 address\n"
+ "SR Policy endpoint IPv6 address\n")
+{
+ char xpath[XPATH_POLICY_BASELEN];
+ int ret;
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/policy[color='%s'][endpoint='%s']",
+ num_str, endpoint_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ ret = nb_cli_apply_changes(vty, NULL);
+ if (ret == CMD_SUCCESS)
+ VTY_PUSH_XPATH(SR_POLICY_NODE, xpath);
+
+ return ret;
+}
+
+DEFPY(srte_no_policy,
+ srte_no_policy_cmd,
+ "no policy color (0-4294967295)$num endpoint <A.B.C.D|X:X::X:X>$endpoint",
+ NO_STR
+ "Segment Routing Policy\n"
+ "SR Policy color\n"
+ "SR Policy color value\n"
+ "SR Policy endpoint\n"
+ "SR Policy endpoint IPv4 address\n"
+ "SR Policy endpoint IPv6 address\n")
+{
+ char xpath[XPATH_POLICY_BASELEN];
+
+ snprintf(xpath, sizeof(xpath),
+ "/frr-pathd:pathd/srte/policy[color='%s'][endpoint='%s']",
+ num_str, endpoint_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_srte_policy(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " policy color %s endpoint %s\n",
+ yang_dnode_get_string(dnode, "./color"),
+ yang_dnode_get_string(dnode, "./endpoint"));
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/name
+ */
+DEFPY(srte_policy_name,
+ srte_policy_name_cmd,
+ "name WORD$name",
+ "Segment Routing Policy name\n"
+ "SR Policy name value\n")
+{
+ nb_cli_enqueue_change(vty, "./name", NB_OP_CREATE, name);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_policy_no_name,
+ srte_policy_no_name_cmd,
+ "no name [WORD]",
+ NO_STR
+ "Segment Routing Policy name\n"
+ "SR Policy name value\n")
+{
+ nb_cli_enqueue_change(vty, "./name", NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+void cli_show_srte_policy_name(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " name %s\n", yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/binding-sid
+ */
+DEFPY(srte_policy_binding_sid,
+ srte_policy_binding_sid_cmd,
+ "binding-sid (16-1048575)$label",
+ "Segment Routing Policy Binding-SID\n"
+ "SR Policy Binding-SID label\n")
+{
+ nb_cli_enqueue_change(vty, "./binding-sid", NB_OP_CREATE, label_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_policy_no_binding_sid,
+ srte_policy_no_binding_sid_cmd,
+ "no binding-sid [(16-1048575)]",
+ NO_STR
+ "Segment Routing Policy Binding-SID\n"
+ "SR Policy Binding-SID label\n")
+{
+ nb_cli_enqueue_change(vty, "./binding-sid", NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_srte_policy_binding_sid(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " binding-sid %s\n", yang_dnode_get_string(dnode, NULL));
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path
+ */
+DEFPY(srte_policy_candidate_exp,
+ srte_policy_candidate_exp_cmd,
+ "candidate-path preference (0-4294967295)$preference name WORD$name \
+ explicit segment-list WORD$list_name",
+ "Segment Routing Policy Candidate Path\n"
+ "Segment Routing Policy Candidate Path Preference\n"
+ "Administrative Preference\n"
+ "Segment Routing Policy Candidate Path Name\n"
+ "Symbolic Name\n"
+ "Explicit Path\n"
+ "List of SIDs\n"
+ "Name of the Segment List\n")
+{
+ nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, preference_str);
+ nb_cli_enqueue_change(vty, "./name", NB_OP_MODIFY, name);
+ nb_cli_enqueue_change(vty, "./protocol-origin", NB_OP_MODIFY, "local");
+ nb_cli_enqueue_change(vty, "./originator", NB_OP_MODIFY, "config");
+ nb_cli_enqueue_change(vty, "./type", NB_OP_MODIFY, "explicit");
+ nb_cli_enqueue_change(vty, "./segment-list-name", NB_OP_MODIFY,
+ list_name);
+ return nb_cli_apply_changes(vty, "./candidate-path[preference='%s']",
+ preference_str);
+}
+
+DEFPY_NOSH(
+ srte_policy_candidate_dyn,
+ srte_policy_candidate_dyn_cmd,
+ "candidate-path preference (0-4294967295)$preference name WORD$name dynamic",
+ "Segment Routing Policy Candidate Path\n"
+ "Segment Routing Policy Candidate Path Preference\n"
+ "Administrative Preference\n"
+ "Segment Routing Policy Candidate Path Name\n"
+ "Symbolic Name\n"
+ "Dynamic Path\n")
+{
+ char xpath[XPATH_MAXLEN + XPATH_CANDIDATE_BASELEN];
+ int ret;
+
+ snprintf(xpath, sizeof(xpath), "%s/candidate-path[preference='%s']",
+ VTY_CURR_XPATH, preference_str);
+
+ nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, preference_str);
+ nb_cli_enqueue_change(vty, "./name", NB_OP_MODIFY, name);
+ nb_cli_enqueue_change(vty, "./protocol-origin", NB_OP_MODIFY, "local");
+ nb_cli_enqueue_change(vty, "./originator", NB_OP_MODIFY, "config");
+ nb_cli_enqueue_change(vty, "./type", NB_OP_MODIFY, "dynamic");
+ ret = nb_cli_apply_changes(vty, "./candidate-path[preference='%s']",
+ preference_str);
+
+ if (ret == CMD_SUCCESS)
+ VTY_PUSH_XPATH(SR_CANDIDATE_DYN_NODE, xpath);
+
+ return ret;
+}
+
+DEFPY(srte_candidate_bandwidth,
+ srte_candidate_bandwidth_cmd,
+ "bandwidth BANDWIDTH$value [required$required]",
+ "Define a bandwidth constraint\n"
+ "Bandwidth value\n"
+ "Required constraint\n")
+{
+ nb_cli_enqueue_change(vty, "./constraints/bandwidth/required",
+ NB_OP_MODIFY, required ? "true" : "false");
+ nb_cli_enqueue_change(vty, "./constraints/bandwidth/value",
+ NB_OP_MODIFY, value);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_no_bandwidth,
+ srte_candidate_no_bandwidth_cmd,
+ "no bandwidth [BANDWIDTH$value] [required$required]",
+ NO_STR
+ "Remove a bandwidth constraint\n"
+ "Bandwidth value\n"
+ "Required constraint\n")
+{
+ nb_cli_enqueue_change(vty, "./constraints/bandwidth", NB_OP_DESTROY,
+ NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_affinity_filter,
+ srte_candidate_affinity_filter_cmd,
+ "affinity {exclude-any|include-any|include-all}$type BITPATTERN$value",
+ "Affinity constraint\n"
+ "Exclude any matching link\n"
+ "Include any matching link\n"
+ "Include all matching links\n"
+ "Attribute filter bit pattern as an hexadecimal value from 0x00000000 to 0xFFFFFFFF\n")
+{
+ uint32_t filter;
+ char xpath[XPATH_CANDIDATE_MAXLEN];
+ char decimal_value[11];
+
+ if (sscanf(value, "0x%x", &filter) != 1) {
+ vty_out(vty, "affinity type: fscanf: %s\n",
+ safe_strerror(errno));
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ snprintf(decimal_value, sizeof(decimal_value), "%u", filter);
+ snprintf(xpath, sizeof(xpath), "./constraints/affinity/%s", type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, decimal_value);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_no_affinity_filter,
+ srte_candidate_no_affinity_filter_cmd,
+ "no affinity {exclude-any|include-any|include-all}$type [BITPATTERN$value]",
+ NO_STR
+ "Affinity constraint\n"
+ "Exclude any matching link\n"
+ "Include any matching link\n"
+ "Include all matching links\n"
+ "Attribute filter bit pattern as an hexadecimal value from 0x00000000 to 0xFFFFFFFF\n")
+{
+ char xpath[XPATH_CANDIDATE_MAXLEN];
+
+ snprintf(xpath, sizeof(xpath), "./constraints/affinity/%s", type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_metric,
+ srte_candidate_metric_cmd,
+ "metric [bound$bound] <igp|te|hc|abc|lmll|cigp|cte|pigp|pte|phc|msd|pd|pdv|pl|ppd|ppdv|ppl|nap|nlp|dc|bnc>$type METRIC$value [required$required]",
+ "Define a metric constraint\n"
+ "If the metric is bounded\n"
+ "IGP metric\n"
+ "TE metric\n"
+ "Hop Counts\n"
+ "Aggregate bandwidth consumption\n"
+ "Load of the most loaded link\n"
+ "Cumulative IGP cost\n"
+ "Cumulative TE cost\n"
+ "P2MP IGP metric\n"
+ "P2MP TE metric\n"
+ "P2MP hop count metric\n"
+ "Segment-ID (SID) Depth.\n"
+ "Path Delay metric\n"
+ "Path Delay Variation metric\n"
+ "Path Loss metric\n"
+ "P2MP Path Delay metric\n"
+ "P2MP Path Delay variation metric\n"
+ "P2MP Path Loss metric\n"
+ "Number of adaptations on a path\n"
+ "Number of layers on a path\n"
+ "Domain Count metric\n"
+ "Border Node Count metric\n"
+ "Metric value\n"
+ "Required constraint\n")
+{
+ char xpath[XPATH_CANDIDATE_MAXLEN];
+ snprintf(xpath, sizeof(xpath), "./constraints/metrics[type='%s']/value",
+ type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, value);
+ snprintf(xpath, sizeof(xpath),
+ "./constraints/metrics[type='%s']/is-bound", type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ (bound != NULL) ? "true" : "false");
+ snprintf(xpath, sizeof(xpath),
+ "./constraints/metrics[type='%s']/required", type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ required ? "true" : "false");
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_no_metric,
+ srte_candidate_no_metric_cmd,
+ "no metric [bound] <igp|te|hc|abc|lmll|cigp|cte|pigp|pte|phc|msd|pd|pdv|pl|ppd|ppdv|ppl|nap|nlp|dc|bnc>$type [METRIC$value] [required$required]",
+ NO_STR
+ "Remove a metric constraint\n"
+ "If the metric is bounded\n"
+ "IGP metric\n"
+ "TE metric\n"
+ "Hop Counts\n"
+ "Aggregate bandwidth consumption\n"
+ "Load of the most loaded link\n"
+ "Cumulative IGP cost\n"
+ "Cumulative TE cost\n"
+ "P2MP IGP metric\n"
+ "P2MP TE metric\n"
+ "P2MP hop count metric\n"
+ "Segment-ID (SID) Depth.\n"
+ "Path Delay metric\n"
+ "Path Delay Variation metric\n"
+ "Path Loss metric\n"
+ "P2MP Path Delay metric\n"
+ "P2MP Path Delay variation metric\n"
+ "P2MP Path Loss metric\n"
+ "Number of adaptations on a path\n"
+ "Number of layers on a path\n"
+ "Domain Count metric\n"
+ "Border Node Count metric\n"
+ "Metric value\n"
+ "Required constraint\n")
+{
+ char xpath[XPATH_CANDIDATE_MAXLEN];
+ snprintf(xpath, sizeof(xpath), "./constraints/metrics[type='%s']",
+ type);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_policy_no_candidate,
+ srte_policy_no_candidate_cmd,
+ "no candidate-path\
+ preference (0-4294967295)$preference\
+ [name WORD\
+ <\
+ explicit segment-list WORD\
+ |dynamic\
+ >]",
+ NO_STR
+ "Segment Routing Policy Candidate Path\n"
+ "Segment Routing Policy Candidate Path Preference\n"
+ "Administrative Preference\n"
+ "Segment Routing Policy Candidate Path Name\n"
+ "Symbolic Name\n"
+ "Explicit Path\n"
+ "List of SIDs\n"
+ "Name of the Segment List\n"
+ "Dynamic Path\n")
+{
+ nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, "./candidate-path[preference='%s']",
+ preference_str);
+}
+
+DEFPY(srte_candidate_objfun,
+ srte_candidate_objfun_cmd,
+ "objective-function <mcp|mlp|mbp|mbc|mll|mcc|spt|mct|mplp|mup|mrup|mtd|mbn|mctd|msl|mss|msn>$type [required$required]",
+ "Define an objective function constraint\n"
+ "Minimum Cost Path\n"
+ "Minimum Load Path\n"
+ "Maximum residual Bandwidth Path\n"
+ "Minimize aggregate Bandwidth Consumption\n"
+ "Minimize the Load of the most loaded Link\n"
+ "Minimize the Cumulative Cost of a set of paths\n"
+ "Shortest Path Tree\n"
+ "Minimum Cost Tree\n"
+ "Minimum Packet Loss Path\n"
+ "Maximum Under-Utilized Path\n"
+ "Maximum Reserved Under-Utilized Path\n"
+ "Minimize the number of Transit Domains\n"
+ "Minimize the number of Border Nodes\n"
+ "Minimize the number of Common Transit Domains\n"
+ "Minimize the number of Shared Links\n"
+ "Minimize the number of Shared SRLGs\n"
+ "Minimize the number of Shared Nodes\n"
+ "Required constraint\n")
+{
+ char xpath[XPATH_CANDIDATE_MAXLEN];
+ nb_cli_enqueue_change(vty, "./constraints/objective-function",
+ NB_OP_DESTROY, NULL);
+ snprintf(xpath, sizeof(xpath),
+ "./constraints/objective-function/required");
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ required ? "true" : "false");
+ nb_cli_enqueue_change(vty, "./constraints/objective-function/type",
+ NB_OP_MODIFY, type);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY(srte_candidate_no_objfun,
+ srte_candidate_no_objfun_cmd,
+ "no objective-function [<mcp|mlp|mbp|mbc|mll|mcc|spt|mct|mplp|mup|mrup|mtd|mbn|mctd|msl|mss|msn>] [required$required]",
+ NO_STR
+ "Remove an objective function constraint\n"
+ "Minimum Cost Path\n"
+ "Minimum Load Path\n"
+ "Maximum residual Bandwidth Path\n"
+ "Minimize aggregate Bandwidth Consumption\n"
+ "Minimize the Load of the most loaded Link\n"
+ "Minimize the Cumulative Cost of a set of paths\n"
+ "Shortest Path Tree\n"
+ "Minimum Cost Tree\n"
+ "Minimum Packet Loss Path\n"
+ "Maximum Under-Utilized Path\n"
+ "Maximum Reserved Under-Utilized Path\n"
+ "Minimize the number of Transit Domains\n"
+ "Minimize the number of Border Nodes\n"
+ "Minimize the number of Common Transit Domains\n"
+ "Minimize the number of Shared Links\n"
+ "Minimize the number of Shared SRLGs\n"
+ "Minimize the number of Shared Nodes\n"
+ "Required constraint\n")
+{
+ nb_cli_enqueue_change(vty, "./constraints/objective-function",
+ NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+static const char *objfun_type_name(enum objfun_type type)
+{
+ switch (type) {
+ case OBJFUN_MCP:
+ return "mcp";
+ case OBJFUN_MLP:
+ return "mlp";
+ case OBJFUN_MBP:
+ return "mbp";
+ case OBJFUN_MBC:
+ return "mbc";
+ case OBJFUN_MLL:
+ return "mll";
+ case OBJFUN_MCC:
+ return "mcc";
+ case OBJFUN_SPT:
+ return "spt";
+ case OBJFUN_MCT:
+ return "mct";
+ case OBJFUN_MPLP:
+ return "mplp";
+ case OBJFUN_MUP:
+ return "mup";
+ case OBJFUN_MRUP:
+ return "mrup";
+ case OBJFUN_MTD:
+ return "mtd";
+ case OBJFUN_MBN:
+ return "mbn";
+ case OBJFUN_MCTD:
+ return "mctd";
+ case OBJFUN_MSL:
+ return "msl";
+ case OBJFUN_MSS:
+ return "mss";
+ case OBJFUN_MSN:
+ return "msn";
+ default:
+ return NULL;
+ }
+}
+
+DEFPY_NOSH(show_debugging_pathd, show_debugging_pathd_cmd,
+ "show debugging [pathd]",
+ SHOW_STR
+ "State of each debugging option\n"
+ "pathd module debugging\n")
+{
+ /* nothing to do here */
+ return CMD_SUCCESS;
+}
+
+static const char *metric_type_name(enum srte_candidate_metric_type type)
+{
+ switch (type) {
+ case SRTE_CANDIDATE_METRIC_TYPE_IGP:
+ return "igp";
+ case SRTE_CANDIDATE_METRIC_TYPE_TE:
+ return "te";
+ case SRTE_CANDIDATE_METRIC_TYPE_HC:
+ return "hc";
+ case SRTE_CANDIDATE_METRIC_TYPE_ABC:
+ return "abc";
+ case SRTE_CANDIDATE_METRIC_TYPE_LMLL:
+ return "lmll";
+ case SRTE_CANDIDATE_METRIC_TYPE_CIGP:
+ return "cigp";
+ case SRTE_CANDIDATE_METRIC_TYPE_CTE:
+ return "cte";
+ case SRTE_CANDIDATE_METRIC_TYPE_PIGP:
+ return "pigp";
+ case SRTE_CANDIDATE_METRIC_TYPE_PTE:
+ return "pte";
+ case SRTE_CANDIDATE_METRIC_TYPE_PHC:
+ return "phc";
+ case SRTE_CANDIDATE_METRIC_TYPE_MSD:
+ return "msd";
+ case SRTE_CANDIDATE_METRIC_TYPE_PD:
+ return "pd";
+ case SRTE_CANDIDATE_METRIC_TYPE_PDV:
+ return "pdv";
+ case SRTE_CANDIDATE_METRIC_TYPE_PL:
+ return "pl";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPD:
+ return "ppd";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPDV:
+ return "ppdv";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPL:
+ return "ppl";
+ case SRTE_CANDIDATE_METRIC_TYPE_NAP:
+ return "nap";
+ case SRTE_CANDIDATE_METRIC_TYPE_NLP:
+ return "nlp";
+ case SRTE_CANDIDATE_METRIC_TYPE_DC:
+ return "dc";
+ case SRTE_CANDIDATE_METRIC_TYPE_BNC:
+ return "bnc";
+ default:
+ return NULL;
+ }
+}
+
+static void config_write_float(struct vty *vty, float value)
+{
+ if (fabs(truncf(value) - value) < FLT_EPSILON) {
+ vty_out(vty, " %d", (int)value);
+ return;
+ } else {
+ vty_out(vty, " %f", value);
+ }
+}
+
+static void config_write_metric(struct vty *vty,
+ enum srte_candidate_metric_type type,
+ float value, bool required, bool is_bound)
+{
+ const char *name = metric_type_name(type);
+ if (name == NULL)
+ return;
+ vty_out(vty, " metric %s%s", is_bound ? "bound " : "",
+ metric_type_name(type));
+ config_write_float(vty, value);
+ vty_out(vty, required ? " required" : "");
+ vty_out(vty, "\n");
+}
+
+static int config_write_metric_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct vty *vty = arg;
+ enum srte_candidate_metric_type type;
+ bool required, is_bound = false;
+ float value;
+
+ type = yang_dnode_get_enum(dnode, "./type");
+ value = (float)yang_dnode_get_dec64(dnode, "./value");
+ required = yang_dnode_get_bool(dnode, "./required");
+ if (yang_dnode_exists(dnode, "./is-bound"))
+ is_bound = yang_dnode_get_bool(dnode, "./is-bound");
+
+ config_write_metric(vty, type, value, required, is_bound);
+ return YANG_ITER_CONTINUE;
+}
+
+void cli_show_srte_policy_candidate_path(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults)
+{
+ float bandwidth;
+ uint32_t affinity;
+ bool required;
+ enum objfun_type objfun_type;
+ const char *type = yang_dnode_get_string(dnode, "./type");
+
+ vty_out(vty, " candidate-path preference %s name %s %s",
+ yang_dnode_get_string(dnode, "./preference"),
+ yang_dnode_get_string(dnode, "./name"), type);
+ if (strmatch(type, "explicit"))
+ vty_out(vty, " segment-list %s",
+ yang_dnode_get_string(dnode, "./segment-list-name"));
+ vty_out(vty, "\n");
+
+ if (strmatch(type, "dynamic")) {
+ if (yang_dnode_exists(dnode, "./constraints/bandwidth")) {
+ bandwidth = (float)yang_dnode_get_dec64(
+ dnode, "./constraints/bandwidth/value");
+ required = yang_dnode_get_bool(
+ dnode, "./constraints/bandwidth/required");
+ vty_out(vty, " %sbandwidth",
+ required ? "required " : "");
+ config_write_float(vty, bandwidth);
+ vty_out(vty, "\n");
+ }
+ if (yang_dnode_exists(dnode,
+ "./constraints/affinity/exclude-any")) {
+ affinity = yang_dnode_get_uint32(
+ dnode, "./constraints/affinity/exclude-any");
+ vty_out(vty, " affinity exclude-any 0x%08x\n",
+ affinity);
+ }
+ if (yang_dnode_exists(dnode,
+ "./constraints/affinity/include-any")) {
+ affinity = yang_dnode_get_uint32(
+ dnode, "./constraints/affinity/include-any");
+ vty_out(vty, " affinity include-any 0x%08x\n",
+ affinity);
+ }
+ if (yang_dnode_exists(dnode,
+ "./constraints/affinity/include-all")) {
+ affinity = yang_dnode_get_uint32(
+ dnode, "./constraints/affinity/include-all");
+ vty_out(vty, " affinity include-all 0x%08x\n",
+ affinity);
+ }
+ yang_dnode_iterate(config_write_metric_cb, vty, dnode,
+ "./constraints/metrics");
+ if (yang_dnode_exists(dnode,
+ "./constraints/objective-function")) {
+ objfun_type = yang_dnode_get_enum(dnode,
+ "./constraints/objective-function/type");
+ required = yang_dnode_get_bool(dnode,
+ "./constraints/objective-function/required");
+ vty_out(vty, " objective-function %s%s\n",
+ objfun_type_name(objfun_type),
+ required ? " required" : "");
+ }
+ }
+}
+
+static int config_write_dnode(const struct lyd_node *dnode, void *arg)
+{
+ struct vty *vty = arg;
+
+ nb_cli_show_dnode_cmds(vty, (struct lyd_node *)dnode, false);
+
+ return YANG_ITER_CONTINUE;
+}
+
+int config_write_segment_routing(struct vty *vty)
+{
+ vty_out(vty, "segment-routing\n");
+ return 1;
+}
+
+int config_write_traffic_eng(struct vty *vty)
+{
+ vty_out(vty, " traffic-eng\n");
+ return 1;
+}
+
+int config_write_segment_lists(struct vty *vty)
+{
+ yang_dnode_iterate(config_write_dnode, vty, running_config->dnode,
+ "/frr-pathd:pathd/srte/segment-list");
+
+ return 1;
+}
+
+int config_write_sr_policies(struct vty *vty)
+{
+ yang_dnode_iterate(config_write_dnode, vty, running_config->dnode,
+ "/frr-pathd:pathd/srte/policy");
+
+ return 1;
+}
+
+void path_cli_init(void)
+{
+ install_node(&segment_routing_node);
+ install_node(&sr_traffic_eng_node);
+ install_node(&srte_segment_list_node);
+ install_node(&srte_policy_node);
+ install_node(&srte_candidate_dyn_node);
+ install_default(SEGMENT_ROUTING_NODE);
+ install_default(SR_TRAFFIC_ENG_NODE);
+ install_default(SR_SEGMENT_LIST_NODE);
+ install_default(SR_POLICY_NODE);
+ install_default(SR_CANDIDATE_DYN_NODE);
+
+ install_element(ENABLE_NODE, &show_debugging_pathd_cmd);
+ install_element(ENABLE_NODE, &show_srte_policy_cmd);
+ install_element(ENABLE_NODE, &show_srte_policy_detail_cmd);
+
+ install_element(CONFIG_NODE, &segment_routing_cmd);
+ install_element(SEGMENT_ROUTING_NODE, &sr_traffic_eng_cmd);
+ install_element(SR_TRAFFIC_ENG_NODE, &srte_segment_list_cmd);
+ install_element(SR_TRAFFIC_ENG_NODE, &srte_no_segment_list_cmd);
+ install_element(SR_SEGMENT_LIST_NODE,
+ &srte_segment_list_segment_cmd);
+ install_element(SR_SEGMENT_LIST_NODE,
+ &srte_segment_list_no_segment_cmd);
+ install_element(SR_TRAFFIC_ENG_NODE, &srte_policy_cmd);
+ install_element(SR_TRAFFIC_ENG_NODE, &srte_no_policy_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_name_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_no_name_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_binding_sid_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_no_binding_sid_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_candidate_exp_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_candidate_dyn_cmd);
+ install_element(SR_POLICY_NODE, &srte_policy_no_candidate_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_bandwidth_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_no_bandwidth_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_affinity_filter_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_no_affinity_filter_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_metric_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_no_metric_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_objfun_cmd);
+ install_element(SR_CANDIDATE_DYN_NODE,
+ &srte_candidate_no_objfun_cmd);
+}
diff --git a/pathd/path_debug.c b/pathd/path_debug.c
new file mode 100644
index 0000000000..df0550715a
--- /dev/null
+++ b/pathd/path_debug.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <stdbool.h>
+#include <time.h>
+#include <libyang/libyang.h>
+
+#include "printfrr.h"
+#include "ipaddr.h"
+
+#include "pathd/path_debug.h"
+
+THREAD_DATA char _debug_buff[DEBUG_BUFF_SIZE];
+
+/**
+ * Gives the string representation of an srte_protocol_origin enum value.
+ *
+ * @param origin The enum value to convert to string
+ * @return a constant string representation of the enum value
+ */
+const char *srte_protocol_origin_name(enum srte_protocol_origin origin)
+{
+ switch (origin) {
+ case SRTE_ORIGIN_UNDEFINED:
+ return "UNDEFINED";
+ case SRTE_ORIGIN_PCEP:
+ return "PCEP";
+ case SRTE_ORIGIN_BGP:
+ return "BGP";
+ case SRTE_ORIGIN_LOCAL:
+ return "LOCAL";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/**
+ * Gives the string representation of an srte_candidate_type enum value.
+ *
+ * @param origin The enum value to convert to string
+ * @return a constant string representation of the enum value
+ */
+const char *srte_candidate_type_name(enum srte_candidate_type type)
+{
+ switch (type) {
+ case SRTE_CANDIDATE_TYPE_EXPLICIT:
+ return "EXPLICIT";
+ case SRTE_CANDIDATE_TYPE_DYNAMIC:
+ return "DYNAMIC";
+ case SRTE_CANDIDATE_TYPE_UNDEFINED:
+ return "UNDEFINED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/**
+ * Gives the string representation of an objfun_type enum value.
+ *
+ * @param origin The enum value to convert to string
+ * @return a constant string representation of the enum value
+ */
+const char *objfun_type_name(enum objfun_type type)
+{
+ switch (type) {
+ case OBJFUN_UNDEFINED:
+ return "UNDEFINED";
+ case OBJFUN_MCP:
+ return "MCP";
+ case OBJFUN_MLP:
+ return "MLP";
+ case OBJFUN_MBP:
+ return "MBP";
+ case OBJFUN_MBC:
+ return "MBC";
+ case OBJFUN_MLL:
+ return "MLL";
+ case OBJFUN_MCC:
+ return "MCC";
+ case OBJFUN_SPT:
+ return "SPT";
+ case OBJFUN_MCT:
+ return "MCT";
+ case OBJFUN_MPLP:
+ return "MPLP";
+ case OBJFUN_MUP:
+ return "MUP";
+ case OBJFUN_MRUP:
+ return "MRUP";
+ case OBJFUN_MTD:
+ return "MTD";
+ case OBJFUN_MBN:
+ return "MBN";
+ case OBJFUN_MCTD:
+ return "MCTD";
+ case OBJFUN_MSL:
+ return "MSL";
+ case OBJFUN_MSS:
+ return "MSS";
+ case OBJFUN_MSN:
+ return "MSN";
+ default:
+ return "UNKNOWN";
+ }
+}
diff --git a/pathd/path_debug.h b/pathd/path_debug.h
new file mode 100644
index 0000000000..d9cfcb6ba2
--- /dev/null
+++ b/pathd/path_debug.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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_DEBUG_H_
+#define _PATH_DEBUG_H_
+
+#include "pathd/pathd.h"
+
+#ifdef __GNUC__
+#define THREAD_DATA __thread
+#else
+#define THREAD_DATA
+#endif
+
+#define DEBUG_IDENT_SIZE 4
+#define DEBUG_BUFF_SIZE 4096
+#define TUP(A, B) ((((uint32_t)(A)) << 16) | ((uint32_t)(B)))
+#define PATHD_FORMAT_INIT() _debug_buff[0] = 0
+#define PATHD_FORMAT(fmt, ...) \
+ csnprintfrr(_debug_buff, DEBUG_BUFF_SIZE, fmt, ##__VA_ARGS__)
+#define PATHD_FORMAT_FINI() _debug_buff
+
+extern THREAD_DATA char _debug_buff[DEBUG_BUFF_SIZE];
+
+const char *srte_protocol_origin_name(enum srte_protocol_origin origin);
+const char *srte_candidate_type_name(enum srte_candidate_type type);
+const char *objfun_type_name(enum objfun_type type);
+
+#endif // _PATH_DEBUG_H_ \ No newline at end of file
diff --git a/pathd/path_errors.c b/pathd/path_errors.c
new file mode 100644
index 0000000000..f8560a848c
--- /dev/null
+++ b/pathd/path_errors.c
@@ -0,0 +1,134 @@
+/*
+ * pathd-specific error messages.
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "lib/ferr.h"
+#include "path_errors.h"
+
+/* clang-format off */
+static struct log_ref ferr_path_err[] = {
+ {
+ .code = EC_PATH_SYSTEM_CALL,
+ .title = "Thread setup error",
+ .description = "A system call for creating, or setting up PCEP module's pthread failed",
+ .suggestion = "Open an Issue with all relevant log files and restart FRR"
+ },
+ {
+ .code = EC_PATH_PCEP_PCC_INIT,
+ .title = "PCC initialization error",
+ .description = "pceplib PCC initialization call failed",
+ .suggestion = "Open an Issue with all relevant log files and restart FRR"
+ },
+ {
+ .code = EC_PATH_PCEP_PCC_FINI,
+ .title = "PCC finalization error",
+ .description = "pceplib PCC finalization call failed",
+ .suggestion = "Open an Issue with all relevant log files and restart FRR"
+ },
+ {
+ .code = EC_PATH_PCEP_PCC_CONF_UPDATE,
+ .title = "PCC configuration update error",
+ .description = "The update of the PCC configuration failed",
+ .suggestion = "Open an Issue with all relevant log files and restart FRR"
+ },
+ {
+ .code = END_FERR,
+ }
+};
+
+static struct log_ref ferr_path_warn[] = {
+ {
+ .code = EC_PATH_PCEP_LIB_CONNECT,
+ .title = "PCC connection error",
+ .description = "The PCEP module failed to connected to configured PCE",
+ .suggestion = "Check the connectivity between the PCC and the PCE"
+ },
+ {
+ .code = EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
+ .title = "PCC connection error",
+ .description = "The PCEP module did not try to connect because it is missing a source address",
+ .suggestion = "Wait for the router ID to be defined or set the PCC source address in the configuration"
+ },
+ {
+ .code = EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ .title = "Recoverable internal error",
+ .description = "Some recoverable internal error",
+ .suggestion = "Open an Issue with all relevant log files"
+ },
+ {
+ .code = EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ .title = "Unsupported PCEP feature",
+ .description = "Receved an unsupported PCEP message",
+ .suggestion = "The PCC and PCE are probably not compatible. Open an Issue with all relevant log files"
+ },
+ {
+ .code = EC_PATH_PCEP_UNEXPECTED_PCEP_MESSAGE,
+ .title = "Unexpected PCEP message",
+ .description = "The PCEP module received an unexpected PCEP message",
+ .suggestion = "Open an Issue with all relevant log files"
+ },
+ {
+ .code = EC_PATH_PCEP_UNEXPECTED_PCEPLIB_EVENT,
+ .title = "Unexpected pceplib event",
+ .description = "The PCEP module received an unexpected event from pceplib",
+ .suggestion = "Open an Issue with all relevant log files"
+ },
+ {
+ .code = EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT,
+ .title = "Unexpected PCEP object",
+ .description = "The PCEP module received an unexpected PCEP object from a PCE",
+ .suggestion = "Open an Issue with all relevant log files"
+ },
+ {
+ .code = EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ .title = "Unexpected PCEP TLV",
+ .description = "The PCEP module received an unexpected PCEP TLV from a PCE",
+ .suggestion = "Open an Issue with all relevant log files"
+ },
+ {
+ .code = EC_PATH_PCEP_UNEXPECTED_PCEP_ERO_SUBOBJ,
+ .title = "Unexpected PCEP ERO sub-object",
+ .description = "The PCEP module received an unexpected PCEP ERO sub-object from a PCE",
+ .suggestion = "Open an Issue with all relevant log files"
+ },
+ {
+ .code = EC_PATH_PCEP_UNEXPECTED_SR_NAI,
+ .title = "Unexpected PCEP SR segment NAI",
+ .description = "The PCEP module received an SR segment with an unsupported NAI specification from the PCE",
+ .suggestion = "Open an Issue with all relevant log files"
+ },
+ {
+ .code = EC_PATH_PCEP_COMPUTATION_REQUEST_TIMEOUT,
+ .title = "Computation request timeout",
+ .description = "The PCE did not respond in time to the PCC computation request",
+ .suggestion = "The PCE is overloaded or incompatible with the PCC, try with a different PCE"
+ },
+ {
+ .code = END_FERR,
+ }
+
+};
+/* clang-format on */
+
+void path_error_init(void)
+{
+ log_ref_add(ferr_path_err);
+ log_ref_add(ferr_path_warn);
+}
diff --git a/pathd/path_errors.h b/pathd/path_errors.h
new file mode 100644
index 0000000000..72e127f26b
--- /dev/null
+++ b/pathd/path_errors.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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_ERRORS_H__
+#define __PATH_ERRORS_H__
+
+#include "lib/ferr.h"
+
+enum path_log_refs {
+ EC_PATH_PCEP_INIT = PATH_FERR_START,
+ EC_PATH_SYSTEM_CALL,
+ EC_PATH_PCEP_PCC_INIT,
+ EC_PATH_PCEP_PCC_FINI,
+ EC_PATH_PCEP_PCC_CONF_UPDATE,
+ EC_PATH_PCEP_LIB_CONNECT,
+ EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
+ EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ EC_PATH_PCEP_UNEXPECTED_PCEP_MESSAGE,
+ EC_PATH_PCEP_UNEXPECTED_PCEPLIB_EVENT,
+ EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT,
+ EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ EC_PATH_PCEP_UNEXPECTED_PCEP_ERO_SUBOBJ,
+ EC_PATH_PCEP_UNEXPECTED_SR_NAI,
+ EC_PATH_PCEP_COMPUTATION_REQUEST_TIMEOUT
+};
+
+extern void path_error_init(void);
+
+#endif
diff --git a/pathd/path_main.c b/pathd/path_main.c
new file mode 100644
index 0000000000..8b7d4aba48
--- /dev/null
+++ b/pathd/path_main.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "thread.h"
+#include "command.h"
+#include "log.h"
+#include "memory.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "libfrr.h"
+#include "vrf.h"
+#include "filter.h"
+
+#include "pathd.h"
+#include "path_nb.h"
+#include "path_zebra.h"
+#include "path_errors.h"
+
+char backup_config_file[256];
+
+zebra_capabilities_t _caps_p[] = {};
+
+struct zebra_privs_t pathd_privs = {
+#if defined(FRR_USER) && defined(FRR_GROUP)
+ .user = FRR_USER,
+ .group = FRR_GROUP,
+#endif
+#if defined(VTY_GROUP)
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0};
+
+struct option longopts[] = {{0}};
+
+/* Master of threads. */
+struct thread_master *master;
+
+static struct frr_daemon_info pathd_di;
+
+/* SIGHUP handler. */
+static void sighup(void)
+{
+ zlog_info("SIGHUP received");
+
+ /* Reload config file. */
+ vty_read_config(NULL, pathd_di.config_file, config_default);
+}
+
+/* SIGINT / SIGTERM handler. */
+static void sigint(void)
+{
+ zlog_notice("Terminating on signal");
+
+ exit(0);
+}
+
+/* SIGUSR1 handler. */
+static void sigusr1(void)
+{
+ zlog_rotate();
+}
+
+struct quagga_signal_t path_signals[] = {
+ {
+ .signal = SIGHUP,
+ .handler = &sighup,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+static const struct frr_yang_module_info *pathd_yang_modules[] = {
+ &frr_filter_info,
+ &frr_interface_info,
+ &frr_pathd_info,
+};
+
+#define PATH_VTY_PORT 2621
+
+FRR_DAEMON_INFO(pathd, PATH, .vty_port = PATH_VTY_PORT,
+
+ .proghelp = "Implementation of PATH.",
+
+ .signals = path_signals, .n_signals = array_size(path_signals),
+
+ .privs = &pathd_privs, .yang_modules = pathd_yang_modules,
+ .n_yang_modules = array_size(pathd_yang_modules), )
+
+int main(int argc, char **argv, char **envp)
+{
+ frr_preinit(&pathd_di, argc, argv);
+ frr_opt_add("", longopts, "");
+
+ while (1) {
+ int opt;
+
+ opt = frr_getopt(argc, argv, NULL);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ default:
+ frr_help_exit(1);
+ break;
+ }
+ }
+
+ master = frr_init();
+
+ access_list_init();
+
+ path_error_init();
+ path_zebra_init(master);
+ path_cli_init();
+
+ frr_config_fork();
+ frr_run(master);
+
+ /* Not reached. */
+ return 0;
+}
diff --git a/pathd/path_memory.c b/pathd/path_memory.c
new file mode 100644
index 0000000000..ad4904a298
--- /dev/null
+++ b/pathd/path_memory.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <memory.h>
+
+#include "pathd/path_memory.h"
+
+DEFINE_MGROUP(PATHD, "pathd")
diff --git a/pathd/path_memory.h b/pathd/path_memory.h
new file mode 100644
index 0000000000..e2f6787f66
--- /dev/null
+++ b/pathd/path_memory.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_PATH_MEMORY_H_
+#define _FRR_PATH_MEMORY_H_
+
+#include "memory.h"
+
+DECLARE_MGROUP(PATHD)
+
+#endif /* _FRR_PATH_MEMORY_H_ */
diff --git a/pathd/path_nb.c b/pathd/path_nb.c
new file mode 100644
index 0000000000..a210e31b9c
--- /dev/null
+++ b/pathd/path_nb.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "pathd/path_nb.h"
+
+static int iter_objfun_cb(const struct lyd_node *dnode, void *arg);
+static int dummy_create(struct nb_cb_create_args *args);
+static int dummy_modify(struct nb_cb_modify_args *args);
+static int dummy_destroy(struct nb_cb_destroy_args *args);
+
+struct of_cb_pref {
+ uint32_t index;
+ enum objfun_type type;
+ struct of_cb_pref *next;
+};
+
+struct of_cb_args {
+ struct of_cb_pref *first;
+ uint32_t free_slot;
+ struct of_cb_pref prefs[MAX_OBJFUN_TYPE];
+};
+
+/* clang-format off */
+const struct frr_yang_module_info frr_pathd_info = {
+ .name = "frr-pathd",
+ .nodes = {
+ {
+ .xpath = "/frr-pathd:pathd",
+ .cbs = {
+ .apply_finish = pathd_apply_finish,
+ },
+ .priority = NB_DFLT_PRIORITY + 1
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list",
+ .cbs = {
+ .create = pathd_srte_segment_list_create,
+ .cli_show = cli_show_srte_segment_list,
+ .destroy = pathd_srte_segment_list_destroy,
+ .get_next = pathd_srte_segment_list_get_next,
+ .get_keys = pathd_srte_segment_list_get_keys,
+ .lookup_entry = pathd_srte_segment_list_lookup_entry,
+ },
+ .priority = NB_DFLT_PRIORITY - 1
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list/protocol-origin",
+ .cbs = {
+ .modify = pathd_srte_segment_list_protocol_origin_modify,
+ },
+ .priority = NB_DFLT_PRIORITY - 1
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list/originator",
+ .cbs = {
+ .modify = pathd_srte_segment_list_originator_modify,
+ },
+ .priority = NB_DFLT_PRIORITY - 1
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list/segment",
+ .cbs = {
+ .create = pathd_srte_segment_list_segment_create,
+ .cli_show = cli_show_srte_segment_list_segment,
+ .destroy = pathd_srte_segment_list_segment_destroy,
+ },
+ .priority = NB_DFLT_PRIORITY - 1
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list/segment/sid-value",
+ .cbs = {
+ .modify = pathd_srte_segment_list_segment_sid_value_modify,
+ },
+ .priority = NB_DFLT_PRIORITY - 1
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai",
+ .cbs = {
+ .create = dummy_create,
+ .destroy = pathd_srte_segment_list_segment_nai_destroy,
+ .apply_finish = pathd_srte_segment_list_segment_nai_apply_finish
+ },
+ .priority = NB_DFLT_PRIORITY - 1
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/type",
+ .cbs = {.modify = dummy_modify}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/local-address",
+ .cbs = {.modify = dummy_modify}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/local-interface",
+ .cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/remote-address",
+ .cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/segment-list/segment/nai/remote-interface",
+ .cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy",
+ .cbs = {
+ .create = pathd_srte_policy_create,
+ .cli_show = cli_show_srte_policy,
+ .destroy = pathd_srte_policy_destroy,
+ .get_next = pathd_srte_policy_get_next,
+ .get_keys = pathd_srte_policy_get_keys,
+ .lookup_entry = pathd_srte_policy_lookup_entry,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/name",
+ .cbs = {
+ .modify = pathd_srte_policy_name_modify,
+ .cli_show = cli_show_srte_policy_name,
+ .destroy = pathd_srte_policy_name_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/binding-sid",
+ .cbs = {
+ .modify = pathd_srte_policy_binding_sid_modify,
+ .cli_show = cli_show_srte_policy_binding_sid,
+ .destroy = pathd_srte_policy_binding_sid_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/is-operational",
+ .cbs = {
+ .get_elem = pathd_srte_policy_is_operational_get_elem
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path",
+ .cbs = {
+ .create = pathd_srte_policy_candidate_path_create,
+ .cli_show = cli_show_srte_policy_candidate_path,
+ .destroy = pathd_srte_policy_candidate_path_destroy,
+ .get_next = pathd_srte_policy_candidate_path_get_next,
+ .get_keys = pathd_srte_policy_candidate_path_get_keys,
+ .lookup_entry = pathd_srte_policy_candidate_path_lookup_entry,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/name",
+ .cbs = {
+ .modify = pathd_srte_policy_candidate_path_name_modify,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/is-best-candidate-path",
+ .cbs = {
+ .get_elem = pathd_srte_policy_candidate_path_is_best_candidate_path_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/protocol-origin",
+ .cbs = {
+ .modify = pathd_srte_policy_candidate_path_protocol_origin_modify,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/originator",
+ .cbs = {
+ .modify = pathd_srte_policy_candidate_path_originator_modify,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/discriminator",
+ .cbs = {
+ .get_elem = pathd_srte_policy_candidate_path_discriminator_get_elem,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/type",
+ .cbs = {
+ .modify = pathd_srte_policy_candidate_path_type_modify,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/segment-list-name",
+ .cbs = {
+ .destroy = pathd_srte_policy_candidate_path_segment_list_name_destroy,
+ .modify = pathd_srte_policy_candidate_path_segment_list_name_modify,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth",
+ .cbs = {
+ .create = dummy_create,
+ .destroy = pathd_srte_policy_candidate_path_bandwidth_destroy,
+ .apply_finish = pathd_srte_policy_candidate_path_bandwidth_apply_finish
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth/required",
+ .cbs = {.modify = dummy_modify}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth/value",
+ .cbs = {.modify = dummy_modify}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/exclude-any",
+ .cbs = {
+ .modify = pathd_srte_policy_candidate_path_exclude_any_modify,
+ .destroy = pathd_srte_policy_candidate_path_exclude_any_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-any",
+ .cbs = {
+ .modify = pathd_srte_policy_candidate_path_include_any_modify,
+ .destroy = pathd_srte_policy_candidate_path_include_any_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-all",
+ .cbs = {
+ .modify = pathd_srte_policy_candidate_path_include_all_modify,
+ .destroy = pathd_srte_policy_candidate_path_include_all_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics",
+ .cbs = {
+ .create = dummy_create,
+ .destroy = pathd_srte_policy_candidate_path_metrics_destroy,
+ .apply_finish = pathd_srte_policy_candidate_path_metrics_apply_finish
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/value",
+ .cbs = {.modify = dummy_modify}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/required",
+ .cbs = {.modify = dummy_modify}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/is-bound",
+ .cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics/is-computed",
+ .cbs = {.modify = dummy_modify, .destroy = dummy_destroy}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function",
+ .cbs = {
+ .create = dummy_create,
+ .destroy = pathd_srte_policy_candidate_path_objfun_destroy,
+ .apply_finish = pathd_srte_policy_candidate_path_objfun_apply_finish
+ }
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function/required",
+ .cbs = {.modify = dummy_modify}
+ },
+ {
+ .xpath = "/frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function/type",
+ .cbs = {.modify = dummy_modify}
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
+
+void iter_objfun_prefs(const struct lyd_node *dnode, const char* path,
+ of_pref_cp_t fun, void *arg)
+{
+ struct of_cb_args args = {0};
+ struct of_cb_pref *p;
+
+ yang_dnode_iterate(iter_objfun_cb, &args, dnode, path);
+ for (p = args.first; p != NULL; p = p->next)
+ fun(p->type, arg);
+}
+
+int iter_objfun_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct of_cb_args *of_arg = arg;
+ struct of_cb_pref *pref;
+ struct of_cb_pref **p;
+
+ if (of_arg->free_slot >= MAX_OBJFUN_TYPE)
+ return YANG_ITER_STOP;
+
+ pref = &of_arg->prefs[of_arg->free_slot++];
+
+ pref->index = yang_dnode_get_uint32(dnode, "./index");
+ pref->type = yang_dnode_get_enum(dnode, "./type");
+
+ /* Simplistic insertion sort */
+ p = &of_arg->first;
+ while (true) {
+ if (*p == NULL) {
+ *p = pref;
+ break;
+ }
+ if ((*p)->index >= pref->index) {
+ pref->next = *p;
+ *p = pref;
+ break;
+ }
+ p = &(*p)->next;
+ }
+
+ return YANG_ITER_CONTINUE;
+}
+
+int dummy_create(struct nb_cb_create_args *args)
+{
+ return NB_OK;
+}
+
+int dummy_modify(struct nb_cb_modify_args *args)
+{
+ return NB_OK;
+}
+
+int dummy_destroy(struct nb_cb_destroy_args *args)
+{
+ return NB_OK;
+}
diff --git a/pathd/path_nb.h b/pathd/path_nb.h
new file mode 100644
index 0000000000..3a0b3863ce
--- /dev/null
+++ b/pathd/path_nb.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_PATH_NB_H_
+#define _FRR_PATH_NB_H_
+
+#include "pathd/pathd.h"
+
+extern const struct frr_yang_module_info frr_pathd_info;
+
+/* Mandatory callbacks. */
+int pathd_srte_segment_list_create(struct nb_cb_create_args *args);
+int pathd_srte_segment_list_destroy(struct nb_cb_destroy_args *args);
+
+const void *pathd_srte_segment_list_get_next(struct nb_cb_get_next_args *args);
+int pathd_srte_segment_list_get_keys(struct nb_cb_get_keys_args *args);
+const void *
+pathd_srte_segment_list_lookup_entry(struct nb_cb_lookup_entry_args *args);
+
+int pathd_srte_segment_list_segment_create(struct nb_cb_create_args *args);
+int pathd_srte_segment_list_segment_destroy(struct nb_cb_destroy_args *args);
+int pathd_srte_segment_list_protocol_origin_modify(
+ struct nb_cb_modify_args *args);
+int pathd_srte_segment_list_originator_modify(struct nb_cb_modify_args *args);
+int pathd_srte_segment_list_segment_sid_value_modify(
+ struct nb_cb_modify_args *args);
+int pathd_srte_segment_list_segment_nai_destroy(
+ struct nb_cb_destroy_args *args);
+void pathd_srte_segment_list_segment_nai_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+int pathd_srte_policy_create(struct nb_cb_create_args *args);
+int pathd_srte_policy_destroy(struct nb_cb_destroy_args *args);
+const void *pathd_srte_policy_get_next(struct nb_cb_get_next_args *args);
+int pathd_srte_policy_get_keys(struct nb_cb_get_keys_args *args);
+const void *
+pathd_srte_policy_lookup_entry(struct nb_cb_lookup_entry_args *args);
+int pathd_srte_policy_name_modify(struct nb_cb_modify_args *args);
+int pathd_srte_policy_name_destroy(struct nb_cb_destroy_args *args);
+int pathd_srte_policy_binding_sid_modify(struct nb_cb_modify_args *args);
+int pathd_srte_policy_binding_sid_destroy(struct nb_cb_destroy_args *args);
+struct yang_data *
+pathd_srte_policy_is_operational_get_elem(struct nb_cb_get_elem_args *args);
+int pathd_srte_policy_candidate_path_create(struct nb_cb_create_args *args);
+int pathd_srte_policy_candidate_path_destroy(struct nb_cb_destroy_args *args);
+int pathd_srte_policy_candidate_path_name_modify(
+ struct nb_cb_modify_args *args);
+const void *
+pathd_srte_policy_candidate_path_get_next(struct nb_cb_get_next_args *args);
+int pathd_srte_policy_candidate_path_get_keys(struct nb_cb_get_keys_args *args);
+const void *pathd_srte_policy_candidate_path_lookup_entry(
+ struct nb_cb_lookup_entry_args *args);
+void pathd_srte_policy_candidate_path_bandwidth_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+int pathd_srte_policy_candidate_path_bandwidth_destroy(
+ struct nb_cb_destroy_args *args);
+int pathd_srte_policy_candidate_path_exclude_any_modify(
+ struct nb_cb_modify_args *args);
+int pathd_srte_policy_candidate_path_exclude_any_destroy(
+ struct nb_cb_destroy_args *args);
+int pathd_srte_policy_candidate_path_include_any_modify(
+ struct nb_cb_modify_args *args);
+int pathd_srte_policy_candidate_path_include_any_destroy(
+ struct nb_cb_destroy_args *args);
+int pathd_srte_policy_candidate_path_include_all_modify(
+ struct nb_cb_modify_args *args);
+int pathd_srte_policy_candidate_path_include_all_destroy(
+ struct nb_cb_destroy_args *args);
+int pathd_srte_policy_candidate_path_metrics_destroy(
+ struct nb_cb_destroy_args *args);
+void pathd_srte_policy_candidate_path_metrics_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+int pathd_srte_policy_candidate_path_objfun_destroy(
+ struct nb_cb_destroy_args *args);
+void pathd_srte_policy_candidate_path_objfun_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+struct yang_data *
+pathd_srte_policy_candidate_path_is_best_candidate_path_get_elem(
+ struct nb_cb_get_elem_args *args);
+int pathd_srte_policy_candidate_path_protocol_origin_modify(
+ struct nb_cb_modify_args *args);
+int pathd_srte_policy_candidate_path_originator_modify(
+ struct nb_cb_modify_args *args);
+struct yang_data *pathd_srte_policy_candidate_path_discriminator_get_elem(
+ struct nb_cb_get_elem_args *args);
+int pathd_srte_policy_candidate_path_type_modify(
+ struct nb_cb_modify_args *args);
+int pathd_srte_policy_candidate_path_segment_list_name_modify(
+ struct nb_cb_modify_args *args);
+int pathd_srte_policy_candidate_path_segment_list_name_destroy(
+ struct nb_cb_destroy_args *args);
+
+/* Optional 'apply_finish' callbacks. */
+void pathd_apply_finish(struct nb_cb_apply_finish_args *args);
+
+/* Optional 'cli_show' callbacks. */
+void cli_show_srte_segment_list(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_segment_list_segment(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_policy(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_policy_name(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_policy_binding_sid(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_policy_candidate_path(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+
+/* Utility functions */
+typedef void (*of_pref_cp_t)(enum objfun_type type, void *arg);
+void iter_objfun_prefs(const struct lyd_node *dnode, const char *path,
+ of_pref_cp_t fun, void *arg);
+
+#endif /* _FRR_PATH_NB_H_ */
diff --git a/pathd/path_nb_config.c b/pathd/path_nb_config.c
new file mode 100644
index 0000000000..669db169ae
--- /dev/null
+++ b/pathd/path_nb_config.c
@@ -0,0 +1,731 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include <lib_errors.h>
+
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "pathd/path_zebra.h"
+#include "pathd/path_nb.h"
+
+/*
+ * XPath: /frr-pathd:pathd
+ */
+void pathd_apply_finish(struct nb_cb_apply_finish_args *args)
+{
+ srte_apply_changes();
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list
+ */
+int pathd_srte_segment_list_create(struct nb_cb_create_args *args)
+{
+ struct srte_segment_list *segment_list;
+ const char *name;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ name = yang_dnode_get_string(args->dnode, "./name");
+ segment_list = srte_segment_list_add(name);
+ nb_running_set_entry(args->dnode, segment_list);
+ SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW);
+
+ return NB_OK;
+}
+
+int pathd_srte_segment_list_destroy(struct nb_cb_destroy_args *args)
+{
+ struct srte_segment_list *segment_list;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ segment_list = nb_running_unset_entry(args->dnode);
+ SET_FLAG(segment_list->flags, F_SEGMENT_LIST_DELETED);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list/protocol-origin
+ */
+int pathd_srte_segment_list_protocol_origin_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct srte_segment_list *segment_list;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ segment_list = nb_running_get_entry(args->dnode, NULL, true);
+ segment_list->protocol_origin = yang_dnode_get_enum(args->dnode, NULL);
+ SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list/originator
+ */
+int pathd_srte_segment_list_originator_modify(struct nb_cb_modify_args *args)
+{
+ struct srte_segment_list *segment_list;
+ const char *originator;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ segment_list = nb_running_get_entry(args->dnode, NULL, true);
+ originator = yang_dnode_get_string(args->dnode, NULL);
+ strlcpy(segment_list->originator, originator,
+ sizeof(segment_list->originator));
+ SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
+
+ return NB_OK;
+}
+
+
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list/segment
+ */
+int pathd_srte_segment_list_segment_create(struct nb_cb_create_args *args)
+{
+ struct srte_segment_list *segment_list;
+ struct srte_segment_entry *segment;
+ uint32_t index;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ segment_list = nb_running_get_entry(args->dnode, NULL, true);
+ index = yang_dnode_get_uint32(args->dnode, "./index");
+ segment = srte_segment_entry_add(segment_list, index);
+ nb_running_set_entry(args->dnode, segment);
+ SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
+
+ return NB_OK;
+}
+
+int pathd_srte_segment_list_segment_destroy(struct nb_cb_destroy_args *args)
+{
+ struct srte_segment_entry *segment;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ segment = nb_running_unset_entry(args->dnode);
+ SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED);
+
+ srte_segment_entry_del(segment);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list/segment/sid-value
+ */
+int pathd_srte_segment_list_segment_sid_value_modify(
+ struct nb_cb_modify_args *args)
+{
+ mpls_label_t sid_value;
+ struct srte_segment_entry *segment;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ segment = nb_running_get_entry(args->dnode, NULL, true);
+ sid_value = yang_dnode_get_uint32(args->dnode, NULL);
+ segment->sid_value = sid_value;
+ SET_FLAG(segment->segment_list->flags, F_SEGMENT_LIST_MODIFIED);
+
+ return NB_OK;
+}
+
+int pathd_srte_segment_list_segment_nai_destroy(struct nb_cb_destroy_args *args)
+{
+ struct srte_segment_entry *segment;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ segment = nb_running_get_entry(args->dnode, NULL, true);
+ segment->nai_type = SRTE_SEGMENT_NAI_TYPE_NONE;
+ segment->nai_local_addr.ipa_type = IPADDR_NONE;
+ segment->nai_local_iface = 0;
+ segment->nai_remote_addr.ipa_type = IPADDR_NONE;
+ segment->nai_remote_iface = 0;
+
+ return NB_OK;
+}
+
+void pathd_srte_segment_list_segment_nai_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct srte_segment_entry *segment;
+ enum srte_segment_nai_type type;
+ struct ipaddr local_addr, remote_addr;
+ uint32_t local_iface = 0, remote_iface = 0;
+
+ segment = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_enum(args->dnode, "./type");
+
+ yang_dnode_get_ip(&local_addr, args->dnode, "./local-address");
+
+ switch (type) {
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY:
+ yang_dnode_get_ip(&remote_addr, args->dnode,
+ "./remote-address");
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY:
+ yang_dnode_get_ip(&remote_addr, args->dnode,
+ "./remote-address");
+ local_iface =
+ yang_dnode_get_uint32(args->dnode, "./local-interface");
+ remote_iface = yang_dnode_get_uint32(args->dnode,
+ "./remote-interface");
+ break;
+ default:
+ break;
+ }
+
+ srte_segment_entry_set_nai(segment, type, &local_addr, local_iface,
+ &remote_addr, remote_iface);
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy
+ */
+int pathd_srte_policy_create(struct nb_cb_create_args *args)
+{
+ struct srte_policy *policy;
+ uint32_t color;
+ struct ipaddr endpoint;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ color = yang_dnode_get_uint32(args->dnode, "./color");
+ yang_dnode_get_ip(&endpoint, args->dnode, "./endpoint");
+ policy = srte_policy_add(color, &endpoint);
+
+ nb_running_set_entry(args->dnode, policy);
+ SET_FLAG(policy->flags, F_POLICY_NEW);
+
+ return NB_OK;
+}
+
+int pathd_srte_policy_destroy(struct nb_cb_destroy_args *args)
+{
+ struct srte_policy *policy;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ policy = nb_running_unset_entry(args->dnode);
+ SET_FLAG(policy->flags, F_POLICY_DELETED);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/name
+ */
+int pathd_srte_policy_name_modify(struct nb_cb_modify_args *args)
+{
+ struct srte_policy *policy;
+ const char *name;
+
+ if (args->event != NB_EV_APPLY && args->event != NB_EV_VALIDATE)
+ return NB_OK;
+
+ policy = nb_running_get_entry(args->dnode, NULL, true);
+
+ if (args->event == NB_EV_VALIDATE) {
+ /* the policy name is fixed after setting it once */
+ if (strlen(policy->name) > 0) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "The SR Policy name is fixed!");
+ return NB_ERR_RESOURCE;
+ } else
+ return NB_OK;
+ }
+
+ name = yang_dnode_get_string(args->dnode, NULL);
+ strlcpy(policy->name, name, sizeof(policy->name));
+ SET_FLAG(policy->flags, F_POLICY_MODIFIED);
+
+ return NB_OK;
+}
+
+int pathd_srte_policy_name_destroy(struct nb_cb_destroy_args *args)
+{
+ struct srte_policy *policy;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ policy = nb_running_get_entry(args->dnode, NULL, true);
+ policy->name[0] = '\0';
+ SET_FLAG(policy->flags, F_POLICY_MODIFIED);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/binding-sid
+ */
+int pathd_srte_policy_binding_sid_modify(struct nb_cb_modify_args *args)
+{
+ struct srte_policy *policy;
+ mpls_label_t binding_sid;
+
+ policy = nb_running_get_entry(args->dnode, NULL, true);
+ binding_sid = yang_dnode_get_uint32(args->dnode, NULL);
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ break;
+ case NB_EV_PREPARE:
+ if (path_zebra_request_label(binding_sid) < 0)
+ return NB_ERR_RESOURCE;
+ break;
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ srte_policy_update_binding_sid(policy, binding_sid);
+ SET_FLAG(policy->flags, F_POLICY_MODIFIED);
+ break;
+ }
+
+ return NB_OK;
+}
+
+int pathd_srte_policy_binding_sid_destroy(struct nb_cb_destroy_args *args)
+{
+ struct srte_policy *policy;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ policy = nb_running_get_entry(args->dnode, NULL, true);
+ srte_policy_update_binding_sid(policy, MPLS_LABEL_NONE);
+ SET_FLAG(policy->flags, F_POLICY_MODIFIED);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path
+ */
+int pathd_srte_policy_candidate_path_create(struct nb_cb_create_args *args)
+{
+ struct srte_policy *policy;
+ struct srte_candidate *candidate;
+ uint32_t preference;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ policy = nb_running_get_entry(args->dnode, NULL, true);
+ preference = yang_dnode_get_uint32(args->dnode, "./preference");
+ candidate = srte_candidate_add(policy, preference);
+ nb_running_set_entry(args->dnode, candidate);
+ SET_FLAG(candidate->flags, F_CANDIDATE_NEW);
+
+ return NB_OK;
+}
+
+int pathd_srte_policy_candidate_path_destroy(struct nb_cb_destroy_args *args)
+{
+ struct srte_candidate *candidate;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ candidate = nb_running_unset_entry(args->dnode);
+ SET_FLAG(candidate->flags, F_CANDIDATE_DELETED);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path/name
+ */
+int pathd_srte_policy_candidate_path_name_modify(struct nb_cb_modify_args *args)
+{
+ struct srte_candidate *candidate;
+ const char *name;
+ char xpath[XPATH_MAXLEN];
+ char xpath_buf[XPATH_MAXLEN - 3];
+
+ if (args->event != NB_EV_APPLY && args->event != NB_EV_VALIDATE)
+ return NB_OK;
+
+ /* the candidate name is fixed after setting it once, this is checked
+ * here */
+ if (args->event == NB_EV_VALIDATE) {
+ /* first get the precise path to the candidate path */
+ yang_dnode_get_path(args->dnode, xpath_buf, sizeof(xpath_buf));
+ snprintf(xpath, sizeof(xpath), "%s%s", xpath_buf, "/..");
+
+ candidate = nb_running_get_entry_non_rec(NULL, xpath, false);
+
+ /* then check if it exists and if the name was provided */
+ if (candidate && strlen(candidate->name) > 0) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "The candidate name is fixed!");
+ return NB_ERR_RESOURCE;
+ } else
+ return NB_OK;
+ }
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+
+ name = yang_dnode_get_string(args->dnode, NULL);
+ strlcpy(candidate->name, name, sizeof(candidate->name));
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+
+ return NB_OK;
+}
+
+
+static int affinity_filter_modify(struct nb_cb_modify_args *args,
+ enum affinity_filter_type type)
+{
+ uint32_t filter;
+ struct srte_candidate *candidate;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ assert(args->context != NULL);
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ filter = yang_dnode_get_uint32(args->dnode, NULL);
+ srte_candidate_set_affinity_filter(candidate, type, filter);
+
+ return NB_OK;
+}
+
+static int affinity_filter_destroy(struct nb_cb_destroy_args *args,
+ enum affinity_filter_type type)
+{
+ struct srte_candidate *candidate;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ assert(args->context != NULL);
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ srte_candidate_unset_affinity_filter(candidate, type);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/exclude-any
+ */
+
+int pathd_srte_policy_candidate_path_exclude_any_modify(
+ struct nb_cb_modify_args *args)
+{
+ return affinity_filter_modify(args, AFFINITY_FILTER_EXCLUDE_ANY);
+}
+
+int pathd_srte_policy_candidate_path_exclude_any_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return affinity_filter_destroy(args, AFFINITY_FILTER_EXCLUDE_ANY);
+}
+
+
+/*
+ * XPath:
+ * /frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-any
+ */
+int pathd_srte_policy_candidate_path_include_any_modify(
+ struct nb_cb_modify_args *args)
+{
+ return affinity_filter_modify(args, AFFINITY_FILTER_INCLUDE_ANY);
+}
+
+int pathd_srte_policy_candidate_path_include_any_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return affinity_filter_destroy(args, AFFINITY_FILTER_INCLUDE_ANY);
+}
+
+
+/*
+ * XPath:
+ * /frr-pathd:pathd/srte/policy/candidate-path/constraints/affinity/include-all
+ */
+int pathd_srte_policy_candidate_path_include_all_modify(
+ struct nb_cb_modify_args *args)
+{
+ return affinity_filter_modify(args, AFFINITY_FILTER_INCLUDE_ALL);
+}
+
+int pathd_srte_policy_candidate_path_include_all_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return affinity_filter_destroy(args, AFFINITY_FILTER_INCLUDE_ALL);
+}
+
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path/constraints/metrics
+ */
+int pathd_srte_policy_candidate_path_metrics_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct srte_candidate *candidate;
+ enum srte_candidate_metric_type type;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ assert(args->context != NULL);
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+
+ type = yang_dnode_get_enum(args->dnode, "./type");
+ srte_candidate_unset_metric(candidate, type);
+
+ return NB_OK;
+}
+
+void pathd_srte_policy_candidate_path_metrics_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct srte_candidate *candidate;
+ enum srte_candidate_metric_type type;
+ float value;
+ bool required, is_bound = false, is_computed = false;
+
+ assert(args->context != NULL);
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+
+ type = yang_dnode_get_enum(args->dnode, "./type");
+ value = (float)yang_dnode_get_dec64(args->dnode, "./value");
+ required = yang_dnode_get_bool(args->dnode, "./required");
+ if (yang_dnode_exists(args->dnode, "./is-bound"))
+ is_bound = yang_dnode_get_bool(args->dnode, "./is-bound");
+ if (yang_dnode_exists(args->dnode, "./is-computed"))
+ is_computed = yang_dnode_get_bool(args->dnode, "./is-computed");
+
+ srte_candidate_set_metric(candidate, type, value, required, is_bound,
+ is_computed);
+}
+
+/*
+ * XPath:
+ * /frr-pathd:pathd/srte/policy/candidate-path/constraints/objective-function
+ */
+int pathd_srte_policy_candidate_path_objfun_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct srte_candidate *candidate;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ assert(args->context != NULL);
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ srte_candidate_unset_objfun(candidate);
+
+ return NB_OK;
+}
+
+void pathd_srte_policy_candidate_path_objfun_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct srte_candidate *candidate;
+ enum objfun_type type;
+ bool required;
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ required = yang_dnode_get_bool(args->dnode, "./required");
+ type = yang_dnode_get_enum(args->dnode, "./type");
+ srte_candidate_set_objfun(candidate, required, type);
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path/protocol-origin
+ */
+int pathd_srte_policy_candidate_path_protocol_origin_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct srte_candidate *candidate;
+ enum srte_protocol_origin protocol_origin;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ protocol_origin = yang_dnode_get_enum(args->dnode, NULL);
+ candidate->protocol_origin = protocol_origin;
+ candidate->lsp->protocol_origin = protocol_origin;
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path/originator
+ */
+int pathd_srte_policy_candidate_path_originator_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct srte_candidate *candidate;
+ const char *originator;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ originator = yang_dnode_get_string(args->dnode, NULL);
+ strlcpy(candidate->originator, originator,
+ sizeof(candidate->originator));
+ strlcpy(candidate->lsp->originator, originator,
+ sizeof(candidate->lsp->originator));
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path/type
+ */
+int pathd_srte_policy_candidate_path_type_modify(struct nb_cb_modify_args *args)
+{
+ struct srte_candidate *candidate;
+ enum srte_candidate_type type;
+ char xpath[XPATH_MAXLEN];
+ char xpath_buf[XPATH_MAXLEN - 3];
+
+ if (args->event != NB_EV_APPLY && args->event != NB_EV_VALIDATE)
+ return NB_OK;
+
+ /* the candidate type is fixed after setting it once, this is checked
+ * here */
+ if (args->event == NB_EV_VALIDATE) {
+ /* first get the precise path to the candidate path */
+ yang_dnode_get_path(args->dnode, xpath_buf, sizeof(xpath_buf));
+ snprintf(xpath, sizeof(xpath), "%s%s", xpath_buf, "/..");
+
+ candidate = nb_running_get_entry_non_rec(NULL, xpath, false);
+
+ /* then check if it exists and if the type was provided */
+ if (candidate
+ && candidate->type != SRTE_CANDIDATE_TYPE_UNDEFINED) {
+ flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
+ "The candidate type is fixed!");
+ return NB_ERR_RESOURCE;
+ } else
+ return NB_OK;
+ }
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+
+ type = yang_dnode_get_enum(args->dnode, NULL);
+ candidate->type = type;
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path/segment-list-name
+ */
+int pathd_srte_policy_candidate_path_segment_list_name_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct srte_candidate *candidate;
+ const char *segment_list_name;
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ segment_list_name = yang_dnode_get_string(args->dnode, NULL);
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ candidate->segment_list = srte_segment_list_find(segment_list_name);
+ candidate->lsp->segment_list = candidate->segment_list;
+ assert(candidate->segment_list);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+
+ return NB_OK;
+}
+
+int pathd_srte_policy_candidate_path_segment_list_name_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct srte_candidate *candidate;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ candidate->segment_list = NULL;
+ candidate->lsp->segment_list = NULL;
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path/constraints/bandwidth
+ */
+void pathd_srte_policy_candidate_path_bandwidth_apply_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct srte_candidate *candidate;
+ float value;
+ bool required;
+
+ assert(args->context != NULL);
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ value = (float)yang_dnode_get_dec64(args->dnode, "./value");
+ required = yang_dnode_get_bool(args->dnode, "./required");
+ srte_candidate_set_bandwidth(candidate, value, required);
+}
+
+int pathd_srte_policy_candidate_path_bandwidth_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct srte_candidate *candidate;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ assert(args->context != NULL);
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ srte_candidate_unset_bandwidth(candidate);
+ return NB_OK;
+}
diff --git a/pathd/path_nb_state.c b/pathd/path_nb_state.c
new file mode 100644
index 0000000000..60f04f45c1
--- /dev/null
+++ b/pathd/path_nb_state.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+#include "table.h"
+#include "command.h"
+#include "northbound.h"
+#include "libfrr.h"
+
+#include "pathd/pathd.h"
+#include "pathd/path_nb.h"
+
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list
+ */
+const void *pathd_srte_segment_list_get_next(struct nb_cb_get_next_args *args)
+{
+ struct srte_segment_list *segment_list =
+ (struct srte_segment_list *)args->list_entry;
+
+ if (args->list_entry == NULL)
+ segment_list =
+ RB_MIN(srte_segment_list_head, &srte_segment_lists);
+ else
+ segment_list = RB_NEXT(srte_segment_list_head, segment_list);
+
+ return segment_list;
+}
+
+int pathd_srte_segment_list_get_keys(struct nb_cb_get_keys_args *args)
+{
+ const struct srte_segment_list *segment_list =
+ (struct srte_segment_list *)args->list_entry;
+
+ args->keys->num = 1;
+ snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%s",
+ segment_list->name);
+
+ return NB_OK;
+}
+
+const void *
+pathd_srte_segment_list_lookup_entry(struct nb_cb_lookup_entry_args *args)
+{
+ return srte_segment_list_find(args->keys->key[0]);
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy
+ */
+const void *pathd_srte_policy_get_next(struct nb_cb_get_next_args *args)
+{
+ struct srte_policy *policy = (struct srte_policy *)args->list_entry;
+
+ if (args->list_entry == NULL)
+ policy = RB_MIN(srte_policy_head, &srte_policies);
+ else
+ policy = RB_NEXT(srte_policy_head, policy);
+
+ return policy;
+}
+
+int pathd_srte_policy_get_keys(struct nb_cb_get_keys_args *args)
+{
+ const struct srte_policy *policy =
+ (struct srte_policy *)args->list_entry;
+
+ args->keys->num = 2;
+ snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%u",
+ policy->color);
+ ipaddr2str(&policy->endpoint, args->keys->key[1],
+ sizeof(args->keys->key[1]));
+
+ return NB_OK;
+}
+
+const void *pathd_srte_policy_lookup_entry(struct nb_cb_lookup_entry_args *args)
+{
+ uint32_t color;
+ struct ipaddr endpoint;
+
+ color = yang_str2uint32(args->keys->key[0]);
+ yang_str2ip(args->keys->key[1], &endpoint);
+
+ return srte_policy_find(color, &endpoint);
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/is-operational
+ */
+struct yang_data *
+pathd_srte_policy_is_operational_get_elem(struct nb_cb_get_elem_args *args)
+{
+ struct srte_policy *policy = (struct srte_policy *)args->list_entry;
+ bool is_operational = false;
+
+ if (policy->status == SRTE_POLICY_STATUS_UP)
+ is_operational = true;
+
+ return yang_data_new_bool(args->xpath, is_operational);
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path
+ */
+const void *
+pathd_srte_policy_candidate_path_get_next(struct nb_cb_get_next_args *args)
+{
+ struct srte_policy *policy =
+ (struct srte_policy *)args->parent_list_entry;
+ struct srte_candidate *candidate =
+ (struct srte_candidate *)args->list_entry;
+
+ if (args->list_entry == NULL)
+ candidate =
+ RB_MIN(srte_candidate_head, &policy->candidate_paths);
+ else
+ candidate = RB_NEXT(srte_candidate_head, candidate);
+
+ return candidate;
+}
+
+int pathd_srte_policy_candidate_path_get_keys(struct nb_cb_get_keys_args *args)
+{
+ const struct srte_candidate *candidate =
+ (struct srte_candidate *)args->list_entry;
+
+ args->keys->num = 1;
+ snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%u",
+ candidate->preference);
+
+ return NB_OK;
+}
+
+const void *pathd_srte_policy_candidate_path_lookup_entry(
+ struct nb_cb_lookup_entry_args *args)
+{
+ struct srte_policy *policy =
+ (struct srte_policy *)args->parent_list_entry;
+ uint32_t preference;
+
+ preference = yang_str2uint32(args->keys->key[0]);
+
+ return srte_candidate_find(policy, preference);
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate_path/is-best-candidate-path
+ */
+struct yang_data *
+pathd_srte_policy_candidate_path_is_best_candidate_path_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ struct srte_candidate *candidate =
+ (struct srte_candidate *)args->list_entry;
+
+ return yang_data_new_bool(
+ args->xpath, CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST));
+}
+
+/*
+ * XPath: /frr-pathd:pathd/srte/policy/candidate-path/discriminator
+ */
+struct yang_data *pathd_srte_policy_candidate_path_discriminator_get_elem(
+ struct nb_cb_get_elem_args *args)
+{
+ struct srte_candidate *candidate =
+ (struct srte_candidate *)args->list_entry;
+
+ return yang_data_new_uint32(args->xpath, candidate->discriminator);
+}
diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c
new file mode 100644
index 0000000000..2f9ff4f0f0
--- /dev/null
+++ b/pathd/path_pcep.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include <pcep_utils_counters.h>
+
+#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_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_config.h"
+
+
+/*
+ * Globals.
+ */
+static struct pcep_glob pcep_glob_space = {.dbg = {0, "pathd module: pcep"}};
+struct pcep_glob *pcep_g = &pcep_glob_space;
+
+/* Main Thread Even Handler */
+static int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id,
+ void *payload);
+static int pcep_main_event_start_sync(int pcc_id);
+static int pcep_main_event_start_sync_cb(struct path *path, void *arg);
+static int pcep_main_event_update_candidate(struct path *path);
+static int pcep_main_event_remove_candidate_segments(const char *originator,
+ bool force);
+
+/* Hook Handlers called from the Main Thread */
+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);
+
+/* Path manipulation functions */
+static struct path_metric *pcep_copy_metrics(struct path_metric *metric);
+static struct path_hop *pcep_copy_hops(struct path_hop *hop);
+
+/* Module Functions */
+static int pcep_module_finish(void);
+static int pcep_module_late_init(struct thread_master *tm);
+static int pcep_module_init(void);
+
+/* ------------ Path Helper Functions ------------ */
+
+struct path *pcep_new_path(void)
+{
+ struct path *path;
+ path = XCALLOC(MTYPE_PCEP, sizeof(*path));
+ path->binding_sid = MPLS_LABEL_NONE;
+ path->enforce_bandwidth = true;
+ return path;
+}
+
+struct path_hop *pcep_new_hop(void)
+{
+ struct path_hop *hop;
+ hop = XCALLOC(MTYPE_PCEP, sizeof(*hop));
+ return hop;
+}
+
+struct path_metric *pcep_new_metric(void)
+{
+ struct path_metric *metric;
+ metric = XCALLOC(MTYPE_PCEP, sizeof(*metric));
+ return metric;
+}
+
+struct path_metric *pcep_copy_metrics(struct path_metric *metric)
+{
+ if (metric == NULL)
+ return NULL;
+ struct path_metric *new_metric = pcep_new_metric();
+ *new_metric = *metric;
+ new_metric->next = pcep_copy_metrics(metric->next);
+ return new_metric;
+}
+
+struct path_hop *pcep_copy_hops(struct path_hop *hop)
+{
+ if (hop == NULL)
+ return NULL;
+ struct path_hop *new_hop = pcep_new_hop();
+ *new_hop = *hop;
+ new_hop->next = pcep_copy_hops(hop->next);
+ return new_hop;
+}
+
+struct path *pcep_copy_path(struct path *path)
+{
+ struct path *new_path = pcep_new_path();
+
+ *new_path = *path;
+ new_path->first_metric = pcep_copy_metrics(path->first_metric);
+ new_path->first_hop = pcep_copy_hops(path->first_hop);
+ if (path->name != NULL)
+ new_path->name = XSTRDUP(MTYPE_PCEP, path->name);
+ if (path->originator != NULL)
+ new_path->originator = XSTRDUP(MTYPE_PCEP, path->originator);
+ return new_path;
+}
+
+void pcep_free_path(struct path *path)
+{
+ struct path_hop *hop;
+ struct path_metric *metric;
+ char *tmp;
+
+ metric = path->first_metric;
+ while (metric != NULL) {
+ struct path_metric *next = metric->next;
+ XFREE(MTYPE_PCEP, metric);
+ metric = next;
+ }
+ hop = path->first_hop;
+ while (hop != NULL) {
+ struct path_hop *next = hop->next;
+ XFREE(MTYPE_PCEP, hop);
+ hop = next;
+ }
+ if (path->originator != NULL) {
+ /* The path own the memory, it is const so it is clear it
+ shouldn't be modified. XFREE macro do not support type casting
+ so we need a temporary variable */
+ tmp = (char *)path->originator;
+ XFREE(MTYPE_PCEP, tmp);
+ path->originator = NULL;
+ }
+ if (path->name != NULL) {
+ /* The path own the memory, it is const so it is clear it
+ shouldn't be modified. XFREE macro do not support type casting
+ so we need a temporary variable */
+ tmp = (char *)path->name;
+ XFREE(MTYPE_PCEP, tmp);
+ path->name = NULL;
+ }
+ XFREE(MTYPE_PCEP, path);
+}
+
+
+/* ------------ Main Thread Even Handler ------------ */
+
+int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id,
+ void *payload)
+{
+ int ret = 0;
+
+ switch (type) {
+ case PCEP_MAIN_EVENT_START_SYNC:
+ ret = pcep_main_event_start_sync(pcc_id);
+ break;
+ case PCEP_MAIN_EVENT_UPDATE_CANDIDATE:
+ assert(payload != NULL);
+ ret = pcep_main_event_update_candidate((struct path *)payload);
+ break;
+ case PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP:
+ ret = pcep_main_event_remove_candidate_segments(
+ (const char *)payload, true);
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ "Unexpected event received in the main thread: %u",
+ type);
+ break;
+ }
+
+ return ret;
+}
+
+int pcep_main_event_start_sync(int pcc_id)
+{
+ path_pcep_config_list_path(pcep_main_event_start_sync_cb, &pcc_id);
+ pcep_ctrl_sync_done(pcep_g->fpt, pcc_id);
+ return 0;
+}
+
+int pcep_main_event_start_sync_cb(struct path *path, void *arg)
+{
+ int *pcc_id = (int *)arg;
+ pcep_ctrl_sync_path(pcep_g->fpt, *pcc_id, path);
+ return 1;
+}
+
+int pcep_main_event_update_candidate(struct path *path)
+{
+ struct path *resp = NULL;
+ int ret = 0;
+
+ ret = path_pcep_config_update_path(path);
+ if (ret != PATH_NB_ERR && path->srp_id != 0) {
+ /* ODL and Cisco requires the first reported
+ * LSP to have a DOWN status, the later status changes
+ * will be comunicated through hook calls.
+ */
+ enum pcep_lsp_operational_status real_status;
+ if ((resp = path_pcep_config_get_path(&path->nbkey))) {
+ resp->srp_id = path->srp_id;
+ real_status = resp->status;
+ resp->status = PCEP_LSP_OPERATIONAL_DOWN;
+ pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp);
+ /* If the update did not have any effect and the real
+ * status is not DOWN, we need to send a second report
+ * so the PCE is aware of the real status. This is due
+ * to the fact that NO notification will be received
+ * if the update did not apply any changes */
+ if ((ret == PATH_NB_NO_CHANGE)
+ && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) {
+ resp->status = real_status;
+ resp->srp_id = 0;
+ pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id,
+ resp);
+ }
+ pcep_free_path(resp);
+ }
+ }
+ return ret;
+}
+
+int pcep_main_event_remove_candidate_segments(const char *originator,
+ bool force)
+{
+ srte_candidate_unset_segment_list(originator, force);
+ /* Avoid compiler warnings about const char* */
+ void *free_ptr = (void *)originator;
+ XFREE(MTYPE_PCEP, free_ptr);
+
+ srte_apply_changes();
+
+ return 0;
+}
+
+/* ------------ Hook Handlers Functions Called From Main Thread ------------ */
+
+int pathd_candidate_created_handler(struct srte_candidate *candidate)
+{
+ struct path *path = candidate_to_path(candidate);
+ int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_CREATED, path);
+ return ret;
+}
+
+int pathd_candidate_updated_handler(struct srte_candidate *candidate)
+{
+ struct path *path = candidate_to_path(candidate);
+ int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_UPDATED, path);
+ return ret;
+}
+
+int pathd_candidate_removed_handler(struct srte_candidate *candidate)
+{
+ struct path *path = candidate_to_path(candidate);
+ int ret = pcep_ctrl_pathd_event(pcep_g->fpt, PCEP_PATH_REMOVED, path);
+ return ret;
+}
+
+
+/* ------------ Module Functions ------------ */
+
+int pcep_module_late_init(struct thread_master *tm)
+{
+ assert(pcep_g->fpt == NULL);
+ assert(pcep_g->master == NULL);
+
+ struct frr_pthread *fpt;
+
+ if (pcep_ctrl_initialize(tm, &fpt, pcep_main_event_handler))
+ return 1;
+
+ if (pcep_lib_initialize(fpt))
+ return 1;
+
+ pcep_g->master = tm;
+ pcep_g->fpt = fpt;
+
+ hook_register(pathd_candidate_created, pathd_candidate_created_handler);
+ hook_register(pathd_candidate_updated, pathd_candidate_updated_handler);
+ hook_register(pathd_candidate_removed, pathd_candidate_removed_handler);
+
+ hook_register(frr_fini, pcep_module_finish);
+
+ pcep_cli_init();
+
+ return 0;
+}
+
+int pcep_module_finish(void)
+{
+ pcep_ctrl_finalize(&pcep_g->fpt);
+ pcep_lib_finalize();
+
+ for (int i = 0; i < MAX_PCC; i++)
+ if (pcep_g->pce_opts_cli[i] != NULL)
+ XFREE(MTYPE_PCEP, pcep_g->pce_opts_cli[i]);
+
+ return 0;
+}
+
+int pcep_module_init(void)
+{
+ 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;
+}
+
+FRR_MODULE_SETUP(.name = "frr_pathd_pcep", .version = FRR_VERSION,
+ .description = "FRR pathd PCEP module",
+ .init = pcep_module_init)
diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h
new file mode 100644
index 0000000000..1896c265c1
--- /dev/null
+++ b/pathd/path_pcep.h
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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_H_
+#define _PATH_PCEP_H_
+
+#include <stdbool.h>
+#include <debug.h>
+#include <netinet/tcp.h>
+#include <pcep_utils_logging.h>
+#include <pcep_pcc_api.h>
+#include "mpls.h"
+#include "pathd/pathd.h"
+#include "pathd/path_pcep_memory.h"
+
+#define PCEP_DEFAULT_PORT 4189
+#define MAX_PCC 32
+#define MAX_PCE 32
+#define MAX_TAG_SIZE 50
+#define PCEP_DEBUG_MODE_BASIC 0x01
+#define PCEP_DEBUG_MODE_PATH 0x02
+#define PCEP_DEBUG_MODE_PCEP 0x04
+#define PCEP_DEBUG_MODE_PCEPLIB 0x08
+#define PCEP_DEBUG(fmt, ...) \
+ do { \
+ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC)) \
+ DEBUGD(&pcep_g->dbg, "pcep: " fmt, ##__VA_ARGS__); \
+ } while (0)
+#define PCEP_DEBUG_PATH(fmt, ...) \
+ do { \
+ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH)) \
+ DEBUGD(&pcep_g->dbg, "pcep: " fmt, ##__VA_ARGS__); \
+ } while (0)
+#define PCEP_DEBUG_PCEP(fmt, ...) \
+ do { \
+ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP)) \
+ DEBUGD(&pcep_g->dbg, "pcep: " fmt, ##__VA_ARGS__); \
+ } while (0)
+#define PCEP_DEBUG_PCEPLIB(priority, fmt, ...) \
+ do { \
+ switch (priority) { \
+ case LOG_DEBUG: \
+ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, \
+ PCEP_DEBUG_MODE_PCEPLIB)) \
+ DEBUGD(&pcep_g->dbg, "pcep: " fmt, \
+ ##__VA_ARGS__); \
+ break; \
+ case LOG_INFO: \
+ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, \
+ PCEP_DEBUG_MODE_PCEPLIB)) \
+ DEBUGI(&pcep_g->dbg, "pcep: " fmt, \
+ ##__VA_ARGS__); \
+ break; \
+ case LOG_NOTICE: \
+ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, \
+ PCEP_DEBUG_MODE_PCEPLIB)) \
+ DEBUGN(&pcep_g->dbg, "pcep: " fmt, \
+ ##__VA_ARGS__); \
+ break; \
+ case LOG_WARNING: \
+ case LOG_ERR: \
+ default: \
+ zlog(priority, "pcep: " fmt, ##__VA_ARGS__); \
+ break; \
+ } \
+ } while (0)
+
+struct pcep_config_group_opts {
+ char name[64];
+ char tcp_md5_auth[TCP_MD5SIG_MAXKEYLEN];
+ struct ipaddr source_ip;
+ short source_port;
+ 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 session_timeout_inteval_seconds;
+ int delegation_timeout_seconds;
+};
+
+struct pce_opts {
+ struct ipaddr addr;
+ short port;
+ char pce_name[64];
+ struct pcep_config_group_opts config_opts;
+ uint8_t precedence; /* Multi-PCE precedence */
+};
+
+struct pcc_opts {
+ struct ipaddr addr;
+ short port;
+ short msd;
+};
+
+/* 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 lsp_nb_key {
+ uint32_t color;
+ struct ipaddr endpoint;
+ uint32_t preference;
+};
+
+struct sid_mpls {
+ mpls_label_t label;
+ uint8_t traffic_class;
+ bool is_bottom;
+ uint8_t ttl;
+};
+
+struct pcep_caps {
+ bool is_stateful;
+ /* If we know the objective functions supported by the PCE.
+ * If we don't know, it doesn't mean the PCE doesn't support any */
+ bool supported_ofs_are_known;
+ /* Defined if we know which objective funtions are supported by the PCE.
+ * One bit per objective function, the bit index being equal to
+ * enum pcep_objfun_type values: bit 0 is not used, bit 1 is
+ * PCEP_OBJFUN_MCP, up to bit 17 that is PCEP_OBJFUN_MSN */
+ uint32_t supported_ofs;
+};
+
+union sid {
+ uint32_t value;
+ struct sid_mpls mpls;
+};
+
+struct nai {
+ /* NAI type */
+ enum pcep_sr_subobj_nai type;
+ /* Local IP address*/
+ struct ipaddr local_addr;
+ /* Local interface identifier if the NAI is an unnumbered adjacency */
+ uint32_t local_iface;
+ /* Remote address if the NAI is an adjacency */
+ struct ipaddr remote_addr;
+ /* Remote interface identifier if the NAI is an unnumbered adjacency */
+ uint32_t remote_iface;
+};
+
+struct path_hop {
+ /* Pointer to the next hop in the path */
+ struct path_hop *next;
+ /* Indicateif this ia a loose or strict hop */
+ bool is_loose;
+ /* Indicate if there is an SID for the hop */
+ bool has_sid;
+ /* Indicate if the hop as a MPLS label */
+ bool is_mpls;
+ /* Indicate if the MPLS label has extra attributes (TTL, class..)*/
+ bool has_attribs;
+ /* Hop's SID if available */
+ union sid sid;
+ /* Indicate if there is a NAI for this hop */
+ bool has_nai;
+ /* NAI if available */
+ struct nai nai;
+};
+
+struct path_metric {
+ /* Pointer to the next metric */
+ struct path_metric *next;
+ /* The metric type */
+ enum pcep_metric_types type;
+ /* If the metric should be enforced */
+ bool enforce;
+ /* If the metric value is bound (a maximum) */
+ bool is_bound;
+ /* If the metric value is computed */
+ bool is_computed;
+ /* The metric value */
+ float value;
+};
+
+struct path {
+ /* Both the nbkey and the plspid are keys comming from the PCC,
+ but the PCE is only using the plspid. The missing key is looked up by
+ the PCC so we always have both */
+
+ /* The northbound key identifying this path */
+ struct lsp_nb_key nbkey;
+ /* The generated unique PLSP identifier for this path.
+ See draft-ietf-pce-stateful-pce */
+ uint32_t plsp_id;
+
+ /* The transport address the path is comming from, PCE or PCC*/
+ struct ipaddr sender;
+ /* The pcc protocol address, must be the same family as the endpoint */
+ struct ipaddr pcc_addr;
+
+ /* The identifier of the PCC the path is for/from. If 0 it is undefined,
+ meaning it hasn't be set yet or is for all the PCC */
+ int pcc_id;
+
+ /* The origin of the path creation */
+ enum srte_protocol_origin create_origin;
+ /* The origin of the path modification */
+ enum srte_protocol_origin update_origin;
+ /* The identifier of the entity that originated the path */
+ const char *originator;
+ /* The type of the path, for PCE initiated or updated path it is always
+ SRTE_CANDIDATE_TYPE_DYNAMIC */
+ enum srte_candidate_type type;
+
+ /* The following data comes from either the PCC or the PCE if available
+ */
+
+ /* Path's binding SID */
+ mpls_label_t binding_sid;
+ /* The name of the path */
+ const char *name;
+ /* The request identifier from the PCE, when getting a path from the
+ PCE. See draft-ietf-pce-stateful-pce */
+ uint32_t srp_id;
+ /* The request identifier from the PCC , when getting a path from the
+ PCE after a computation request. See rfc5440, section-7.4 */
+ uint32_t req_id;
+ /* The operational status of the path */
+ enum pcep_lsp_operational_status status;
+ /* If true, the receiver (PCC) must remove the path.
+ See draft-ietf-pce-pce-initiated-lsp */
+ bool do_remove;
+ /* Indicate the given path was removed by the PCC.
+ See draft-ietf-pce-stateful-pce, section-7.3, flag R */
+ bool was_removed;
+ /* Indicate the path is part of the synchronization process.
+ See draft-ietf-pce-stateful-pce, section-7.3, flag S */
+ bool is_synching;
+ /* Indicate if the path bandwidth requirment is defined */
+ bool has_bandwidth;
+ /* Indicate if the bandwidth requirment should be enforced */
+ bool enforce_bandwidth;
+ /* Path required bandwidth if defined */
+ float bandwidth;
+ /* Specify the list of hop defining the path */
+ struct path_hop *first_hop;
+ /* Specify the list of metrics */
+ struct path_metric *first_metric;
+ /* Indicate if the path has a PCC-defined objective function */
+ bool has_pcc_objfun;
+ /* Indicate the PCC-defined objective function is required */
+ bool enforce_pcc_objfun;
+ /* PCC-defined Objective Function */
+ enum objfun_type pcc_objfun;
+ /* Indicate if the path has a PCE-defined objective function */
+ bool has_pce_objfun;
+ /* PCE-defined Objective Function */
+ enum objfun_type pce_objfun;
+ /* Indicate if some affinity filters are defined */
+ bool has_affinity_filters;
+ /* Affinity attribute filters indexed by enum affinity_filter_type - 1
+ */
+ uint32_t affinity_filters[MAX_AFFINITY_FILTER_TYPE];
+
+ /* The following data need to be specialized for a given PCE */
+
+ /* Indicate the path is delegated to the PCE.
+ See draft-ietf-pce-stateful-pce, section-7.3, flag D */
+ bool is_delegated;
+ /* Indicate if the PCE wants the path to get active.
+ See draft-ietf-pce-stateful-pce, section-7.3, flag A */
+ bool go_active;
+ /* Indicate the given path was created by the PCE,
+ See draft-ietf-pce-pce-initiated-lsp, section-5.3.1, flag C */
+ bool was_created;
+
+ /* The following data is defined for comnputation replies */
+
+ /* Indicate that no path could be computed */
+ bool no_path;
+};
+
+struct pcep_glob {
+ struct debug dbg;
+ struct thread_master *master;
+ struct frr_pthread *fpt;
+ 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;
+
+/* Path Helper Functions */
+struct path *pcep_new_path(void);
+struct path_hop *pcep_new_hop(void);
+struct path_metric *pcep_new_metric(void);
+struct path *pcep_copy_path(struct path *path);
+void pcep_free_path(struct path *path);
+
+
+#endif // _PATH_PCEP_H_
diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c
new file mode 100644
index 0000000000..add3391f22
--- /dev/null
+++ b/pathd/path_pcep_cli.c
@@ -0,0 +1,2033 @@
+/*
+ * 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 <zebra.h>
+#include <pcep_utils_counters.h>
+#include <pcep_session_logic.h>
+
+#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_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_debug.h"
+#include "pathd/path_pcep_lib.h"
+#include "pathd/path_pcep_pcc.h"
+
+#ifndef VTYSH_EXTRACT_PL
+#include "pathd/path_pcep_cli_clippy.c"
+#endif
+
+#define DEFAULT_PCE_PRECEDENCE 255
+#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 255
+#define DEFAULT_TIMER_DEADTIMER 120
+#define DEFAULT_TIMER_DEADTIMER_MIN 4
+#define DEFAULT_TIMER_DEADTIMER_MAX 255
+#define DEFAULT_TIMER_PCEP_REQUEST 30
+#define DEFAULT_TIMER_SESSION_TIMEOUT_INTERVAL 30
+#define DEFAULT_DELEGATION_TIMEOUT_INTERVAL 10
+
+/* 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_pcep_config_write(struct vty *vty);
+static int pcep_cli_pcc_config_write(struct vty *vty);
+static int pcep_cli_pce_config_write(struct vty *vty);
+static int pcep_cli_pcep_pce_config_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_pce_config_options(struct pce_opts_cli *pce_opts_cli);
+static struct pcep_config_group_opts *
+pcep_cli_find_pcep_pce_config(const char *group_name);
+static bool
+pcep_cli_add_pcep_pce_config(struct pcep_config_group_opts *config_group_opts);
+static struct pcep_config_group_opts *
+pcep_cli_create_pcep_pce_config(const char *group_name);
+static bool pcep_cli_is_pcep_pce_config_used(const char *group_name);
+static void pcep_cli_delete_pcep_pce_config(const char *group_name);
+static int pcep_cli_print_pce_config(struct pcep_config_group_opts *group_opts,
+ char *buf, size_t buf_len);
+static void print_pcep_capabilities(char *buf, size_t buf_len,
+ pcep_configuration *config);
+static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts,
+ struct pcep_pcc_info *pcc_info);
+static bool pcep_cli_pcc_has_pce(const char *pce_name);
+static void pcep_cli_add_pce_connection(struct pce_opts *pce_opts);
+static void pcep_cli_remove_pce_connection(struct pce_opts *pce_opts);
+static int path_pcep_cli_pcc_pcc_peer_delete(struct vty *vty,
+ const char *peer_name,
+ const char *precedence_str,
+ long precedence);
+
+/*
+ * Globals.
+ */
+
+static const char PCEP_VTYSH_ARG_ADDRESS[] = "address";
+static const char PCEP_VTYSH_ARG_SOURCE_ADDRESS[] = "source-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_TIMER[] = "timer";
+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_SESSION_TIMEOUT[] = "session-timeout-interval";
+static const char PCEP_VTYSH_ARG_DELEGATION_TIMEOUT[] = "delegation-timeout";
+static const char PCEP_VTYSH_ARG_SR_DRAFT07[] = "sr-draft07";
+static const char PCEP_VTYSH_ARG_PCE_INIT[] = "pce-initiated";
+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";
+static const char PCEP_CLI_CAP_STATEFUL[] = " [Stateful PCE]";
+static const char PCEP_CLI_CAP_INCL_DB_VER[] = " [Include DB version]";
+static const char PCEP_CLI_CAP_LSP_TRIGGERED[] = " [LSP Triggered Resync]";
+static const char PCEP_CLI_CAP_LSP_DELTA[] = " [LSP Delta Sync]";
+static const char PCEP_CLI_CAP_PCE_TRIGGERED[] =
+ " [PCE triggered Initial Sync]";
+static const char PCEP_CLI_CAP_SR_TE_PST[] = " [SR TE PST]";
+static const char PCEP_CLI_CAP_PCC_RESOLVE_NAI[] =
+ " [PCC can resolve NAI to SID]";
+static const char PCEP_CLI_CAP_PCC_INITIATED[] = " [PCC Initiated LSPs]";
+static const char PCEP_CLI_CAP_PCC_PCE_INITIATED[] =
+ " [PCC and PCE Initiated LSPs]";
+
+struct pce_connections {
+ int num_connections;
+ struct pce_opts *connections[MAX_PCC];
+};
+
+struct pce_connections pce_connections_g = {.num_connections = 0};
+
+/* Default PCE group that all PCE-Groups and PCEs will inherit from */
+struct pcep_config_group_opts default_pcep_config_group_opts_g = {
+ .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,
+ .session_timeout_inteval_seconds =
+ DEFAULT_TIMER_SESSION_TIMEOUT_INTERVAL,
+ .delegation_timeout_seconds = DEFAULT_DELEGATION_TIMEOUT_INTERVAL,
+ .source_port = DEFAULT_PCEP_TCP_PORT,
+ .source_ip.ipa_type = IPADDR_NONE,
+};
+
+/* Used by PCEP_PCE_CONFIG_NODE sub-commands to operate on the current pce group
+ */
+struct pcep_config_group_opts *current_pcep_config_group_opts_g = NULL;
+/* Used by PCEP_PCE_NODE sub-commands to operate on the current pce opts */
+struct pce_opts_cli *current_pce_opts_g = NULL;
+short pcc_msd_g = DEFAULT_PCC_MSD;
+bool pcc_msd_configured_g = false;
+
+static struct cmd_node pcep_node = {
+ .name = "srte pcep",
+ .node = PCEP_NODE,
+ .parent_node = SR_TRAFFIC_ENG_NODE,
+ .config_write = pcep_cli_pcep_config_write,
+ .prompt = "%s(config-sr-te-pcep)# "
+};
+
+static struct cmd_node pcep_pcc_node = {
+ .name = "srte pcep pcc",
+ .node = PCEP_PCC_NODE,
+ .parent_node = PCEP_NODE,
+ .config_write = pcep_cli_pcc_config_write,
+ .prompt = "%s(config-sr-te-pcep-pcc)# "
+};
+
+static struct cmd_node pcep_pce_node = {
+ .name = "srte pcep pce",
+ .node = PCEP_PCE_NODE,
+ .parent_node = PCEP_NODE,
+ .config_write = pcep_cli_pce_config_write,
+ .prompt = "%s(config-sr-te-pcep-pce)# "
+};
+
+static struct cmd_node pcep_pce_config_node = {
+ .name = "srte pcep pce-config",
+ .node = PCEP_PCE_CONFIG_NODE,
+ .parent_node = PCEP_NODE,
+ .config_write = pcep_cli_pcep_pce_config_write,
+ .prompt = "%s(pce-sr-te-pcep-pce-config)# "
+};
+
+/* 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 = \
+ ((pce_config != NULL \
+ && pce_config->config_param != not_set_value) \
+ ? pce_config->config_param \
+ : default_pcep_config_group_opts_g \
+ .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));
+ strlcpy(pce_opts_cli->pce_opts.pce_name, name,
+ sizeof(pce_opts_cli->pce_opts.pce_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_pce_config_options(struct pce_opts_cli *pce_opts_cli)
+{
+ if (pce_opts_cli->merged == true) {
+ return;
+ }
+
+ struct pcep_config_group_opts *pce_config =
+ pcep_cli_find_pcep_pce_config(pce_opts_cli->config_group_name);
+
+ /* Configuration priorities:
+ * 1) pce_opts->config_opts, if present, overwrite pce_config
+ * config_opts 2) pce_config config_opts, if present, overwrite
+ * default config_opts 3) If neither pce_opts->config_opts nor
+ * pce_config config_opts are set, then the default config_opts value
+ * will be used.
+ */
+
+ const char *tcp_md5_auth_str =
+ pce_opts_cli->pce_config_group_opts.tcp_md5_auth;
+ if (pce_opts_cli->pce_config_group_opts.tcp_md5_auth[0] == '\0') {
+ if (pce_config != NULL && pce_config->tcp_md5_auth[0] != '\0') {
+ tcp_md5_auth_str = pce_config->tcp_md5_auth;
+ } else {
+ tcp_md5_auth_str =
+ default_pcep_config_group_opts_g.tcp_md5_auth;
+ }
+ }
+ strncpy(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth,
+ tcp_md5_auth_str, TCP_MD5SIG_MAXKEYLEN);
+
+ struct ipaddr *source_ip =
+ &pce_opts_cli->pce_config_group_opts.source_ip;
+ if (pce_opts_cli->pce_config_group_opts.source_ip.ipa_type
+ == IPADDR_NONE) {
+ if (pce_config != NULL
+ && pce_config->source_ip.ipa_type != IPADDR_NONE) {
+ source_ip = &pce_config->source_ip;
+ } else {
+ source_ip = &default_pcep_config_group_opts_g.source_ip;
+ }
+ }
+ memcpy(&pce_opts_cli->pce_opts.config_opts.source_ip, source_ip,
+ sizeof(struct ipaddr));
+
+ 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(session_timeout_inteval_seconds, 0);
+ MERGE_COMPARE_CONFIG_GROUP_VALUE(delegation_timeout_seconds, 0);
+ MERGE_COMPARE_CONFIG_GROUP_VALUE(source_port, 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_pce_config(const char *group_name)
+{
+ for (int i = 0; i < MAX_PCE; i++) {
+ struct pcep_config_group_opts *pcep_pce_config_rhs =
+ pcep_g->config_group_opts[i];
+ if (pcep_pce_config_rhs != NULL) {
+ if (strcmp(group_name, pcep_pce_config_rhs->name)
+ == 0) {
+ return pcep_pce_config_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_pce_config(
+ 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_pce_config(const char *group_name)
+{
+ struct pcep_config_group_opts *pcep_config_group_opts =
+ XCALLOC(MTYPE_PCEP, sizeof(struct pcep_config_group_opts));
+ strlcpy(pcep_config_group_opts->name, group_name,
+ sizeof(pcep_config_group_opts->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_pce_config_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_pce_config(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;
+ }
+ }
+ }
+}
+
+static bool pcep_cli_pcc_has_pce(const char *pce_name)
+{
+ for (int i = 0; i < MAX_PCC; i++) {
+ struct pce_opts *pce_opts = pce_connections_g.connections[i];
+ if (pce_opts == NULL) {
+ continue;
+ }
+
+ if (strcmp(pce_opts->pce_name, pce_name) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void pcep_cli_add_pce_connection(struct pce_opts *pce_opts)
+{
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (pce_connections_g.connections[i] == NULL) {
+ pce_connections_g.num_connections++;
+ pce_connections_g.connections[i] = pce_opts;
+ return;
+ }
+ }
+}
+
+static void pcep_cli_remove_pce_connection(struct pce_opts *pce_opts)
+{
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (pce_connections_g.connections[i] == pce_opts) {
+ pce_connections_g.num_connections--;
+ pce_connections_g.connections[i] = NULL;
+ 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_srte_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_pce_config(struct vty *vty,
+ const char *pcep_pce_config)
+{
+ struct pcep_config_group_opts *pce_config =
+ pcep_cli_find_pcep_pce_config(pcep_pce_config);
+ if (pce_config == NULL) {
+ pce_config = pcep_cli_create_pcep_pce_config(pcep_pce_config);
+ if (pcep_cli_add_pcep_pce_config(pce_config) == false) {
+ vty_out(vty,
+ "%% Cannot create pce-config, as the Maximum limit of %d pce-config has been reached.\n",
+ MAX_PCE);
+ XFREE(MTYPE_PCEP, pce_config);
+ return CMD_WARNING;
+ }
+ } else {
+ vty_out(vty,
+ "Notice: changes to this pce-config will not affect PCEs already configured with this group\n");
+ }
+
+ current_pcep_config_group_opts_g = pce_config;
+ vty->node = PCEP_PCE_CONFIG_NODE;
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_pcep_pce_config_delete(struct vty *vty,
+ const char *pcep_pce_config)
+{
+ struct pcep_config_group_opts *pce_config =
+ pcep_cli_find_pcep_pce_config(pcep_pce_config);
+ if (pce_config == NULL) {
+ vty_out(vty,
+ "%% Cannot delete pce-config, since it does not exist.\n");
+ return CMD_WARNING;
+ }
+
+ if (pcep_cli_is_pcep_pce_config_used(pce_config->name)) {
+ vty_out(vty,
+ "%% Cannot delete pce-config, since it is in use by a peer.\n");
+ return CMD_WARNING;
+ }
+
+ pcep_cli_delete_pcep_pce_config(pce_config->name);
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_show_srte_pcep_pce_config(struct vty *vty,
+ const char *pcep_pce_config)
+{
+ char buf[1024] = "";
+
+ /* Only show 1 Peer config group */
+ struct pcep_config_group_opts *group_opts;
+ if (pcep_pce_config != NULL) {
+ if (strcmp(pcep_pce_config, "default") == 0) {
+ group_opts = &default_pcep_config_group_opts_g;
+ } else {
+ group_opts =
+ pcep_cli_find_pcep_pce_config(pcep_pce_config);
+ }
+ if (group_opts == NULL) {
+ vty_out(vty, "%% pce-config [%s] does not exist.\n",
+ pcep_pce_config);
+ return CMD_WARNING;
+ }
+
+ vty_out(vty, "pce-config: %s\n", group_opts->name);
+ pcep_cli_print_pce_config(group_opts, buf, sizeof(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, "pce-config: %s\n", group_opts->name);
+ pcep_cli_print_pce_config(group_opts, buf, sizeof(buf));
+ vty_out(vty, "%s", buf);
+ buf[0] = 0;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_pce(struct vty *vty, const char *pce_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(pce_peer_name);
+ if (pce_opts_cli == NULL) {
+ pce_opts_cli = pcep_cli_create_pce_opts(pce_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 = PCEP_PCE_NODE;
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_pce_delete(struct vty *vty, const char *pce_peer_name)
+{
+ struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(pce_peer_name);
+ if (pce_opts_cli == NULL) {
+ vty_out(vty, "%% PCC peer does not exist.\n");
+ return CMD_WARNING;
+ }
+
+ /* To better work with frr-reload, go ahead and delete it if its in use
+ */
+ if (pcep_cli_pcc_has_pce(pce_peer_name)) {
+ vty_out(vty,
+ "%% Notice: the pce is in use by a PCC, also disconnecting.\n");
+ path_pcep_cli_pcc_pcc_peer_delete(vty, pce_peer_name, NULL, 0);
+ }
+
+ pcep_cli_delete_pce(pce_peer_name);
+
+ return CMD_SUCCESS;
+}
+
+/* Internal Util func to show an individual PCE,
+ * only used by path_pcep_cli_show_srte_pcep_pce() */
+static void show_pce_peer(struct vty *vty, struct pce_opts_cli *pce_opts_cli)
+{
+ struct pce_opts *pce_opts = &pce_opts_cli->pce_opts;
+ vty_out(vty, "PCE: %s\n", pce_opts->pce_name);
+
+ /* Remote PCE IP address */
+ 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, " pce-config: %s\n",
+ pce_opts_cli->config_group_name);
+ }
+
+ char buf[1024] = "";
+ pcep_cli_print_pce_config(&pce_opts->config_opts, buf, sizeof(buf));
+ vty_out(vty, "%s", buf);
+}
+
+static int path_pcep_cli_show_srte_pcep_pce(struct vty *vty,
+ const char *pce_peer)
+{
+ /* Only show 1 PCE */
+ struct pce_opts_cli *pce_opts_cli;
+ if (pce_peer != NULL) {
+ pce_opts_cli = pcep_cli_find_pce(pce_peer);
+ if (pce_opts_cli == NULL) {
+ vty_out(vty, "%% PCE [%s] does not exist.\n", pce_peer);
+ return CMD_WARNING;
+ }
+
+ pcep_cli_merge_pcep_pce_config_options(pce_opts_cli);
+ show_pce_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_pce_config_options(pce_opts_cli);
+ show_pce_peer(vty, pce_opts_cli);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_peer_sr_draft07(struct vty *vty)
+{
+ struct pcep_config_group_opts *pce_config = NULL;
+
+ if (vty->node == PCEP_PCE_NODE) {
+ /* TODO need to see if the pce is in use, and reset the
+ * connection */
+ pce_config = &current_pce_opts_g->pce_config_group_opts;
+ current_pce_opts_g->merged = false;
+ } else if (vty->node == PCEP_PCE_CONFIG_NODE) {
+ pce_config = current_pcep_config_group_opts_g;
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+
+ pce_config->draft07 = true;
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_peer_pce_initiated(struct vty *vty)
+{
+ struct pcep_config_group_opts *pce_config = NULL;
+
+ if (vty->node == PCEP_PCE_NODE) {
+ /* TODO need to see if the pce is in use, and reset the
+ * connection */
+ pce_config = &current_pce_opts_g->pce_config_group_opts;
+ current_pce_opts_g->merged = false;
+ } else if (vty->node == PCEP_PCE_CONFIG_NODE) {
+ pce_config = current_pcep_config_group_opts_g;
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+
+ pce_config->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 *pce_config = NULL;
+
+ if (vty->node == PCEP_PCE_NODE) {
+ /* TODO need to see if the pce is in use, and reset the
+ * connection */
+ pce_config = &current_pce_opts_g->pce_config_group_opts;
+ current_pce_opts_g->merged = false;
+ } else if (vty->node == PCEP_PCE_CONFIG_NODE) {
+ pce_config = current_pcep_config_group_opts_g;
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+
+ strncpy(pce_config->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 == PCEP_PCE_NODE) {
+ /* TODO need to see if the pce is in use, and reset the
+ * connection */
+ pce_opts = &current_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_source_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 pcep_config_group_opts *pce_config = NULL;
+ if (vty->node == PCEP_PCE_NODE) {
+ /* TODO need to see if the pce is in use, and reset the
+ * connection */
+ pce_config = &current_pce_opts_g->pce_config_group_opts;
+ current_pce_opts_g->merged = false;
+ } else if (vty->node == PCEP_PCE_CONFIG_NODE) {
+ pce_config = current_pcep_config_group_opts_g;
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+
+ /* Handle the optional source IP */
+ if (ipv6_str != NULL) {
+ pce_config->source_ip.ipa_type = IPADDR_V6;
+ memcpy(&pce_config->source_ip.ipaddr_v6, ipv6,
+ sizeof(struct in6_addr));
+ } else if (ip_str != NULL) {
+ pce_config->source_ip.ipa_type = IPADDR_V4;
+ memcpy(&pce_config->source_ip.ipaddr_v4, ip,
+ sizeof(struct in_addr));
+ }
+
+ /* Handle the optional port */
+ PCEP_VTYSH_INT_ARG_CHECK(port_str, port, pce_config->source_port, 0,
+ 65535);
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_peer_pcep_pce_config_ref(struct vty *vty,
+ const char *config_group_name)
+{
+ if (vty->node == PCEP_PCE_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 *pce_config =
+ pcep_cli_find_pcep_pce_config(config_group_name);
+ if (pce_config == NULL) {
+ vty_out(vty, "%% pce-config [%s] does not exist.\n",
+ config_group_name);
+ return CMD_WARNING;
+ }
+
+ strlcpy(current_pce_opts_g->config_group_name, config_group_name,
+ sizeof(current_pce_opts_g->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 *session_timeout_interval_str, long session_timeout_interval,
+ const char *delegation_timeout_str, long delegation_timeout)
+{
+ struct pcep_config_group_opts *pce_config = NULL;
+ if (vty->node == PCEP_PCE_NODE) {
+ /* TODO need to see if the pce is in use, and reset the
+ * connection */
+ pce_config = &current_pce_opts_g->pce_config_group_opts;
+ current_pce_opts_g->merged = false;
+ } else if (vty->node == PCEP_PCE_CONFIG_NODE) {
+ pce_config = current_pcep_config_group_opts_g;
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+
+ if (min_peer_keep_alive && max_peer_keep_alive)
+ if (min_peer_keep_alive >= max_peer_keep_alive) {
+ return CMD_ERR_NO_MATCH;
+ }
+
+ if (min_peer_dead_timer && max_peer_dead_timer)
+ if (min_peer_dead_timer >= max_peer_dead_timer) {
+ return CMD_ERR_NO_MATCH;
+ }
+
+ /* Handle the arguments */
+ PCEP_VTYSH_INT_ARG_CHECK(keep_alive_str, keep_alive,
+ pce_config->keep_alive_seconds, 0, 64);
+ PCEP_VTYSH_INT_ARG_CHECK(min_peer_keep_alive_str, min_peer_keep_alive,
+ pce_config->min_keep_alive_seconds, 0, 256);
+ PCEP_VTYSH_INT_ARG_CHECK(max_peer_keep_alive_str, max_peer_keep_alive,
+ pce_config->max_keep_alive_seconds, 0, 256);
+ PCEP_VTYSH_INT_ARG_CHECK(dead_timer_str, dead_timer,
+ pce_config->dead_timer_seconds, 3, 256);
+ PCEP_VTYSH_INT_ARG_CHECK(min_peer_dead_timer_str, min_peer_dead_timer,
+ pce_config->min_dead_timer_seconds, 3, 256);
+ PCEP_VTYSH_INT_ARG_CHECK(max_peer_dead_timer_str, max_peer_dead_timer,
+ pce_config->max_dead_timer_seconds, 3, 256);
+ PCEP_VTYSH_INT_ARG_CHECK(pcep_request_str, pcep_request,
+ pce_config->pcep_request_time_seconds, 0, 121);
+ PCEP_VTYSH_INT_ARG_CHECK(
+ session_timeout_interval_str, session_timeout_interval,
+ pce_config->session_timeout_inteval_seconds, 0, 121);
+ PCEP_VTYSH_INT_ARG_CHECK(delegation_timeout_str, delegation_timeout,
+ pce_config->delegation_timeout_seconds, 0, 61);
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_pcc(struct vty *vty)
+{
+ VTY_PUSH_CONTEXT_NULL(PCEP_PCC_NODE);
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_pcc_delete(struct vty *vty)
+{
+ /* Clear the pce_connections */
+ memset(&pce_connections_g, 0, sizeof(pce_connections_g));
+ pcc_msd_configured_g = false;
+
+ pcep_ctrl_remove_pcc(pcep_g->fpt, NULL);
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_pcc_pcc_msd(struct vty *vty, const char *msd_str,
+ long msd)
+{
+ pcc_msd_configured_g = true;
+ PCEP_VTYSH_INT_ARG_CHECK(msd_str, msd, pcc_msd_g, 0, 33);
+
+ 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_cli_pcc_has_pce(peer_name)) {
+ vty_out(vty, "%% The peer [%s] has already been configured.\n",
+ peer_name);
+ return CMD_WARNING;
+ }
+
+ /* Get the optional precedence argument */
+ pce_opts->precedence = DEFAULT_PCE_PRECEDENCE;
+ PCEP_VTYSH_INT_ARG_CHECK(precedence_str, precedence,
+ pce_opts->precedence, 0, 256);
+
+ /* Finalize the pce_opts config values */
+ pcep_cli_merge_pcep_pce_config_options(pce_opts_cli);
+ pcep_cli_add_pce_connection(&pce_opts_cli->pce_opts);
+
+ /* 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;
+ }
+
+ /* Update the pcc_opts with the source ip, port, and msd */
+ struct pcc_opts *pcc_opts_copy =
+ XMALLOC(MTYPE_PCEP, sizeof(struct pcc_opts));
+ memcpy(&pcc_opts_copy->addr,
+ &pce_opts_cli->pce_opts.config_opts.source_ip,
+ sizeof(struct pcc_opts));
+ pcc_opts_copy->msd = pcc_msd_g;
+ pcc_opts_copy->port = pce_opts_cli->pce_opts.config_opts.source_port;
+ if (pcep_ctrl_update_pcc_options(pcep_g->fpt, pcc_opts_copy)) {
+ return CMD_WARNING;
+ }
+
+ /* Send a copy of the pce_opts, this one is only used for the CLI */
+ 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, 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_cli_pcc_has_pce(peer_name)) {
+ vty_out(vty,
+ "%% WARN: The peer [%s] is not connected to the PCC.\n",
+ peer_name);
+ return CMD_WARNING;
+ }
+
+ struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(peer_name);
+ pcep_cli_remove_pce_connection(&pce_opts_cli->pce_opts);
+
+ /* Send a copy of the pce_opts, this one is used for CLI only */
+ struct pce_opts *pce_opts_copy =
+ XMALLOC(MTYPE_PCEP, sizeof(struct pce_opts));
+ memcpy(pce_opts_copy, &pce_opts_cli->pce_opts, sizeof(struct pce_opts));
+ pcep_ctrl_remove_pcc(pcep_g->fpt, pce_opts_copy);
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_show_srte_pcep_pcc(struct vty *vty)
+{
+ vty_out(vty, "pcc msd %d\n", pcc_msd_g);
+
+ return CMD_SUCCESS;
+}
+
+/* Internal util function to print pcep capabilities to a buffer */
+static void print_pcep_capabilities(char *buf, size_t buf_len,
+ pcep_configuration *config)
+{
+ if (config->support_stateful_pce_lsp_update) {
+ csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_STATEFUL);
+ }
+ if (config->support_include_db_version) {
+ csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_INCL_DB_VER);
+ }
+ if (config->support_lsp_triggered_resync) {
+ csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_LSP_TRIGGERED);
+ }
+ if (config->support_lsp_delta_sync) {
+ csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_LSP_DELTA);
+ }
+ if (config->support_pce_triggered_initial_sync) {
+ csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_PCE_TRIGGERED);
+ }
+ if (config->support_sr_te_pst) {
+ csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_SR_TE_PST);
+ }
+ if (config->pcc_can_resolve_nai_to_sid) {
+ csnprintfrr(buf, buf_len, "%s", PCEP_CLI_CAP_PCC_RESOLVE_NAI);
+ }
+}
+
+/* Internal util function to print a pcep session */
+static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts,
+ struct pcep_pcc_info *pcc_info)
+{
+ char buf[1024];
+ buf[0] = '\0';
+
+ vty_out(vty, "\nPCE %s\n", pce_opts->pce_name);
+
+ /* PCE IP */
+ if (IS_IPADDR_V4(&pce_opts->addr)) {
+ vty_out(vty, " PCE IP %pI4 port %d\n",
+ &pce_opts->addr.ipaddr_v4, pce_opts->port);
+ } else if (IS_IPADDR_V6(&pce_opts->addr)) {
+ vty_out(vty, " PCE IPv6 %pI6 port %d\n",
+ &pce_opts->addr.ipaddr_v6, pce_opts->port);
+ }
+
+ /* PCC IP */
+ if (IS_IPADDR_V4(&pcc_info->pcc_addr)) {
+ vty_out(vty, " PCC IP %pI4 port %d\n",
+ &pcc_info->pcc_addr.ipaddr_v4, pcc_info->pcc_port);
+ } else if (IS_IPADDR_V6(&pcc_info->pcc_addr)) {
+ vty_out(vty, " PCC IPv6 %pI6 port %d\n",
+ &pcc_info->pcc_addr.ipaddr_v6, pcc_info->pcc_port);
+ }
+ vty_out(vty, " PCC MSD %d\n", pcc_info->msd);
+
+ if (pcc_info->status == PCEP_PCC_OPERATING) {
+ vty_out(vty, " Session Status UP\n");
+ } else {
+ vty_out(vty, " Session Status %s\n",
+ pcc_status_name(pcc_info->status));
+ }
+
+ if (pcc_info->is_best_multi_pce) {
+ vty_out(vty, " Precedence %d, best candidate\n",
+ ((pcc_info->precedence > 0) ? pcc_info->precedence
+ : DEFAULT_PCE_PRECEDENCE));
+ } else {
+ vty_out(vty, " Precedence %d\n",
+ ((pcc_info->precedence > 0) ? pcc_info->precedence
+ : DEFAULT_PCE_PRECEDENCE));
+ }
+ vty_out(vty, " Confidence %s\n",
+ ((pcc_info->previous_best) ? "low"
+ : "normal"));
+
+ /* PCEPlib pcep session values, get a thread safe copy of the counters
+ */
+ pcep_session *session =
+ pcep_ctrl_get_pcep_session(pcep_g->fpt, pcc_info->pcc_id);
+
+ /* Config Options values */
+ struct pcep_config_group_opts *config_opts = &pce_opts->config_opts;
+ if (session != NULL) {
+ vty_out(vty, " Timer: KeepAlive config %d, pce-negotiated %d\n",
+ config_opts->keep_alive_seconds,
+ session->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds);
+ vty_out(vty, " Timer: DeadTimer config %d, pce-negotiated %d\n",
+ config_opts->dead_timer_seconds,
+ session->pcc_config.dead_timer_pce_negotiated_seconds);
+ } else {
+ vty_out(vty, " Timer: KeepAlive %d\n",
+ config_opts->keep_alive_seconds);
+ vty_out(vty, " Timer: DeadTimer %d\n",
+ config_opts->dead_timer_seconds);
+ }
+ vty_out(vty, " Timer: PcRequest %d\n",
+ config_opts->pcep_request_time_seconds);
+ vty_out(vty, " Timer: SessionTimeout Interval %d\n",
+ config_opts->session_timeout_inteval_seconds);
+ vty_out(vty, " Timer: Delegation Timeout %d\n",
+ config_opts->delegation_timeout_seconds);
+ if (strlen(config_opts->tcp_md5_auth) > 0) {
+ vty_out(vty, " TCP MD5 Auth Str: %s\n",
+ config_opts->tcp_md5_auth);
+ } else {
+ vty_out(vty, " No TCP MD5 Auth\n");
+ }
+
+ if (config_opts->draft07) {
+ vty_out(vty, " PCE SR Version draft07\n");
+ } else {
+ vty_out(vty, " PCE SR Version draft16 and RFC8408\n");
+ }
+
+ vty_out(vty, " Next PcReq ID %d\n", pcc_info->next_reqid);
+ vty_out(vty, " Next PLSP ID %d\n", pcc_info->next_plspid);
+
+ if (session != NULL) {
+ if (pcc_info->status == PCEP_PCC_SYNCHRONIZING
+ || pcc_info->status == PCEP_PCC_OPERATING) {
+ time_t current_time = time(NULL);
+ struct tm lt = {0};
+ /* Just for the timezone */
+ localtime_r(&current_time, &lt);
+ gmtime_r(&session->time_connected, &lt);
+ vty_out(vty,
+ " Connected for %ld seconds, since %d-%02d-%02d %02d:%02d:%02d UTC\n",
+ (current_time - session->time_connected),
+ lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday,
+ lt.tm_hour, lt.tm_min, lt.tm_sec);
+ }
+
+ /* PCC capabilities */
+ buf[0] = '\0';
+ int index = 0;
+ if (config_opts->pce_initiated) {
+ index += csnprintfrr(buf, sizeof(buf), "%s",
+ PCEP_CLI_CAP_PCC_PCE_INITIATED);
+ } else {
+ index += csnprintfrr(buf, sizeof(buf), "%s",
+ PCEP_CLI_CAP_PCC_INITIATED);
+ }
+ print_pcep_capabilities(buf, sizeof(buf) - index,
+ &session->pcc_config);
+ vty_out(vty, " PCC Capabilities:%s\n", buf);
+
+ /* PCE capabilities */
+ buf[0] = '\0';
+ print_pcep_capabilities(buf, sizeof(buf), &session->pce_config);
+ if (buf[0] != '\0') {
+ vty_out(vty, " PCE Capabilities:%s\n", buf);
+ }
+ XFREE(MTYPE_PCEP, session);
+ } else {
+ vty_out(vty, " Detailed session information not available\n");
+ }
+
+ /* Message Counters, get a thread safe copy of the counters */
+ struct counters_group *group =
+ pcep_ctrl_get_counters(pcep_g->fpt, pcc_info->pcc_id);
+
+ if (group != NULL) {
+ struct counters_subgroup *rx_msgs =
+ find_subgroup(group, COUNTER_SUBGROUP_ID_RX_MSG);
+ struct counters_subgroup *tx_msgs =
+ find_subgroup(group, COUNTER_SUBGROUP_ID_TX_MSG);
+
+ if (rx_msgs != NULL && tx_msgs != NULL) {
+ vty_out(vty, " PCEP Message Statistics\n");
+ vty_out(vty, " %27s %6s\n", "Sent", "Rcvd");
+ for (int i = 0; i < rx_msgs->max_counters; i++) {
+ struct counter *rx_counter =
+ rx_msgs->counters[i];
+ struct counter *tx_counter =
+ tx_msgs->counters[i];
+ if (rx_counter != NULL && tx_counter != NULL) {
+ vty_out(vty, " %20s: %5d %5d\n",
+ tx_counter->counter_name,
+ tx_counter->counter_value,
+ rx_counter->counter_value);
+ }
+ }
+ vty_out(vty, " %20s: %5d %5d\n", "Total",
+ subgroup_counters_total(tx_msgs),
+ subgroup_counters_total(rx_msgs));
+ }
+ pcep_lib_free_counters(group);
+ } else {
+ vty_out(vty, " Counters not available\n");
+ }
+
+ XFREE(MTYPE_PCEP, pcc_info);
+}
+
+static int path_pcep_cli_show_srte_pcep_session(struct vty *vty,
+ const char *pcc_peer)
+{
+ struct pce_opts_cli *pce_opts_cli;
+ struct pcep_pcc_info *pcc_info;
+
+ /* Only show 1 PCEP session */
+ 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;
+ }
+
+ if (!pcep_cli_pcc_has_pce(pcc_peer)) {
+ vty_out(vty, "%% PCC is not connected to PCE [%s].\n",
+ pcc_peer);
+ return CMD_WARNING;
+ }
+
+ pcc_info = pcep_ctrl_get_pcc_info(pcep_g->fpt, pcc_peer);
+ if (pcc_info == NULL) {
+ vty_out(vty,
+ "%% Cannot retrieve PCEP session info for PCE [%s]\n",
+ pcc_peer);
+ return CMD_WARNING;
+ }
+
+ print_pcep_session(vty, &pce_opts_cli->pce_opts, pcc_info);
+
+ return CMD_SUCCESS;
+ }
+
+ /* Show all PCEP sessions */
+ struct pce_opts *pce_opts;
+ int num_pcep_sessions_conf = 0;
+ int num_pcep_sessions_conn = 0;
+ for (int i = 0; i < MAX_PCC; i++) {
+ pce_opts = pce_connections_g.connections[i];
+ if (pce_opts == NULL) {
+ continue;
+ }
+
+ pcc_info =
+ pcep_ctrl_get_pcc_info(pcep_g->fpt, pce_opts->pce_name);
+ if (pcc_info == NULL) {
+ vty_out(vty,
+ "%% Cannot retrieve PCEP session info for PCE [%s]\n",
+ pce_opts->pce_name);
+ continue;
+ }
+
+ num_pcep_sessions_conn +=
+ pcc_info->status == PCEP_PCC_OPERATING ? 1 : 0;
+ num_pcep_sessions_conf++;
+ print_pcep_session(vty, pce_opts, pcc_info);
+ }
+
+ vty_out(vty, "PCEP Sessions => Configured %d ; Connected %d\n",
+ num_pcep_sessions_conf, num_pcep_sessions_conn);
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_clear_srte_pcep_session(struct vty *vty,
+ const char *pcc_peer)
+{
+ struct pce_opts_cli *pce_opts_cli;
+
+ /* Only clear 1 PCEP session */
+ 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;
+ }
+
+ if (!pcep_cli_pcc_has_pce(pcc_peer)) {
+ vty_out(vty, "%% PCC is not connected to PCE [%s].\n",
+ pcc_peer);
+ return CMD_WARNING;
+ }
+
+ pcep_ctrl_reset_pcc_session(pcep_g->fpt,
+ pce_opts_cli->pce_opts.pce_name);
+ vty_out(vty, "PCEP session cleared for peer %s\n", pcc_peer);
+
+ return CMD_SUCCESS;
+ }
+
+ /* Clear all PCEP sessions */
+ struct pce_opts *pce_opts;
+ int num_pcep_sessions = 0;
+ for (int i = 0; i < MAX_PCC; i++) {
+ pce_opts = pce_connections_g.connections[i];
+ if (pce_opts == NULL) {
+ continue;
+ }
+
+ num_pcep_sessions++;
+ pcep_ctrl_reset_pcc_session(pcep_g->fpt, pce_opts->pce_name);
+ vty_out(vty, "PCEP session cleared for peer %s\n",
+ pce_opts->pce_name);
+ }
+
+ vty_out(vty, "Cleared [%d] PCEP sessions\n", num_pcep_sessions);
+
+ 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_pcep_config_write(struct vty *vty)
+{
+ vty_out(vty, " pcep\n");
+ return 1;
+}
+
+int pcep_cli_pcc_config_write(struct vty *vty)
+{
+ struct pce_opts *pce_opts;
+ char buf[128] = "";
+ int lines = 0;
+
+ /* The MSD, nor any PCE peers have been configured on the PCC */
+ if (!pcc_msd_configured_g && pce_connections_g.num_connections == 0) {
+ return lines;
+ }
+
+ vty_out(vty, " pcc\n");
+ lines++;
+
+ /* Prepare the MSD, if present */
+ if (pcc_msd_configured_g) {
+ vty_out(vty, " %s %d\n", PCEP_VTYSH_ARG_MSD, pcc_msd_g);
+ lines++;
+ }
+
+ if (pce_connections_g.num_connections == 0) {
+ return lines;
+ }
+
+ buf[0] = 0;
+ for (int i = 0; i < MAX_PCC; i++) {
+ pce_opts = pce_connections_g.connections[i];
+ if (pce_opts == NULL) {
+ continue;
+ }
+
+ /* Only show the PCEs configured in the pcc sub-command */
+ if (!pcep_cli_pcc_has_pce(pce_opts->pce_name)) {
+ continue;
+ }
+
+ csnprintfrr(buf, sizeof(buf), " peer %s",
+ pce_opts->pce_name);
+ if (pce_opts->precedence > 0
+ && pce_opts->precedence != DEFAULT_PCE_PRECEDENCE) {
+ csnprintfrr(buf, sizeof(buf), " %s %d",
+ PCEP_VTYSH_ARG_PRECEDENCE,
+ pce_opts->precedence);
+ }
+ vty_out(vty, "%s\n", buf);
+ lines++;
+ buf[0] = 0;
+ }
+
+ return lines;
+}
+
+/* Internal function used by pcep_cli_pce_config_write()
+ * and pcep_cli_pcep_pce_config_write() */
+static int pcep_cli_print_pce_config(struct pcep_config_group_opts *group_opts,
+ char *buf, size_t buf_len)
+{
+ int lines = 0;
+
+ if (group_opts->source_ip.ipa_type != IPADDR_NONE
+ || group_opts->source_port != 0) {
+ csnprintfrr(buf, buf_len, " ");
+ if (IS_IPADDR_V4(&group_opts->source_ip)) {
+ csnprintfrr(buf, buf_len, " %s %s %pI4",
+ PCEP_VTYSH_ARG_SOURCE_ADDRESS,
+ PCEP_VTYSH_ARG_IP,
+ &group_opts->source_ip.ipaddr_v4);
+ } else if (IS_IPADDR_V6(&group_opts->source_ip)) {
+ csnprintfrr(buf, buf_len, " %s %s %pI6",
+ PCEP_VTYSH_ARG_SOURCE_ADDRESS,
+ PCEP_VTYSH_ARG_IPV6,
+ &group_opts->source_ip.ipaddr_v6);
+ }
+ if (group_opts->source_port > 0) {
+ csnprintfrr(buf, buf_len, " %s %d", PCEP_VTYSH_ARG_PORT,
+ group_opts->source_port);
+ }
+ csnprintfrr(buf, buf_len, "\n");
+ lines++;
+ }
+ /* Group the keep-alive together for devman */
+ if ((group_opts->keep_alive_seconds > 0)
+ || (group_opts->min_keep_alive_seconds > 0)
+ || (group_opts->max_keep_alive_seconds > 0)) {
+ csnprintfrr(buf, buf_len, " %s", PCEP_VTYSH_ARG_TIMER);
+
+ if (group_opts->keep_alive_seconds > 0) {
+ csnprintfrr(buf, buf_len, " %s %d",
+ PCEP_VTYSH_ARG_KEEP_ALIVE,
+ group_opts->keep_alive_seconds);
+ }
+ if (group_opts->min_keep_alive_seconds > 0) {
+ csnprintfrr(buf, buf_len, " %s %d",
+ PCEP_VTYSH_ARG_KEEP_ALIVE_MIN,
+ group_opts->min_keep_alive_seconds);
+ }
+ if (group_opts->max_keep_alive_seconds > 0) {
+ csnprintfrr(buf, buf_len, " %s %d",
+ PCEP_VTYSH_ARG_KEEP_ALIVE_MAX,
+ group_opts->max_keep_alive_seconds);
+ }
+ csnprintfrr(buf, buf_len, "\n");
+ lines++;
+ }
+
+ /* Group the dead-timer together for devman */
+ if ((group_opts->dead_timer_seconds > 0)
+ || (group_opts->min_dead_timer_seconds > 0)
+ || (group_opts->max_dead_timer_seconds > 0)) {
+ csnprintfrr(buf, buf_len, " %s", PCEP_VTYSH_ARG_TIMER);
+
+ if (group_opts->dead_timer_seconds > 0) {
+ csnprintfrr(buf, buf_len, " %s %d",
+ PCEP_VTYSH_ARG_DEAD_TIMER,
+ group_opts->dead_timer_seconds);
+ }
+ if (group_opts->min_dead_timer_seconds > 0) {
+ csnprintfrr(buf, buf_len, " %s %d",
+ PCEP_VTYSH_ARG_DEAD_TIMER_MIN,
+ group_opts->min_dead_timer_seconds);
+ }
+ if (group_opts->max_dead_timer_seconds > 0) {
+ csnprintfrr(buf, buf_len, " %s %d",
+ PCEP_VTYSH_ARG_DEAD_TIMER_MAX,
+ group_opts->max_dead_timer_seconds);
+ }
+ csnprintfrr(buf, buf_len, "\n");
+ lines++;
+ }
+
+ if (group_opts->pcep_request_time_seconds > 0) {
+ csnprintfrr(buf, buf_len, " %s %s %d\n",
+ PCEP_VTYSH_ARG_TIMER, PCEP_VTYSH_ARG_PCEP_REQUEST,
+ group_opts->pcep_request_time_seconds);
+ lines++;
+ }
+ if (group_opts->delegation_timeout_seconds > 0) {
+ csnprintfrr(buf, buf_len, " %s %s %d\n",
+ PCEP_VTYSH_ARG_TIMER,
+ PCEP_VTYSH_ARG_DELEGATION_TIMEOUT,
+ group_opts->delegation_timeout_seconds);
+ lines++;
+ }
+ if (group_opts->session_timeout_inteval_seconds > 0) {
+ csnprintfrr(buf, buf_len, " %s %s %d\n",
+ PCEP_VTYSH_ARG_TIMER,
+ PCEP_VTYSH_ARG_SESSION_TIMEOUT,
+ group_opts->session_timeout_inteval_seconds);
+ lines++;
+ }
+ if (group_opts->tcp_md5_auth[0] != '\0') {
+ csnprintfrr(buf, buf_len, " %s %s\n", PCEP_VTYSH_ARG_TCP_MD5,
+ group_opts->tcp_md5_auth);
+ lines++;
+ }
+ if (group_opts->draft07) {
+ csnprintfrr(buf, buf_len, " %s\n",
+ PCEP_VTYSH_ARG_SR_DRAFT07);
+ lines++;
+ }
+ if (group_opts->pce_initiated) {
+ csnprintfrr(buf, buf_len, " %s\n", PCEP_VTYSH_ARG_PCE_INIT);
+ lines++;
+ }
+
+ return lines;
+}
+
+int pcep_cli_pce_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, " pce %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 if (IS_IPADDR_V4(&pce_opts->addr)) {
+ 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 %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_pce_config(
+ &pce_opts_cli->pce_config_group_opts, buf, sizeof(buf));
+
+ vty_out(vty, "%s", buf);
+ buf[0] = '\0';
+ }
+
+ return lines;
+}
+
+int pcep_cli_pcep_pce_config_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 %s\n", group_opts->name);
+ lines += 1;
+
+ lines +=
+ pcep_cli_print_pce_config(group_opts, buf, sizeof(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(show_debugging_pathd_pcep,
+ show_debugging_pathd_pcep_cmd,
+ "show debugging pathd-pcep",
+ SHOW_STR
+ "State of each debugging option\n"
+ "pathd pcep module debugging\n")
+{
+ vty_out(vty, "Pathd pcep debugging status:\n");
+
+ if (DEBUG_MODE_CHECK(&pcep_g->dbg, DEBUG_MODE_CONF)) {
+ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_BASIC))
+ vty_out(vty, " Pathd pcep %s debugging is on\n",
+ PCEP_VTYSH_ARG_BASIC);
+ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PATH))
+ vty_out(vty, " Pathd pcep %s debugging is on\n",
+ PCEP_VTYSH_ARG_PATH);
+ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEP))
+ vty_out(vty, " Pathd pcep %s debugging is on\n",
+ PCEP_VTYSH_ARG_MESSAGE);
+ if (DEBUG_FLAGS_CHECK(&pcep_g->dbg, PCEP_DEBUG_MODE_PCEPLIB))
+ vty_out(vty, " Pathd pcep %s debugging is on\n",
+ PCEP_VTYSH_ARG_PCEPLIB);
+ }
+
+ return CMD_SUCCESS;
+}
+
+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_srte_pcep_counters,
+ pcep_cli_show_srte_pcep_counters_cmd,
+ "show sr-te pcep counters",
+ SHOW_STR
+ "SR-TE info\n"
+ "PCEP info\n"
+ "PCEP counters\n")
+{
+ return path_pcep_cli_show_srte_pcep_counters(vty);
+}
+
+DEFPY_NOSH(
+ pcep_cli_pcep,
+ pcep_cli_pcep_cmd,
+ "pcep",
+ "PCEP configuration\n")
+{
+ vty->node = PCEP_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFPY_NOSH(
+ pcep_cli_pcep_pce_config,
+ pcep_cli_pcep_pce_config_cmd,
+ "[no] pce-config WORD$name",
+ NO_STR
+ "Shared configuration\n"
+ "Shared configuration name\n")
+{
+ if (no == NULL)
+ return path_pcep_cli_pcep_pce_config(vty, name);
+ return path_pcep_cli_pcep_pce_config_delete(vty, name);
+}
+
+DEFPY(pcep_cli_show_srte_pcep_pce_config,
+ pcep_cli_show_srte_pcep_pce_config_cmd,
+ "show sr-te pcep pce-config [<default|WORD>$name]",
+ SHOW_STR
+ "SR-TE info\n"
+ "PCEP info\n"
+ "Show shared PCE configuration\n"
+ "Show default hard-coded values\n"
+ "Shared configuration name\n")
+{
+ return path_pcep_cli_show_srte_pcep_pce_config(vty, name);
+}
+
+DEFPY_NOSH(
+ pcep_cli_pce,
+ pcep_cli_pce_cmd,
+ "[no] pce WORD$name",
+ NO_STR
+ "PCE configuration, address sub-config is mandatory\n"
+ "PCE name\n")
+{
+ if (no == NULL)
+ return path_pcep_cli_pce(vty, name);
+ return path_pcep_cli_pce_delete(vty, name);
+}
+
+DEFPY(pcep_cli_show_srte_pcep_pce,
+ pcep_cli_show_srte_pcep_pce_cmd,
+ "show sr-te pcep pce [WORD$name]",
+ SHOW_STR
+ "SR-TE info\n"
+ "PCEP info\n"
+ "Show detailed pce values\n"
+ "pce name\n")
+{
+ return path_pcep_cli_show_srte_pcep_pce(vty, name);
+}
+
+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 <ip A.B.C.D | ipv6 X:X::X:X> [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_source_address,
+ pcep_cli_peer_source_address_cmd,
+ "source-address [ip A.B.C.D | ipv6 X:X::X:X] [port (1024-65535)]",
+ "PCE source IP Address configuration\n"
+ "PCE source IPv4 address\n"
+ "PCE source IPv4 address value\n"
+ "PCE source IPv6 address\n"
+ "PCE source IPv6 address value\n"
+ "Source PCE server port\n"
+ "Source PCE server port value\n")
+{
+ return path_pcep_cli_peer_source_address(vty, ip_str, &ip, ipv6_str,
+ &ipv6, port_str, port);
+}
+
+DEFPY(pcep_cli_peer_pcep_pce_config_ref,
+ pcep_cli_peer_pcep_pce_config_ref_cmd,
+ "config WORD$name",
+ "PCE shared configuration to use\n"
+ "Shared configuration name\n")
+{
+ return path_pcep_cli_peer_pcep_pce_config_ref(vty, name);
+}
+
+DEFPY(pcep_cli_peer_timers,
+ pcep_cli_peer_timers_cmd,
+ "timer [keep-alive (1-63)] [min-peer-keep-alive (1-255)] [max-peer-keep-alive (1-255)] "
+ "[dead-timer (4-255)] [min-peer-dead-timer (4-255)] [max-peer-dead-timer (4-255)] "
+ "[pcep-request (1-120)] [session-timeout-interval (1-120)] [delegation-timeout (1-60)]",
+ "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 Session Timeout Interval\n"
+ "PCC Session Timeout Interval value in seconds\n"
+ "Multi-PCE delegation timeout\n"
+ "Multi-PCE delegation timeout 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, session_timeout_interval_str,
+ session_timeout_interval, delegation_timeout_str,
+ delegation_timeout);
+}
+
+DEFPY_NOSH(
+ pcep_cli_pcc,
+ pcep_cli_pcc_cmd,
+ "[no] pcc",
+ NO_STR
+ "PCC configuration\n")
+{
+ if (no != NULL) {
+ return path_pcep_cli_pcc_delete(vty);
+ } else {
+ return path_pcep_cli_pcc(vty);
+ }
+}
+
+DEFPY(pcep_cli_pcc_pcc_msd,
+ pcep_cli_pcc_pcc_msd_cmd,
+ "msd (1-32)",
+ "PCC maximum SID depth \n"
+ "PCC maximum SID depth value\n")
+{
+ return path_pcep_cli_pcc_pcc_msd(vty, 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);
+ }
+}
+
+DEFPY(pcep_cli_show_srte_pcc,
+ pcep_cli_show_srte_pcc_cmd,
+ "show sr-te pcep pcc",
+ SHOW_STR
+ "SR-TE info\n"
+ "PCEP info\n"
+ "Show current PCC configuration\n")
+{
+ return path_pcep_cli_show_srte_pcep_pcc(vty);
+}
+
+DEFPY(pcep_cli_show_srte_pcep_session,
+ pcep_cli_show_srte_pcep_session_cmd,
+ "show sr-te pcep session [WORD]$pce",
+ SHOW_STR
+ "SR-TE info\n"
+ "PCEP info\n"
+ "Show PCEP Session information\n"
+ "PCE name\n")
+{
+ return path_pcep_cli_show_srte_pcep_session(vty, pce);
+}
+
+DEFPY(pcep_cli_clear_srte_pcep_session,
+ pcep_cli_clear_srte_pcep_session_cmd,
+ "clear sr-te pcep session [WORD]$pce",
+ CLEAR_STR
+ "SR-TE\n"
+ "PCEP\n"
+ "Reset PCEP connection\n"
+ "PCE name\n")
+{
+ return path_pcep_cli_clear_srte_pcep_session(vty, pce);
+}
+
+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);
+
+ memset(&pce_connections_g, 0, sizeof(pce_connections_g));
+
+ install_node(&pcep_node);
+ install_node(&pcep_pcc_node);
+ install_node(&pcep_pce_node);
+ install_node(&pcep_pce_config_node);
+
+ install_default(PCEP_PCE_CONFIG_NODE);
+ install_default(PCEP_PCE_NODE);
+ install_default(PCEP_PCC_NODE);
+ install_default(PCEP_NODE);
+
+ install_element(SR_TRAFFIC_ENG_NODE, &pcep_cli_pcep_cmd);
+
+ /* PCEP configuration group related configuration commands */
+ install_element(PCEP_NODE, &pcep_cli_pcep_pce_config_cmd);
+ install_element(PCEP_PCE_CONFIG_NODE,
+ &pcep_cli_peer_source_address_cmd);
+ install_element(PCEP_PCE_CONFIG_NODE, &pcep_cli_peer_timers_cmd);
+ install_element(PCEP_PCE_CONFIG_NODE, &pcep_cli_peer_sr_draft07_cmd);
+ install_element(PCEP_PCE_CONFIG_NODE, &pcep_cli_peer_pce_initiated_cmd);
+ install_element(PCEP_PCE_CONFIG_NODE, &pcep_cli_peer_tcp_md5_auth_cmd);
+
+ /* PCE peer related configuration commands */
+ install_element(PCEP_NODE, &pcep_cli_pce_cmd);
+ install_element(PCEP_PCE_NODE, &pcep_cli_peer_address_cmd);
+ install_element(PCEP_PCE_NODE, &pcep_cli_peer_source_address_cmd);
+ install_element(PCEP_PCE_NODE, &pcep_cli_peer_pcep_pce_config_ref_cmd);
+ install_element(PCEP_PCE_NODE, &pcep_cli_peer_timers_cmd);
+ install_element(PCEP_PCE_NODE, &pcep_cli_peer_sr_draft07_cmd);
+ install_element(PCEP_PCE_NODE, &pcep_cli_peer_pce_initiated_cmd);
+ install_element(PCEP_PCE_NODE, &pcep_cli_peer_tcp_md5_auth_cmd);
+
+ /* PCC related configuration commands */
+ install_element(ENABLE_NODE, &pcep_cli_show_srte_pcc_cmd);
+ install_element(PCEP_NODE, &pcep_cli_pcc_cmd);
+ install_element(PCEP_PCC_NODE, &pcep_cli_pcc_pcc_peer_cmd);
+ install_element(PCEP_PCC_NODE, &pcep_cli_pcc_pcc_msd_cmd);
+
+ /* Top commands */
+ install_element(CONFIG_NODE, &pcep_cli_debug_cmd);
+ install_element(ENABLE_NODE, &pcep_cli_debug_cmd);
+ install_element(ENABLE_NODE, &show_debugging_pathd_pcep_cmd);
+ install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_counters_cmd);
+ install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_pce_config_cmd);
+ install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_pce_cmd);
+ install_element(ENABLE_NODE, &pcep_cli_show_srte_pcep_session_cmd);
+ install_element(ENABLE_NODE, &pcep_cli_clear_srte_pcep_session_cmd);
+}
diff --git a/pathd/path_pcep_cli.h b/pathd/path_pcep_cli.h
new file mode 100644
index 0000000000..0b101ab215
--- /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_config.c b/pathd/path_pcep_config.c
new file mode 100644
index 0000000000..989223ebc3
--- /dev/null
+++ b/pathd/path_pcep_config.c
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <northbound.h>
+#include <yang.h>
+#include <printfrr.h>
+#include <pcep-objects.h>
+#include "pathd/pathd.h"
+#include "pathd/path_pcep.h"
+#include "pathd/path_pcep_config.h"
+#include "pathd/path_pcep_debug.h"
+#include "thread.h"
+
+#define MAX_XPATH 256
+#define MAX_FLOAT_LEN 22
+#define INETADDR4_MAXLEN 16
+#define INETADDR6_MAXLEN 40
+
+
+static void copy_candidate_objfun_info(struct srte_candidate *candidate,
+ struct path *path);
+static void copy_candidate_affinity_filters(struct srte_candidate *candidate,
+ struct path *path);
+static struct path_hop *
+path_pcep_config_list_path_hops(struct srte_segment_list *segment_list);
+static struct srte_candidate *lookup_candidate(struct lsp_nb_key *key);
+static char *candidate_name(struct srte_candidate *candidate);
+static enum pcep_lsp_operational_status
+status_int_to_ext(enum srte_policy_status status);
+static enum pcep_sr_subobj_nai pcep_nai_type(enum srte_segment_nai_type type);
+static enum srte_segment_nai_type srte_nai_type(enum pcep_sr_subobj_nai type);
+
+static int path_pcep_config_lookup_cb(struct thread *t)
+{
+ struct path *path = THREAD_ARG(t);
+ struct srte_candidate *candidate = lookup_candidate(&path->nbkey);
+ struct srte_lsp *lsp;
+
+ if (candidate == NULL)
+ return 0;
+
+ lsp = candidate->lsp;
+
+ if (path->name == NULL)
+ path->name = candidate_name(candidate);
+ if (path->type == SRTE_CANDIDATE_TYPE_UNDEFINED)
+ path->type = candidate->type;
+ if (path->create_origin == SRTE_ORIGIN_UNDEFINED)
+ path->create_origin = candidate->protocol_origin;
+ if ((path->update_origin == SRTE_ORIGIN_UNDEFINED)
+ && (lsp->segment_list != NULL))
+ path->update_origin = lsp->segment_list->protocol_origin;
+
+ return 0;
+}
+
+void path_pcep_config_lookup(struct path *path)
+{
+ /*
+ * Configuration access is strictly done via the main thread
+ */
+ thread_execute(master, path_pcep_config_lookup_cb, path, 0);
+}
+
+struct path *path_pcep_config_get_path(struct lsp_nb_key *key)
+{
+ struct srte_candidate *candidate = lookup_candidate(key);
+ if (candidate == NULL)
+ return NULL;
+ return candidate_to_path(candidate);
+}
+
+void path_pcep_config_list_path(path_list_cb_t cb, void *arg)
+{
+ struct path *path;
+ struct srte_policy *policy;
+ struct srte_candidate *candidate;
+
+ RB_FOREACH (policy, srte_policy_head, &srte_policies) {
+ RB_FOREACH (candidate, srte_candidate_head,
+ &policy->candidate_paths) {
+ path = candidate_to_path(candidate);
+ if (!cb(path, arg))
+ return;
+ }
+ }
+}
+
+struct path *candidate_to_path(struct srte_candidate *candidate)
+{
+ char *name;
+ struct path *path;
+ struct path_hop *hop = NULL;
+ struct path_metric *metric = NULL;
+ struct srte_policy *policy;
+ struct srte_lsp *lsp;
+ enum pcep_lsp_operational_status status;
+ enum srte_protocol_origin update_origin = 0;
+ char *originator = NULL;
+
+ policy = candidate->policy;
+ lsp = candidate->lsp;
+
+ if (lsp->segment_list != NULL) {
+ hop = path_pcep_config_list_path_hops(lsp->segment_list);
+ update_origin = lsp->segment_list->protocol_origin;
+ originator = XSTRDUP(MTYPE_PCEP, lsp->segment_list->originator);
+ }
+ path = pcep_new_path();
+ name = candidate_name(candidate);
+ if (CHECK_FLAG(candidate->flags, F_CANDIDATE_BEST)) {
+ status = status_int_to_ext(policy->status);
+ } else {
+ status = PCEP_LSP_OPERATIONAL_DOWN;
+ }
+ for (uint32_t i = 0; i < MAX_METRIC_TYPE; i++) {
+ struct path_metric *path_metric;
+ struct srte_metric *srte_metric = &lsp->metrics[i];
+ if (CHECK_FLAG(srte_metric->flags, F_METRIC_IS_DEFINED)) {
+ path_metric = pcep_new_metric();
+ path_metric->next = metric;
+ metric = path_metric;
+ metric->type = i + 1;
+ metric->value = srte_metric->value;
+ metric->enforce = CHECK_FLAG(srte_metric->flags,
+ F_METRIC_IS_REQUIRED);
+ metric->is_bound = CHECK_FLAG(srte_metric->flags,
+ F_METRIC_IS_BOUND);
+ metric->is_computed = CHECK_FLAG(srte_metric->flags,
+ F_METRIC_IS_COMPUTED);
+ }
+ }
+ *path = (struct path){
+ .nbkey = (struct lsp_nb_key){.color = policy->color,
+ .endpoint = policy->endpoint,
+ .preference =
+ candidate->preference},
+ .create_origin = lsp->protocol_origin,
+ .update_origin = update_origin,
+ .originator = originator,
+ .plsp_id = 0,
+ .name = name,
+ .type = candidate->type,
+ .srp_id = 0,
+ .req_id = 0,
+ .binding_sid = policy->binding_sid,
+ .status = status,
+ .do_remove = false,
+ .go_active = false,
+ .was_created = false,
+ .was_removed = false,
+ .is_synching = false,
+ .is_delegated = false,
+ .first_hop = hop,
+ .first_metric = metric};
+
+ path->has_bandwidth = CHECK_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH);
+ if (path->has_bandwidth) {
+ path->enforce_bandwidth =
+ CHECK_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH);
+ path->bandwidth = lsp->bandwidth;
+ } else {
+ path->enforce_bandwidth = true;
+ path->bandwidth = 0;
+ }
+
+ copy_candidate_objfun_info(candidate, path);
+ copy_candidate_affinity_filters(candidate, path);
+
+ return path;
+}
+
+void copy_candidate_objfun_info(struct srte_candidate *candidate,
+ struct path *path)
+{
+ struct srte_lsp *lsp = candidate->lsp;
+
+ if (lsp != NULL) {
+ if (CHECK_FLAG(lsp->flags, F_CANDIDATE_HAS_OBJFUN)) {
+ path->has_pce_objfun = true;
+ path->pce_objfun = lsp->objfun;
+ } else {
+ path->has_pce_objfun = false;
+ path->pce_objfun = OBJFUN_UNDEFINED;
+ }
+ }
+ if (CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN)) {
+ path->has_pcc_objfun = true;
+ path->pcc_objfun = candidate->objfun;
+ path->enforce_pcc_objfun = CHECK_FLAG(
+ candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN);
+
+ } else {
+ path->has_pcc_objfun = false;
+ path->pcc_objfun = OBJFUN_UNDEFINED;
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN);
+ }
+}
+
+void copy_candidate_affinity_filters(struct srte_candidate *candidate,
+ struct path *path)
+{
+ bool eany = CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_EXCLUDE_ANY);
+ bool iany = CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_INCLUDE_ANY);
+ bool iall = CHECK_FLAG(candidate->flags, F_CANDIDATE_HAS_INCLUDE_ALL);
+ path->has_affinity_filters = eany || iany || iall;
+ path->affinity_filters[AFFINITY_FILTER_EXCLUDE_ANY - 1] =
+ eany ? candidate->affinity_filters[AFFINITY_FILTER_EXCLUDE_ANY
+ - 1]
+ : 0;
+ path->affinity_filters[AFFINITY_FILTER_INCLUDE_ANY - 1] =
+ iany ? candidate->affinity_filters[AFFINITY_FILTER_INCLUDE_ANY
+ - 1]
+ : 0;
+ path->affinity_filters[AFFINITY_FILTER_INCLUDE_ALL - 1] =
+ iall ? candidate->affinity_filters[AFFINITY_FILTER_INCLUDE_ALL
+ - 1]
+ : 0;
+}
+
+struct path_hop *
+path_pcep_config_list_path_hops(struct srte_segment_list *segment_list)
+{
+ struct srte_segment_entry *segment;
+ struct path_hop *hop = NULL, *last_hop = NULL;
+
+ RB_FOREACH_REVERSE (segment, srte_segment_entry_head,
+ &segment_list->segments) {
+ hop = pcep_new_hop();
+ *hop = (struct path_hop){
+ .next = last_hop,
+ .is_loose = false,
+ .has_sid = true,
+ .is_mpls = true,
+ .has_attribs = false,
+ .sid = {.mpls = {.label = segment->sid_value}},
+ .has_nai =
+ segment->nai_type != SRTE_SEGMENT_NAI_TYPE_NONE,
+ .nai = {.type = pcep_nai_type(segment->nai_type)}};
+ switch (segment->nai_type) {
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
+ memcpy(&hop->nai.local_addr, &segment->nai_local_addr,
+ sizeof(struct ipaddr));
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY:
+ memcpy(&hop->nai.local_addr, &segment->nai_local_addr,
+ sizeof(struct ipaddr));
+ memcpy(&hop->nai.remote_addr, &segment->nai_remote_addr,
+ sizeof(struct ipaddr));
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY:
+ memcpy(&hop->nai.local_addr, &segment->nai_local_addr,
+ sizeof(struct ipaddr));
+ hop->nai.local_iface = segment->nai_local_iface;
+ memcpy(&hop->nai.remote_addr, &segment->nai_remote_addr,
+ sizeof(struct ipaddr));
+ hop->nai.remote_iface = segment->nai_remote_iface;
+ break;
+ default:
+ break;
+ }
+ last_hop = hop;
+ }
+ return hop;
+}
+
+int path_pcep_config_update_path(struct path *path)
+{
+ assert(path != NULL);
+ assert(path->nbkey.preference != 0);
+ assert(path->nbkey.endpoint.ipa_type == IPADDR_V4);
+
+ struct path_hop *hop;
+ struct path_metric *metric;
+ int index;
+ char segment_list_name_buff[64 + 1 + 64 + 1 + 11 + 1];
+ char *segment_list_name = NULL;
+ struct srte_candidate *candidate;
+ struct srte_segment_list *segment_list = NULL;
+ struct srte_segment_entry *segment;
+
+ candidate = lookup_candidate(&path->nbkey);
+
+ // if there is no candidate to update we are done
+ if (!candidate)
+ return 0;
+
+ // first clean up old segment list if present
+ if (candidate->lsp->segment_list) {
+ SET_FLAG(candidate->lsp->segment_list->flags,
+ F_SEGMENT_LIST_DELETED);
+ candidate->lsp->segment_list = NULL;
+ }
+
+ if (path->first_hop != NULL) {
+ snprintf(segment_list_name_buff, sizeof(segment_list_name_buff),
+ "%s-%u", path->name, path->plsp_id);
+ segment_list_name = segment_list_name_buff;
+
+ segment_list = srte_segment_list_add(segment_list_name);
+ segment_list->protocol_origin = path->update_origin;
+ strlcpy(segment_list->originator, path->originator,
+ sizeof(segment_list->originator));
+ SET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW);
+ SET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
+
+ for (hop = path->first_hop, index = 10; hop != NULL;
+ hop = hop->next, index += 10) {
+ assert(hop->has_sid);
+ assert(hop->is_mpls);
+
+ segment = srte_segment_entry_add(segment_list, index);
+
+ segment->sid_value = (mpls_label_t)hop->sid.mpls.label;
+ SET_FLAG(segment->segment_list->flags,
+ F_SEGMENT_LIST_MODIFIED);
+
+ if (hop->has_nai)
+ srte_segment_entry_set_nai(
+ segment, srte_nai_type(hop->nai.type),
+ &hop->nai.local_addr,
+ hop->nai.local_iface,
+ &hop->nai.remote_addr,
+ hop->nai.remote_iface);
+ }
+ }
+
+ candidate->lsp->segment_list = segment_list;
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+
+ for (metric = path->first_metric; metric != NULL; metric = metric->next)
+ srte_lsp_set_metric(candidate->lsp, metric->type, metric->value,
+ metric->enforce, metric->is_bound,
+ metric->is_computed);
+
+ if (path->has_bandwidth)
+ srte_lsp_set_bandwidth(candidate->lsp, path->bandwidth,
+ path->enforce_bandwidth);
+
+ if (path->has_pce_objfun) {
+ SET_FLAG(candidate->lsp->flags, F_CANDIDATE_HAS_OBJFUN);
+ candidate->lsp->objfun = path->pce_objfun;
+ }
+
+ srte_apply_changes();
+
+ return 0;
+}
+
+struct srte_candidate *lookup_candidate(struct lsp_nb_key *key)
+{
+ struct srte_policy *policy = NULL;
+ policy = srte_policy_find(key->color, &key->endpoint);
+ if (policy == NULL)
+ return NULL;
+ return srte_candidate_find(policy, key->preference);
+}
+
+char *candidate_name(struct srte_candidate *candidate)
+{
+ return asprintfrr(MTYPE_PCEP, "%s-%s", candidate->policy->name,
+ candidate->name);
+}
+
+enum pcep_lsp_operational_status
+status_int_to_ext(enum srte_policy_status status)
+{
+ switch (status) {
+ case SRTE_POLICY_STATUS_UP:
+ return PCEP_LSP_OPERATIONAL_ACTIVE;
+ case SRTE_POLICY_STATUS_GOING_UP:
+ return PCEP_LSP_OPERATIONAL_GOING_UP;
+ case SRTE_POLICY_STATUS_GOING_DOWN:
+ return PCEP_LSP_OPERATIONAL_GOING_DOWN;
+ default:
+ return PCEP_LSP_OPERATIONAL_DOWN;
+ }
+}
+
+enum pcep_sr_subobj_nai pcep_nai_type(enum srte_segment_nai_type type)
+{
+ switch (type) {
+ case SRTE_SEGMENT_NAI_TYPE_NONE:
+ return PCEP_SR_SUBOBJ_NAI_ABSENT;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
+ return PCEP_SR_SUBOBJ_NAI_IPV4_NODE;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
+ return PCEP_SR_SUBOBJ_NAI_IPV6_NODE;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY:
+ return PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY:
+ return PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY:
+ return PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY;
+ default:
+ return PCEP_SR_SUBOBJ_NAI_UNKNOWN;
+ }
+}
+
+enum srte_segment_nai_type srte_nai_type(enum pcep_sr_subobj_nai type)
+{
+ switch (type) {
+ case PCEP_SR_SUBOBJ_NAI_ABSENT:
+ return SRTE_SEGMENT_NAI_TYPE_NONE;
+ case PCEP_SR_SUBOBJ_NAI_IPV4_NODE:
+ return SRTE_SEGMENT_NAI_TYPE_IPV4_NODE;
+ case PCEP_SR_SUBOBJ_NAI_IPV6_NODE:
+ return SRTE_SEGMENT_NAI_TYPE_IPV6_NODE;
+ case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY:
+ return SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY;
+ case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY:
+ return SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY;
+ case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY:
+ return SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY;
+ default:
+ return SRTE_SEGMENT_NAI_TYPE_NONE;
+ }
+}
diff --git a/pathd/path_pcep_config.h b/pathd/path_pcep_config.h
new file mode 100644
index 0000000000..de29ab29c1
--- /dev/null
+++ b/pathd/path_pcep_config.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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_CONFIG_H_
+#define _PATH_PCEP_CONFIG_H_
+
+#include <stdbool.h>
+#include <debug.h>
+
+#include "pathd/path_pcep.h"
+
+#define PATH_NB_NO_CHANGE 0
+#define PATH_NB_OK 1
+#define PATH_NB_ERR -1
+
+typedef int (*path_list_cb_t)(struct path *path, void *arg);
+
+/* Lookup the candidate path and fill up the missing path attributes like name
+ and type. Used for path generated from PCEP message received from the PCE
+ so they contains more information about the candidate path. If no matching
+ policy or candidate path is found, nothing is changed */
+void path_pcep_config_lookup(struct path *path);
+struct path *path_pcep_config_get_path(struct lsp_nb_key *key);
+void path_pcep_config_list_path(path_list_cb_t cb, void *arg);
+int path_pcep_config_update_path(struct path *path);
+struct path *candidate_to_path(struct srte_candidate *candidate);
+
+
+#endif // _PATH_PCEP_CONFIG_H_
diff --git a/pathd/path_pcep_controller.c b/pathd/path_pcep_controller.c
new file mode 100644
index 0000000000..f4871a4d8d
--- /dev/null
+++ b/pathd/path_pcep_controller.c
@@ -0,0 +1,1078 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "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 "network.h"
+
+#include "pathd/pathd.h"
+#include "pathd/path_errors.h"
+#include "pathd/path_pcep.h"
+#include "pathd/path_pcep_controller.h"
+#include "pathd/path_pcep_pcc.h"
+#include "pathd/path_pcep_config.h"
+#include "pathd/path_pcep_debug.h"
+
+#define MAX_RECONNECT_DELAY 120
+
+#define min(a, b) \
+ ({ \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ _a <= _b ? _a : _b; \
+ })
+
+
+/* Event handling data structures */
+enum pcep_ctrl_event_type {
+ EV_UPDATE_PCC_OPTS = 1,
+ EV_UPDATE_PCE_OPTS,
+ EV_REMOVE_PCC,
+ EV_PATHD_EVENT,
+ EV_SYNC_PATH,
+ EV_SYNC_DONE,
+ EV_PCEPLIB_EVENT,
+ EV_RESET_PCC_SESSION
+};
+
+struct pcep_ctrl_event_data {
+ struct ctrl_state *ctrl_state;
+ enum pcep_ctrl_event_type type;
+ uint32_t sub_type;
+ int pcc_id;
+ void *payload;
+};
+
+struct pcep_main_event_data {
+ pcep_main_event_handler_t handler;
+ int pcc_id;
+ enum pcep_main_event_type type;
+ void *payload;
+};
+
+/* Synchronous call arguments */
+
+struct get_counters_args {
+ struct ctrl_state *ctrl_state;
+ int pcc_id;
+ struct counters_group *counters;
+};
+
+struct send_report_args {
+ struct ctrl_state *ctrl_state;
+ int pcc_id;
+ struct path *path;
+};
+
+struct get_pcep_session_args {
+ struct ctrl_state *ctrl_state;
+ int pcc_id;
+ pcep_session *pcep_session;
+};
+
+/* Internal Functions Called From Main Thread */
+static int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res);
+
+/* Internal Functions Called From Controller Thread */
+static int pcep_thread_finish_event_handler(struct thread *thread);
+static int pcep_thread_get_counters_callback(struct thread *t);
+static int pcep_thread_send_report_callback(struct thread *t);
+static int pcep_thread_get_pcep_session_callback(struct thread *t);
+static int pcep_thread_get_pcc_info_callback(struct thread *t);
+
+/* Controller Thread Timer Handler */
+static int schedule_thread_timer(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_timer_type timer_type,
+ enum pcep_ctrl_timeout_type timeout_type,
+ uint32_t delay, void *payload,
+ struct thread **thread);
+static int schedule_thread_timer_with_cb(
+ struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_timer_type timer_type,
+ enum pcep_ctrl_timeout_type timeout_type, uint32_t delay, void *payload,
+ struct thread **thread, pcep_ctrl_thread_callback timer_cb);
+static int pcep_thread_timer_handler(struct thread *thread);
+
+/* Controller Thread Socket read/write Handler */
+static int schedule_thread_socket(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_socket_type type, bool is_read,
+ void *payload, int fd, struct thread **thread,
+ pcep_ctrl_thread_callback cb);
+
+/* Controller Thread Event Handler */
+static int send_to_thread(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_event_type type, uint32_t sub_type,
+ void *payload);
+static int send_to_thread_with_cb(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_event_type type,
+ uint32_t sub_type, void *payload,
+ pcep_ctrl_thread_callback event_cb);
+static int pcep_thread_event_handler(struct thread *thread);
+static int pcep_thread_event_update_pcc_options(struct ctrl_state *ctrl_state,
+ struct pcc_opts *opts);
+static int pcep_thread_event_update_pce_options(struct ctrl_state *ctrl_state,
+ int pcc_id,
+ struct pce_opts *opts);
+static int pcep_thread_event_remove_pcc_by_id(struct ctrl_state *ctrl_state,
+ int pcc_id);
+static int pcep_thread_event_remove_pcc_all(struct ctrl_state *ctrl_state);
+static int pcep_thread_event_remove_pcc(struct ctrl_state *ctrl_state,
+ struct pce_opts *pce_opts);
+static int pcep_thread_event_sync_path(struct ctrl_state *ctrl_state,
+ int pcc_id, struct path *path);
+static int pcep_thread_event_sync_done(struct ctrl_state *ctrl_state,
+ int pcc_id);
+static int pcep_thread_event_pathd_event(struct ctrl_state *ctrl_state,
+ enum pcep_pathd_event_type type,
+ struct path *path);
+
+/* Main Thread Event Handler */
+static int send_to_main(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_main_event_type type, void *payload);
+static int pcep_main_event_handler(struct thread *thread);
+
+/* Helper functions */
+static void set_ctrl_state(struct frr_pthread *fpt,
+ struct ctrl_state *ctrl_state);
+static struct ctrl_state *get_ctrl_state(struct frr_pthread *fpt);
+int get_next_id(struct ctrl_state *ctrl_state);
+int set_pcc_state(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state);
+void remove_pcc_state(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state);
+static uint32_t backoff_delay(uint32_t max, uint32_t base, uint32_t attempt);
+static const char *timer_type_name(enum pcep_ctrl_timer_type type);
+static const char *timeout_type_name(enum pcep_ctrl_timeout_type type);
+
+
+/* ------------ API Functions Called from Main Thread ------------ */
+
+int pcep_ctrl_initialize(struct thread_master *main_thread,
+ struct frr_pthread **fpt,
+ pcep_main_event_handler_t event_handler)
+{
+ assert(fpt != NULL);
+
+ int ret = 0;
+ struct ctrl_state *ctrl_state;
+ struct frr_pthread_attr attr = {
+ .start = frr_pthread_attr_default.start,
+ .stop = pcep_ctrl_halt_cb,
+ };
+
+ PCEP_DEBUG("Initializing pcep module controller");
+
+ /* Create and start the FRR pthread */
+ *fpt = frr_pthread_new(&attr, "PCEP thread", "pcep_controller");
+ if (*fpt == NULL) {
+ flog_err(EC_PATH_SYSTEM_CALL,
+ "failed to initialize PCEP thread");
+ return 1;
+ }
+ ret = frr_pthread_run(*fpt, NULL);
+ if (ret < 0) {
+ flog_err(EC_PATH_SYSTEM_CALL, "failed to create PCEP thread");
+ return ret;
+ }
+ frr_pthread_wait_running(*fpt);
+
+ /* Initialize the thread state */
+ ctrl_state = XCALLOC(MTYPE_PCEP, sizeof(*ctrl_state));
+ ctrl_state->main = main_thread;
+ ctrl_state->self = (*fpt)->master;
+ ctrl_state->main_event_handler = event_handler;
+ ctrl_state->pcc_count = 0;
+ ctrl_state->pcc_last_id = 0;
+ ctrl_state->pcc_opts =
+ XCALLOC(MTYPE_PCEP, sizeof(*ctrl_state->pcc_opts));
+ /* Default to no PCC address defined */
+ ctrl_state->pcc_opts->addr.ipa_type = IPADDR_NONE;
+ ctrl_state->pcc_opts->port = PCEP_DEFAULT_PORT;
+
+ /* Keep the state reference for events */
+ set_ctrl_state(*fpt, ctrl_state);
+
+ return ret;
+}
+
+int pcep_ctrl_finalize(struct frr_pthread **fpt)
+{
+ assert(fpt != NULL);
+
+ int ret = 0;
+
+ PCEP_DEBUG("Finalizing pcep module controller");
+
+ if (*fpt != NULL) {
+ frr_pthread_stop(*fpt, NULL);
+ *fpt = NULL;
+ }
+
+ return ret;
+}
+
+int pcep_ctrl_update_pcc_options(struct frr_pthread *fpt, struct pcc_opts *opts)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, 0, EV_UPDATE_PCC_OPTS, 0, opts);
+}
+
+int pcep_ctrl_update_pce_options(struct frr_pthread *fpt, struct pce_opts *opts)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, 0, EV_UPDATE_PCE_OPTS, 0, opts);
+}
+
+int pcep_ctrl_remove_pcc(struct frr_pthread *fpt, struct pce_opts *pce_opts)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, 0, EV_REMOVE_PCC, 0, pce_opts);
+}
+
+int pcep_ctrl_reset_pcc_session(struct frr_pthread *fpt, char *pce_name)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, 0, EV_RESET_PCC_SESSION, 0, pce_name);
+}
+
+int pcep_ctrl_pathd_event(struct frr_pthread *fpt,
+ enum pcep_pathd_event_type type, struct path *path)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, 0, EV_PATHD_EVENT, type, path);
+}
+
+int pcep_ctrl_sync_path(struct frr_pthread *fpt, int pcc_id, struct path *path)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, pcc_id, EV_SYNC_PATH, 0, path);
+}
+
+int pcep_ctrl_sync_done(struct frr_pthread *fpt, int pcc_id)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, pcc_id, EV_SYNC_DONE, 0, NULL);
+}
+
+struct counters_group *pcep_ctrl_get_counters(struct frr_pthread *fpt,
+ int pcc_id)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ struct get_counters_args args = {
+ .ctrl_state = ctrl_state, .pcc_id = pcc_id, .counters = NULL};
+ thread_execute(ctrl_state->self, pcep_thread_get_counters_callback,
+ &args, 0);
+ return args.counters;
+}
+
+pcep_session *pcep_ctrl_get_pcep_session(struct frr_pthread *fpt, int pcc_id)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ struct get_pcep_session_args args = {.ctrl_state = ctrl_state,
+ .pcc_id = pcc_id,
+ .pcep_session = NULL};
+ thread_execute(ctrl_state->self, pcep_thread_get_pcep_session_callback,
+ &args, 0);
+ return args.pcep_session;
+}
+
+struct pcep_pcc_info *pcep_ctrl_get_pcc_info(struct frr_pthread *fpt,
+ const char *pce_name)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ struct pcep_pcc_info *args = XCALLOC(MTYPE_PCEP, sizeof(*args));
+ args->ctrl_state = ctrl_state;
+ strncpy(args->pce_name, pce_name, sizeof(args->pce_name));
+ thread_execute(ctrl_state->self, pcep_thread_get_pcc_info_callback,
+ args, 0);
+
+ return args;
+}
+
+void pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
+ struct path *path)
+{
+ /* Sends a report stynchronously */
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ struct send_report_args args = {
+ .ctrl_state = ctrl_state, .pcc_id = pcc_id, .path = path};
+ thread_execute(ctrl_state->self, pcep_thread_send_report_callback,
+ &args, 0);
+}
+
+/* ------------ Internal Functions Called from Main Thread ------------ */
+
+int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res)
+{
+ thread_add_event(fpt->master, pcep_thread_finish_event_handler,
+ (void *)fpt, 0, NULL);
+ pthread_join(fpt->thread, res);
+
+ return 0;
+}
+
+
+/* ------------ API Functions Called From Controller Thread ------------ */
+
+void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id)
+{
+ send_to_main(ctrl_state, pcc_id, PCEP_MAIN_EVENT_START_SYNC, NULL);
+}
+
+void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path)
+{
+ send_to_main(ctrl_state, pcc_id, PCEP_MAIN_EVENT_UPDATE_CANDIDATE,
+ path);
+}
+
+void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state)
+{
+ if (!pcc_state)
+ return;
+ /* Will be deleted when the event is handled */
+ char *originator = XSTRDUP(MTYPE_PCEP, pcc_state->originator);
+ PCEP_DEBUG("schedule candidate path segments removal for originator %s",
+ originator);
+ send_to_main(ctrl_state, pcep_pcc_get_pcc_id(pcc_state),
+ PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP, originator);
+}
+
+void pcep_thread_schedule_sync_best_pce(struct ctrl_state *ctrl_state,
+ int pcc_id, int delay,
+ struct thread **thread)
+{
+
+ schedule_thread_timer(ctrl_state, pcc_id, TM_CALCULATE_BEST_PCE,
+ TO_UNDEFINED, delay, NULL, thread);
+}
+
+void pcep_thread_cancel_timer(struct thread **thread)
+{
+ if (thread == NULL || *thread == NULL) {
+ return;
+ }
+
+ struct pcep_ctrl_timer_data *data = THREAD_ARG(*thread);
+ PCEP_DEBUG("Timer %s / %s canceled", timer_type_name(data->timer_type),
+ timeout_type_name(data->timeout_type));
+ if (data != NULL) {
+ XFREE(MTYPE_PCEP, data);
+ }
+
+ if ((*thread)->master->owner == pthread_self()) {
+ thread_cancel(thread);
+ } else {
+ thread_cancel_async((*thread)->master, thread, NULL);
+ }
+}
+
+void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id,
+ int retry_count, struct thread **thread)
+{
+ uint32_t delay = backoff_delay(MAX_RECONNECT_DELAY, 1, retry_count);
+ PCEP_DEBUG("Schedule RECONNECT_PCC for %us (retry %u)", delay,
+ retry_count);
+ schedule_thread_timer(ctrl_state, pcc_id, TM_RECONNECT_PCC,
+ TO_UNDEFINED, delay, NULL, thread);
+}
+
+void pcep_thread_schedule_timeout(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_timeout_type timeout_type,
+ uint32_t delay, void *param,
+ struct thread **thread)
+{
+ assert(timeout_type > TO_UNDEFINED);
+ assert(timeout_type < TO_MAX);
+ PCEP_DEBUG("Schedule timeout %s for %us",
+ timeout_type_name(timeout_type), delay);
+ schedule_thread_timer(ctrl_state, pcc_id, TM_TIMEOUT, timeout_type,
+ delay, param, thread);
+}
+
+void pcep_thread_schedule_pceplib_timer(struct ctrl_state *ctrl_state,
+ int delay, void *payload,
+ struct thread **thread,
+ pcep_ctrl_thread_callback timer_cb)
+{
+ PCEP_DEBUG("Schedule PCEPLIB_TIMER for %us", delay);
+ schedule_thread_timer_with_cb(ctrl_state, 0, TM_PCEPLIB_TIMER,
+ TO_UNDEFINED, delay, payload, thread,
+ timer_cb);
+}
+
+void pcep_thread_schedule_session_timeout(struct ctrl_state *ctrl_state,
+ int pcc_id, int delay,
+ struct thread **thread)
+{
+ PCEP_DEBUG("Schedule session_timeout interval for %us", delay);
+ schedule_thread_timer(ctrl_state, pcc_id, TM_SESSION_TIMEOUT_PCC,
+ TO_UNDEFINED, delay, NULL, thread);
+}
+
+int pcep_thread_pcc_count(struct ctrl_state *ctrl_state)
+{
+ if (ctrl_state == NULL) {
+ return 0;
+ }
+
+ return ctrl_state->pcc_count;
+}
+
+/* ------------ Internal Functions Called From Controller Thread ------------ */
+
+int pcep_thread_finish_event_handler(struct thread *thread)
+{
+ int i;
+ struct frr_pthread *fpt = THREAD_ARG(thread);
+ struct ctrl_state *ctrl_state = fpt->data;
+
+ assert(ctrl_state != NULL);
+
+ for (i = 0; i < MAX_PCC; i++) {
+ if (ctrl_state->pcc[i]) {
+ pcep_pcc_finalize(ctrl_state, ctrl_state->pcc[i]);
+ ctrl_state->pcc[i] = NULL;
+ }
+ }
+
+ XFREE(MTYPE_PCEP, ctrl_state->pcc_opts);
+ XFREE(MTYPE_PCEP, ctrl_state);
+ fpt->data = NULL;
+
+ atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
+ return 0;
+}
+
+int pcep_thread_get_counters_callback(struct thread *t)
+{
+ struct get_counters_args *args = THREAD_ARG(t);
+ assert(args != NULL);
+ struct ctrl_state *ctrl_state = args->ctrl_state;
+ assert(ctrl_state != NULL);
+ struct pcc_state *pcc_state;
+
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id);
+ if (pcc_state) {
+ args->counters = pcep_lib_copy_counters(pcc_state->sess);
+ } else {
+ args->counters = NULL;
+ }
+
+ return 0;
+}
+
+int pcep_thread_send_report_callback(struct thread *t)
+{
+ struct send_report_args *args = THREAD_ARG(t);
+ assert(args != NULL);
+ struct ctrl_state *ctrl_state = args->ctrl_state;
+ assert(ctrl_state != NULL);
+ struct pcc_state *pcc_state;
+
+ if (args->pcc_id == 0) {
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (ctrl_state->pcc[i]) {
+ pcep_pcc_send_report(ctrl_state,
+ ctrl_state->pcc[i],
+ args->path);
+ }
+ }
+ } else {
+ pcc_state =
+ pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id);
+ pcep_pcc_send_report(ctrl_state, pcc_state, args->path);
+ }
+
+ return 0;
+}
+
+int pcep_thread_get_pcep_session_callback(struct thread *t)
+{
+ struct get_pcep_session_args *args = THREAD_ARG(t);
+ assert(args != NULL);
+ struct ctrl_state *ctrl_state = args->ctrl_state;
+ assert(ctrl_state != NULL);
+ struct pcc_state *pcc_state;
+
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, args->pcc_id);
+ if (pcc_state) {
+ args->pcep_session =
+ pcep_lib_copy_pcep_session(pcc_state->sess);
+ }
+
+ return 0;
+}
+
+int pcep_thread_get_pcc_info_callback(struct thread *t)
+{
+ struct pcep_pcc_info *args = THREAD_ARG(t);
+ assert(args != NULL);
+ struct ctrl_state *ctrl_state = args->ctrl_state;
+ assert(ctrl_state != NULL);
+
+ pcep_pcc_copy_pcc_info(ctrl_state->pcc, args);
+
+ return 0;
+}
+
+/* ------------ Controller Thread Timer Handler ------------ */
+
+int schedule_thread_timer_with_cb(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_timer_type timer_type,
+ enum pcep_ctrl_timeout_type timeout_type,
+ uint32_t delay, void *payload,
+ struct thread **thread,
+ pcep_ctrl_thread_callback timer_cb)
+{
+ assert(thread != NULL);
+
+ struct pcep_ctrl_timer_data *data;
+
+ data = XCALLOC(MTYPE_PCEP, sizeof(*data));
+ data->ctrl_state = ctrl_state;
+ data->timer_type = timer_type;
+ data->timeout_type = timeout_type;
+ data->pcc_id = pcc_id;
+ data->payload = payload;
+
+ thread_add_timer(ctrl_state->self, timer_cb, (void *)data, delay,
+ thread);
+
+ return 0;
+}
+
+int schedule_thread_timer(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_timer_type timer_type,
+ enum pcep_ctrl_timeout_type timeout_type,
+ uint32_t delay, void *payload, struct thread **thread)
+{
+ return schedule_thread_timer_with_cb(ctrl_state, pcc_id, timer_type,
+ timeout_type, delay, payload,
+ thread, pcep_thread_timer_handler);
+}
+
+int pcep_thread_timer_handler(struct thread *thread)
+{
+ /* data unpacking */
+ struct pcep_ctrl_timer_data *data = THREAD_ARG(thread);
+ assert(data != NULL);
+ struct ctrl_state *ctrl_state = data->ctrl_state;
+ assert(ctrl_state != NULL);
+ enum pcep_ctrl_timer_type timer_type = data->timer_type;
+ enum pcep_ctrl_timeout_type timeout_type = data->timeout_type;
+ int pcc_id = data->pcc_id;
+ void *param = data->payload;
+ XFREE(MTYPE_PCEP, data);
+
+ int ret = 0;
+ struct pcc_state *pcc_state = NULL;
+
+ switch (timer_type) {
+ case TM_RECONNECT_PCC:
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ if (!pcc_state)
+ return ret;
+ pcep_pcc_reconnect(ctrl_state, pcc_state);
+ break;
+ case TM_TIMEOUT:
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ if (!pcc_state)
+ return ret;
+ pcep_pcc_timeout_handler(ctrl_state, pcc_state, timeout_type,
+ param);
+ break;
+ case TM_CALCULATE_BEST_PCE:
+ /* Previous best disconnect so new best should be synced */
+ ret = pcep_pcc_timer_update_best_pce(ctrl_state, pcc_id);
+ break;
+ case TM_SESSION_TIMEOUT_PCC:
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_thread_remove_candidate_path_segments(ctrl_state,
+ pcc_state);
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ "Unknown controller timer triggered: %u", timer_type);
+ break;
+ }
+
+ return ret;
+}
+
+int pcep_thread_pcep_event(struct thread *thread)
+{
+ struct pcep_ctrl_event_data *data = THREAD_ARG(thread);
+ assert(data != NULL);
+ struct ctrl_state *ctrl_state = data->ctrl_state;
+ pcep_event *event = data->payload;
+ XFREE(MTYPE_PCEP, data);
+ int i;
+
+ for (i = 0; i < MAX_PCC; i++) {
+ if (ctrl_state->pcc[i]) {
+ struct pcc_state *pcc_state = ctrl_state->pcc[i];
+ if (pcc_state->sess != event->session)
+ continue;
+ pcep_pcc_pcep_event_handler(ctrl_state, pcc_state,
+ event);
+ break;
+ }
+ }
+ destroy_pcep_event(event);
+
+ return 0;
+}
+
+/* ------------ Controller Thread Socket Functions ------------ */
+
+int schedule_thread_socket(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_socket_type type, bool is_read,
+ void *payload, int fd, struct thread **thread,
+ pcep_ctrl_thread_callback socket_cb)
+{
+ assert(thread != NULL);
+
+ struct pcep_ctrl_socket_data *data;
+
+ data = XCALLOC(MTYPE_PCEP, sizeof(*data));
+ data->ctrl_state = ctrl_state;
+ data->type = type;
+ data->is_read = is_read;
+ data->fd = fd;
+ data->pcc_id = pcc_id;
+ data->payload = payload;
+
+ if (is_read) {
+ thread_add_read(ctrl_state->self, socket_cb, (void *)data, fd,
+ thread);
+ } else {
+ thread_add_write(ctrl_state->self, socket_cb, (void *)data, fd,
+ thread);
+ }
+
+ return 0;
+}
+
+int pcep_thread_socket_write(void *fpt, void **thread, int fd, void *payload,
+ pcep_ctrl_thread_callback socket_cb)
+{
+ struct ctrl_state *ctrl_state = ((struct frr_pthread *)fpt)->data;
+
+ return schedule_thread_socket(ctrl_state, 0, SOCK_PCEPLIB, false,
+ payload, fd, (struct thread **)thread,
+ socket_cb);
+}
+
+int pcep_thread_socket_read(void *fpt, void **thread, int fd, void *payload,
+ pcep_ctrl_thread_callback socket_cb)
+{
+ struct ctrl_state *ctrl_state = ((struct frr_pthread *)fpt)->data;
+
+ return schedule_thread_socket(ctrl_state, 0, SOCK_PCEPLIB, true,
+ payload, fd, (struct thread **)thread,
+ socket_cb);
+}
+
+int pcep_thread_send_ctrl_event(void *fpt, void *payload,
+ pcep_ctrl_thread_callback cb)
+{
+ struct ctrl_state *ctrl_state = ((struct frr_pthread *)fpt)->data;
+
+ return send_to_thread_with_cb(ctrl_state, 0, EV_PCEPLIB_EVENT, 0,
+ payload, cb);
+}
+
+/* ------------ Controller Thread Event Handler ------------ */
+
+int send_to_thread(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_event_type type, uint32_t sub_type,
+ void *payload)
+{
+ return send_to_thread_with_cb(ctrl_state, pcc_id, type, sub_type,
+ payload, pcep_thread_event_handler);
+}
+
+int send_to_thread_with_cb(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_event_type type, uint32_t sub_type,
+ void *payload, pcep_ctrl_thread_callback event_cb)
+{
+ struct pcep_ctrl_event_data *data;
+
+ data = XCALLOC(MTYPE_PCEP, sizeof(*data));
+ data->ctrl_state = ctrl_state;
+ data->type = type;
+ data->sub_type = sub_type;
+ data->pcc_id = pcc_id;
+ data->payload = payload;
+
+ thread_add_event(ctrl_state->self, event_cb, (void *)data, 0, NULL);
+
+ return 0;
+}
+
+int pcep_thread_event_handler(struct thread *thread)
+{
+ /* data unpacking */
+ struct pcep_ctrl_event_data *data = THREAD_ARG(thread);
+ assert(data != NULL);
+ struct ctrl_state *ctrl_state = data->ctrl_state;
+ assert(ctrl_state != NULL);
+ enum pcep_ctrl_event_type type = data->type;
+ uint32_t sub_type = data->sub_type;
+ int pcc_id = data->pcc_id;
+ void *payload = data->payload;
+ XFREE(MTYPE_PCEP, data);
+
+ int ret = 0;
+
+ /* Possible sub-type values */
+ enum pcep_pathd_event_type path_event_type = PCEP_PATH_UNDEFINED;
+
+ /* Possible payload values */
+ struct path *path = NULL;
+ struct pcc_opts *pcc_opts = NULL;
+ struct pce_opts *pce_opts = NULL;
+ struct pcc_state *pcc_state = NULL;
+
+ switch (type) {
+ case EV_UPDATE_PCC_OPTS:
+ assert(payload != NULL);
+ pcc_opts = (struct pcc_opts *)payload;
+ ret = pcep_thread_event_update_pcc_options(ctrl_state,
+ pcc_opts);
+ break;
+ case EV_UPDATE_PCE_OPTS:
+ assert(payload != NULL);
+ pce_opts = (struct pce_opts *)payload;
+ ret = pcep_thread_event_update_pce_options(ctrl_state, pcc_id,
+ pce_opts);
+ break;
+ case EV_REMOVE_PCC:
+ pce_opts = (struct pce_opts *)payload;
+ ret = pcep_thread_event_remove_pcc(ctrl_state, pce_opts);
+ if (ret == 0) {
+ ret = pcep_pcc_multi_pce_remove_pcc(ctrl_state,
+ ctrl_state->pcc);
+ }
+ break;
+ case EV_PATHD_EVENT:
+ assert(payload != NULL);
+ path_event_type = (enum pcep_pathd_event_type)sub_type;
+ path = (struct path *)payload;
+ ret = pcep_thread_event_pathd_event(ctrl_state, path_event_type,
+ path);
+ break;
+ case EV_SYNC_PATH:
+ assert(payload != NULL);
+ path = (struct path *)payload;
+ pcep_pcc_multi_pce_sync_path(ctrl_state, pcc_id,
+ ctrl_state->pcc);
+ pcep_thread_event_sync_path(ctrl_state, pcc_id, path);
+ break;
+ case EV_SYNC_DONE:
+ ret = pcep_thread_event_sync_done(ctrl_state, pcc_id);
+ break;
+ case EV_RESET_PCC_SESSION:
+ pcc_state = pcep_pcc_get_pcc_by_name(ctrl_state->pcc,
+ (const char *)payload);
+ if (pcc_state) {
+ pcep_pcc_disable(ctrl_state, pcc_state);
+ ret = pcep_pcc_enable(ctrl_state, pcc_state);
+ } else {
+ flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ "Cannot reset state for PCE: %s",
+ (const char *)payload);
+ }
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ "Unexpected event received in controller thread: %u",
+ type);
+ break;
+ }
+
+ return ret;
+}
+
+int pcep_thread_event_update_pcc_options(struct ctrl_state *ctrl_state,
+ struct pcc_opts *opts)
+{
+ assert(opts != NULL);
+ if (ctrl_state->pcc_opts != NULL) {
+ XFREE(MTYPE_PCEP, ctrl_state->pcc_opts);
+ }
+ ctrl_state->pcc_opts = opts;
+ return 0;
+}
+
+int pcep_thread_event_update_pce_options(struct ctrl_state *ctrl_state,
+ int pcc_id, struct pce_opts *pce_opts)
+{
+ if (!pce_opts || !ctrl_state) {
+ return 0;
+ }
+ struct pcc_state *pcc_state;
+ struct pcc_opts *pcc_opts;
+
+ int current_pcc_id =
+ pcep_pcc_get_pcc_id_by_ip_port(ctrl_state->pcc, pce_opts);
+ if (current_pcc_id) {
+ pcc_state =
+ pcep_pcc_get_pcc_by_id(ctrl_state->pcc, current_pcc_id);
+ } else {
+ pcc_state = pcep_pcc_initialize(ctrl_state,
+ get_next_id(ctrl_state));
+ if (set_pcc_state(ctrl_state, pcc_state)) {
+ XFREE(MTYPE_PCEP, pcc_state);
+ return 0;
+ }
+ }
+
+ /* Copy the pcc options to delegate it to the update function */
+ pcc_opts = XCALLOC(MTYPE_PCEP, sizeof(*pcc_opts));
+ memcpy(pcc_opts, ctrl_state->pcc_opts, sizeof(*pcc_opts));
+
+ if (pcep_pcc_update(ctrl_state, pcc_state, pcc_opts, pce_opts)) {
+ flog_err(EC_PATH_PCEP_PCC_CONF_UPDATE,
+ "failed to update PCC configuration");
+ }
+
+
+ return 0;
+}
+
+int pcep_thread_event_remove_pcc_by_id(struct ctrl_state *ctrl_state,
+ int pcc_id)
+{
+ if (pcc_id) {
+ struct pcc_state *pcc_state =
+ pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ if (pcc_state) {
+ remove_pcc_state(ctrl_state, pcc_state);
+ pcep_pcc_finalize(ctrl_state, pcc_state);
+ }
+ }
+ return 0;
+}
+
+int pcep_thread_event_remove_pcc_all(struct ctrl_state *ctrl_state)
+{
+ assert(ctrl_state != NULL);
+
+ for (int i = 0; i < MAX_PCC; i++) {
+ pcep_thread_event_remove_pcc_by_id(
+ ctrl_state,
+ pcep_pcc_get_pcc_id_by_idx(ctrl_state->pcc, i));
+ }
+ return 0;
+}
+
+int pcep_thread_event_remove_pcc(struct ctrl_state *ctrl_state,
+ struct pce_opts *pce_opts)
+{
+ assert(ctrl_state != NULL);
+
+ if (pce_opts) {
+ int pcc_id = pcep_pcc_get_pcc_id_by_ip_port(ctrl_state->pcc,
+ pce_opts);
+ if (pcc_id) {
+ pcep_thread_event_remove_pcc_by_id(ctrl_state, pcc_id);
+ } else {
+ return -1;
+ }
+ XFREE(MTYPE_PCEP, pce_opts);
+ } else {
+ pcep_thread_event_remove_pcc_all(ctrl_state);
+ }
+
+ return 0;
+}
+
+int pcep_thread_event_sync_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path)
+{
+ struct pcc_state *pcc_state =
+ pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_pcc_sync_path(ctrl_state, pcc_state, path);
+ pcep_free_path(path);
+ return 0;
+}
+
+int pcep_thread_event_sync_done(struct ctrl_state *ctrl_state, int pcc_id)
+{
+ struct pcc_state *pcc_state =
+ pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_pcc_sync_done(ctrl_state, pcc_state);
+ return 0;
+}
+
+int pcep_thread_event_pathd_event(struct ctrl_state *ctrl_state,
+ enum pcep_pathd_event_type type,
+ struct path *path)
+{
+ int i;
+
+ for (i = 0; i < MAX_PCC; i++) {
+ if (ctrl_state->pcc[i]) {
+ struct pcc_state *pcc_state = ctrl_state->pcc[i];
+ pcep_pcc_pathd_event_handler(ctrl_state, pcc_state,
+ type, path);
+ }
+ }
+
+ pcep_free_path(path);
+
+ return 0;
+}
+
+
+/* ------------ Main Thread Event Handler ------------ */
+
+int send_to_main(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_main_event_type type, void *payload)
+{
+ struct pcep_main_event_data *data;
+
+ data = XCALLOC(MTYPE_PCEP, sizeof(*data));
+ data->handler = ctrl_state->main_event_handler;
+ data->type = type;
+ data->pcc_id = pcc_id;
+ data->payload = payload;
+
+ thread_add_event(ctrl_state->main, pcep_main_event_handler,
+ (void *)data, 0, NULL);
+ return 0;
+}
+
+int pcep_main_event_handler(struct thread *thread)
+{
+ /* data unpacking */
+ struct pcep_main_event_data *data = THREAD_ARG(thread);
+ assert(data != NULL);
+ pcep_main_event_handler_t handler = data->handler;
+ enum pcep_main_event_type type = data->type;
+ int pcc_id = data->pcc_id;
+ void *payload = data->payload;
+ XFREE(MTYPE_PCEP, data);
+
+ return handler(type, pcc_id, payload);
+}
+
+
+/* ------------ Helper functions ------------ */
+void set_ctrl_state(struct frr_pthread *fpt, struct ctrl_state *ctrl_state)
+{
+ assert(fpt != NULL);
+ fpt->data = ctrl_state;
+}
+
+struct ctrl_state *get_ctrl_state(struct frr_pthread *fpt)
+{
+ assert(fpt != NULL);
+ assert(fpt->data != NULL);
+
+ struct ctrl_state *ctrl_state;
+ ctrl_state = (struct ctrl_state *)fpt->data;
+ assert(ctrl_state != NULL);
+ return ctrl_state;
+}
+
+int get_next_id(struct ctrl_state *ctrl_state)
+{
+ return ++ctrl_state->pcc_last_id;
+}
+
+int set_pcc_state(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state)
+{
+ assert(ctrl_state != NULL);
+ assert(pcep_pcc_get_pcc_id(pcc_state) != 0);
+
+ int current_pcc_idx = pcep_pcc_get_free_pcc_idx(ctrl_state->pcc);
+ if (current_pcc_idx >= 0) {
+ ctrl_state->pcc[current_pcc_idx] = pcc_state;
+ ctrl_state->pcc_count++;
+ PCEP_DEBUG("added pce pcc_id (%d) idx (%d)",
+ pcep_pcc_get_pcc_id(pcc_state), current_pcc_idx);
+ return 0;
+ } else {
+ PCEP_DEBUG("Max number of pce ");
+ return 1;
+ }
+}
+
+void remove_pcc_state(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state)
+{
+ assert(ctrl_state != NULL);
+ assert(pcep_pcc_get_pcc_id(pcc_state) != 0);
+
+ int idx = 0;
+ idx = pcep_pcc_get_pcc_idx_by_id(ctrl_state->pcc,
+ pcep_pcc_get_pcc_id(pcc_state));
+ if (idx != -1) {
+ ctrl_state->pcc[idx] = NULL;
+ ctrl_state->pcc_count--;
+ PCEP_DEBUG("removed pce pcc_id (%d)",
+ pcep_pcc_get_pcc_id(pcc_state));
+ }
+}
+
+uint32_t backoff_delay(uint32_t max, uint32_t base, uint32_t retry_count)
+{
+ uint32_t a = min(max, base * (1 << retry_count));
+ uint64_t r = frr_weak_random(), m = RAND_MAX;
+ uint32_t b = (a / 2) + (r * (a / 2)) / m;
+ return b;
+}
+
+const char *timer_type_name(enum pcep_ctrl_timer_type type)
+{
+ switch (type) {
+ case TM_UNDEFINED:
+ return "UNDEFINED";
+ case TM_RECONNECT_PCC:
+ return "RECONNECT_PCC";
+ case TM_PCEPLIB_TIMER:
+ return "PCEPLIB_TIMER";
+ case TM_TIMEOUT:
+ return "TIMEOUT";
+ default:
+ return "UNKNOWN";
+ }
+};
+
+const char *timeout_type_name(enum pcep_ctrl_timeout_type type)
+{
+ switch (type) {
+ case TO_UNDEFINED:
+ return "UNDEFINED";
+ case TO_COMPUTATION_REQUEST:
+ return "COMPUTATION_REQUEST";
+ default:
+ return "UNKNOWN";
+ }
+}
diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h
new file mode 100644
index 0000000000..f6eaa0ca2a
--- /dev/null
+++ b/pathd/path_pcep_controller.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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_CONTROLLER_H_
+#define _PATH_PCEP_CONTROLLER_H_
+
+#include "pathd/path_pcep.h"
+
+
+enum pcep_main_event_type {
+ PCEP_MAIN_EVENT_UNDEFINED = 0,
+ PCEP_MAIN_EVENT_START_SYNC,
+ PCEP_MAIN_EVENT_UPDATE_CANDIDATE,
+ PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP
+};
+
+typedef int (*pcep_main_event_handler_t)(enum pcep_main_event_type type,
+ int pcc_id, void *payload);
+
+enum pcep_pathd_event_type {
+ PCEP_PATH_UNDEFINED = 0,
+ PCEP_PATH_CREATED,
+ PCEP_PATH_UPDATED,
+ PCEP_PATH_REMOVED
+};
+
+struct ctrl_state {
+ struct thread_master *main;
+ struct thread_master *self;
+ pcep_main_event_handler_t main_event_handler;
+ struct pcc_opts *pcc_opts;
+ int pcc_count;
+ int pcc_last_id;
+ struct pcc_state *pcc[MAX_PCC];
+};
+
+/* Timer handling data structures */
+
+enum pcep_ctrl_timeout_type { TO_UNDEFINED, TO_COMPUTATION_REQUEST, TO_MAX };
+
+enum pcep_ctrl_timer_type {
+ TM_UNDEFINED,
+ TM_RECONNECT_PCC,
+ TM_PCEPLIB_TIMER,
+ TM_TIMEOUT,
+ TM_CALCULATE_BEST_PCE,
+ TM_SESSION_TIMEOUT_PCC,
+ TM_MAX
+};
+
+struct pcep_ctrl_timer_data {
+ struct ctrl_state *ctrl_state;
+ enum pcep_ctrl_timer_type timer_type;
+ enum pcep_ctrl_timeout_type timeout_type;
+ int pcc_id;
+ void *payload;
+};
+
+/* Socket handling data structures */
+
+enum pcep_ctrl_socket_type { SOCK_PCEPLIB = 1 };
+
+struct pcep_ctrl_socket_data {
+ struct ctrl_state *ctrl_state;
+ enum pcep_ctrl_socket_type type;
+ bool is_read;
+ int fd;
+ int pcc_id;
+ void *payload;
+};
+
+typedef int (*pcep_ctrl_thread_callback)(struct thread *);
+
+/* PCC connection information, populated in a thread-safe
+ * manner with pcep_ctrl_get_pcc_info() */
+struct pcep_pcc_info {
+ struct ctrl_state *ctrl_state; /* will be NULL when returned */
+ char pce_name[64];
+ int pcc_id;
+ struct ipaddr pcc_addr;
+ uint16_t pcc_port;
+ int status;
+ short msd;
+ uint32_t next_reqid;
+ uint32_t next_plspid;
+ bool is_best_multi_pce;
+ bool previous_best;
+ uint8_t precedence;
+};
+
+/* Functions called from the main thread */
+int pcep_ctrl_initialize(struct thread_master *main_thread,
+ struct frr_pthread **fpt,
+ pcep_main_event_handler_t event_handler);
+int pcep_ctrl_finalize(struct frr_pthread **fpt);
+int pcep_ctrl_update_pcc_options(struct frr_pthread *fpt,
+ struct pcc_opts *opts);
+int pcep_ctrl_update_pce_options(struct frr_pthread *fpt,
+ struct pce_opts *opts);
+int pcep_ctrl_remove_pcc(struct frr_pthread *fpt, struct pce_opts *pce_opts);
+int pcep_ctrl_reset_pcc_session(struct frr_pthread *fpt, char *pce_name);
+int pcep_ctrl_pathd_event(struct frr_pthread *fpt,
+ enum pcep_pathd_event_type type, struct path *path);
+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);
+pcep_session *pcep_ctrl_get_pcep_session(struct frr_pthread *fpt, int pcc_id);
+struct pcep_pcc_info *pcep_ctrl_get_pcc_info(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,
+ struct path *path);
+
+/* Functions called from the controller thread */
+void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id);
+void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path);
+void pcep_thread_cancel_timer(struct thread **thread);
+void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id,
+ int retry_count, struct thread **thread);
+void pcep_thread_schedule_timeout(struct ctrl_state *ctrl_state, int pcc_id,
+ enum pcep_ctrl_timeout_type type,
+ uint32_t delay, void *param,
+ struct thread **thread);
+void pcep_thread_schedule_session_timeout(struct ctrl_state *ctrl_state,
+ int pcc_id, int delay,
+ struct thread **thread);
+void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state);
+
+void pcep_thread_schedule_sync_best_pce(struct ctrl_state *ctrl_state,
+ int pcc_id, int delay,
+ struct thread **thread);
+void pcep_thread_schedule_pceplib_timer(struct ctrl_state *ctrl_state,
+ int delay, void *payload,
+ struct thread **thread,
+ pcep_ctrl_thread_callback cb);
+int pcep_thread_socket_read(void *fpt, void **thread, int fd, void *payload,
+ pcep_ctrl_thread_callback cb);
+int pcep_thread_socket_write(void *fpt, void **thread, int fd, void *payload,
+ pcep_ctrl_thread_callback cb);
+
+int pcep_thread_send_ctrl_event(void *fpt, void *payload,
+ pcep_ctrl_thread_callback cb);
+int pcep_thread_pcep_event(struct thread *thread);
+int pcep_thread_pcc_count(struct ctrl_state *ctrl_state);
+
+#endif // _PATH_PCEP_CONTROLLER_H_
diff --git a/pathd/path_pcep_debug.c b/pathd/path_pcep_debug.c
new file mode 100644
index 0000000000..bcaadfe4d8
--- /dev/null
+++ b/pathd/path_pcep_debug.c
@@ -0,0 +1,1771 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <stdbool.h>
+#include <time.h>
+#include <libyang/libyang.h>
+
+#include "printfrr.h"
+#include "ipaddr.h"
+
+#include "pathd/path_pcep_debug.h"
+
+static void _format_pcc_opts(int ps, struct pcc_opts *ops);
+static void _format_pce_opts(int ps, struct pce_opts *ops);
+static void _format_pcc_caps(int ps, struct pcep_caps *caps);
+static void _format_pcc_state(int ps, struct pcc_state *state);
+static void _format_ctrl_state(int ps, struct ctrl_state *state);
+static void _format_path(int ps, struct path *path);
+static void _format_path_hop(int ps, struct path_hop *hop);
+static void _format_path_metric(int ps, struct path_metric *metric);
+static void _format_pcep_event(int ps, pcep_event *event);
+static void _format_pcep_message(int ps, struct pcep_message *msg);
+static void _format_pcep_objects(int ps, double_linked_list *objs);
+static void _format_pcep_object(int ps, struct pcep_object_header *obj);
+static void _format_pcep_object_details(int ps, struct pcep_object_header *obj);
+static void _format_pcep_object_error(int ps, struct pcep_object_error *obj);
+static void _format_pcep_object_open(int ps, struct pcep_object_open *obj);
+static void _format_pcep_object_rp(int ps, struct pcep_object_rp *obj);
+static void _format_pcep_object_srp(int ps, struct pcep_object_srp *obj);
+static void _format_pcep_object_lsp(int psps, struct pcep_object_lsp *obj);
+static void _format_pcep_object_lspa(int psps, struct pcep_object_lspa *obj);
+static void
+_format_pcep_object_ipv4_endpoint(int ps,
+ struct pcep_object_endpoints_ipv4 *obj);
+static void _format_pcep_object_metric(int ps, struct pcep_object_metric *obj);
+static void _format_pcep_object_bandwidth(int ps,
+ struct pcep_object_bandwidth *obj);
+static void _format_pcep_object_nopath(int ps, struct pcep_object_nopath *obj);
+static void
+_format_pcep_object_objfun(int ps, struct pcep_object_objective_function *obj);
+static void _format_pcep_object_ro(int ps, struct pcep_object_ro *obj);
+static void _format_pcep_object_ro_details(int ps,
+ struct pcep_object_ro_subobj *ro);
+static void _format_pcep_object_ro_ipv4(int ps,
+ struct pcep_ro_subobj_ipv4 *obj);
+static void _format_pcep_object_ro_sr(int ps, struct pcep_ro_subobj_sr *obj);
+static void _format_pcep_object_tlvs(int ps, struct pcep_object_header *obj);
+static void _format_pcep_object_tlv(int ps,
+ struct pcep_object_tlv_header *tlv_header);
+static void
+_format_pcep_object_tlv_details(int ps,
+ struct pcep_object_tlv_header *tlv_header);
+static void _format_pcep_object_tlv_symbolic_path_name(
+ int ps, struct pcep_object_tlv_symbolic_path_name *tlv);
+static void _format_pcep_object_tlv_stateful_pce_capability(
+ int ps, struct pcep_object_tlv_stateful_pce_capability *tlv);
+static void _format_pcep_object_tlv_sr_pce_capability(
+ int ps, struct pcep_object_tlv_sr_pce_capability *tlv);
+static void _format_pcep_object_tlv_path_setup_type(
+ int ps, struct pcep_object_tlv_path_setup_type *tlv);
+
+const char *pcc_status_name(enum pcc_status status)
+{
+ switch (status) {
+ case PCEP_PCC_INITIALIZED:
+ return "INITIALIZED";
+ case PCEP_PCC_DISCONNECTED:
+ return "DISCONNECTED";
+ case PCEP_PCC_CONNECTING:
+ return "CONNECTING";
+ case PCEP_PCC_SYNCHRONIZING:
+ return "SYNCHRONIZING";
+ case PCEP_PCC_OPERATING:
+ return "OPERATING";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_event_type_name(pcep_event_type event_type)
+{
+ switch (event_type) {
+ case MESSAGE_RECEIVED:
+ return "MESSAGE_RECEIVED";
+ case PCE_CLOSED_SOCKET:
+ return "PCE_CLOSED_SOCKET";
+ case PCE_SENT_PCEP_CLOSE:
+ return "PCE_SENT_PCEP_CLOSE";
+ case PCE_DEAD_TIMER_EXPIRED:
+ return "PCE_DEAD_TIMER_EXPIRED";
+ case PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED:
+ return "PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED";
+ case PCC_CONNECTED_TO_PCE:
+ return "PCC_CONNECTED_TO_PCE";
+ case PCC_PCEP_SESSION_CLOSED:
+ return "PCC_PCEP_SESSION_CLOSED";
+ case PCC_RCVD_INVALID_OPEN:
+ return "PCC_RCVD_INVALID_OPEN";
+ case PCC_RCVD_MAX_INVALID_MSGS:
+ return "PCC_RCVD_MAX_INVALID_MSGS";
+ case PCC_RCVD_MAX_UNKOWN_MSGS:
+ return "PCC_RCVD_MAX_UNKOWN_MSGS";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_error_type_name(enum pcep_error_type error_type)
+{
+ switch (error_type) {
+
+ case PCEP_ERRT_SESSION_FAILURE:
+ return "SESSION_FAILURE";
+ case PCEP_ERRT_CAPABILITY_NOT_SUPPORTED:
+ return "CAPABILITY_NOT_SUPPORTED";
+ case PCEP_ERRT_UNKNOW_OBJECT:
+ return "UNKNOW_OBJECT";
+ case PCEP_ERRT_NOT_SUPPORTED_OBJECT:
+ return "NOT_SUPPORTED_OBJECT";
+ case PCEP_ERRT_POLICY_VIOLATION:
+ return "POLICY_VIOLATION";
+ case PCEP_ERRT_MANDATORY_OBJECT_MISSING:
+ return "MANDATORY_OBJECT_MISSING";
+ case PCEP_ERRT_SYNC_PC_REQ_MISSING:
+ return "SYNC_PC_REQ_MISSING";
+ case PCEP_ERRT_UNKNOWN_REQ_REF:
+ return "UNKNOWN_REQ_REF";
+ case PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION:
+ return "ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION";
+ case PCEP_ERRT_RECEPTION_OF_INV_OBJECT:
+ return "RECEPTION_OF_INV_OBJECT";
+ case PCEP_ERRT_UNRECOGNIZED_EXRS_SUBOBJ:
+ return "UNRECOGNIZED_EXRS_SUBOBJ";
+ case PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR:
+ return "DIFFSERV_AWARE_TE_ERROR";
+ case PCEP_ERRT_BRPC_PROC_COMPLETION_ERROR:
+ return "BRPC_PROC_COMPLETION_ERROR";
+ case PCEP_ERRT_UNASSIGNED14:
+ return "UNASSIGNED14";
+ case PCEP_ERRT_GLOBAL_CONCURRENT_ERROR:
+ return "GLOBAL_CONCURRENT_ERROR";
+ case PCEP_ERRT_P2PMP_CAP_ERROR:
+ return "P2PMP_CAP_ERROR";
+ case PCEP_ERRT_P2P_ENDPOINTS_ERROR:
+ return "P2P_ENDPOINTS_ERROR";
+ case PCEP_ERRT_P2P_FRAGMENTATION_ERROR:
+ return "P2P_FRAGMENTATION_ERROR";
+ case PCEP_ERRT_INVALID_OPERATION:
+ return "INVALID_OPERATION";
+ case PCEP_ERRT_LSP_STATE_SYNC_ERROR:
+ return "LSP_STATE_SYNC_ERROR";
+ case PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE:
+ return "INVALID_TE_PATH_SETUP_TYPE";
+ case PCEP_ERRT_UNASSIGNED22:
+ return "UNASSIGNED22";
+ case PCEP_ERRT_BAD_PARAMETER_VALUE:
+ return "BAD_PARAMETER_VALUE";
+ case PCEP_ERRT_LSP_INSTANTIATE_ERROR:
+ return "LSP_INSTANTIATE_ERROR";
+ case PCEP_ERRT_START_TLS_FAILURE:
+ return "START_TLS_FAILURE";
+ case PCEP_ERRT_ASSOCIATION_ERROR:
+ return "ASSOCIATION_ERROR";
+ case PCEP_ERRT_WSON_RWA_ERROR:
+ return "WSON_RWA_ERROR";
+ case PCEP_ERRT_H_PCE_ERROR:
+ return "H_PCE_ERROR";
+ case PCEP_ERRT_PATH_COMP_FAILURE:
+ return "PATH_COMP_FAILURE";
+ case PCEP_ERRT_UNASSIGNED30:
+ return "UNASSIGNED30";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_error_value_name(enum pcep_error_type error_type,
+ enum pcep_error_value error_value)
+{
+ switch (TUP(error_type, error_value)) {
+
+ case TUP(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, PCEP_ERRV_UNASSIGNED):
+ case TUP(PCEP_ERRT_SYNC_PC_REQ_MISSING, PCEP_ERRV_UNASSIGNED):
+ case TUP(PCEP_ERRT_UNKNOWN_REQ_REF, PCEP_ERRV_UNASSIGNED):
+ case TUP(PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION,
+ PCEP_ERRV_UNASSIGNED):
+ case TUP(PCEP_ERRT_UNRECOGNIZED_EXRS_SUBOBJ, PCEP_ERRV_UNASSIGNED):
+ return "UNASSIGNED";
+
+ case TUP(PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_RECVD_INVALID_OPEN_MSG):
+ return "RECVD_INVALID_OPEN_MSG";
+ case TUP(PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_OPENWAIT_TIMED_OUT):
+ return "OPENWAIT_TIMED_OUT";
+ case TUP(PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NO_NEG):
+ return "UNACCEPTABLE_OPEN_MSG_NO_NEG";
+ case TUP(PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG):
+ return "UNACCEPTABLE_OPEN_MSG_NEG";
+ case TUP(PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_RECVD_SECOND_OPEN_MSG_UNACCEPTABLE):
+ return "RECVD_SECOND_OPEN_MSG_UNACCEPTABLE";
+ case TUP(PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_RECVD_PCERR):
+ return "RECVD_PCERR";
+ case TUP(PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_KEEPALIVEWAIT_TIMED_OUT):
+ return "KEEPALIVEWAIT_TIMED_OUT";
+ case TUP(PCEP_ERRT_SESSION_FAILURE,
+ PCEP_ERRV_PCEP_VERSION_NOT_SUPPORTED):
+ return "PCEP_VERSION_NOT_SUPPORTED";
+
+ case TUP(PCEP_ERRT_UNKNOW_OBJECT, PCEP_ERRV_UNREC_OBJECT_CLASS):
+ return "UNREC_OBJECT_CLASS";
+ case TUP(PCEP_ERRT_UNKNOW_OBJECT, PCEP_ERRV_UNREC_OBJECT_TYPE):
+ return "UNREC_OBJECT_TYPE";
+
+ case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT,
+ PCEP_ERRV_NOT_SUPPORTED_OBJECT_CLASS):
+ return "NOT_SUPPORTED_OBJECT_CLASS";
+ case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT,
+ PCEP_ERRV_NOT_SUPPORTED_OBJECT_TYPE):
+ return "NOT_SUPPORTED_OBJECT_TYPE";
+ case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT, PCEP_ERRV_UNSUPPORTED_PARAM):
+ return "UNSUPPORTED_PARAM";
+ case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT,
+ PCEP_ERRV_UNSUPPORTED_NW_PERF_CONSTRAINT):
+ return "UNSUPPORTED_NW_PERF_CONSTRAINT";
+ case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT,
+ PCEP_ERRV_NOT_SUPPORTED_BW_OBJECT_3_4):
+ return "NOT_SUPPORTED_BW_OBJECT_3_4";
+ case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT,
+ PCEP_ERRV_UNSUPPORTED_ENDPOINT_TYPE):
+ return "UNSUPPORTED_ENDPOINT_TYPE";
+ case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT,
+ PCEP_ERRV_UNSUPPORTED_ENDPOINT_TLV):
+ return "UNSUPPORTED_ENDPOINT_TLV";
+ case TUP(PCEP_ERRT_NOT_SUPPORTED_OBJECT,
+ PCEP_ERRV_UNSUPPORTED_RP_FLAG_GRANULARITY):
+ return "UNSUPPORTED_RP_FLAG_GRANULARITY";
+
+ case TUP(PCEP_ERRT_POLICY_VIOLATION,
+ PCEP_ERRV_C_BIT_SET_IN_METRIC_OBJECT):
+ return "C_BIT_SET_IN_METRIC_OBJECT";
+ case TUP(PCEP_ERRT_POLICY_VIOLATION,
+ PCEP_ERRV_O_BIT_CLEARD_IN_RP_OBJECT):
+ return "O_BIT_CLEARD_IN_RP_OBJECT";
+ case TUP(PCEP_ERRT_POLICY_VIOLATION,
+ PCEP_ERRV_OBJECTIVE_FUNC_NOT_ALLOWED):
+ return "OBJECTIVE_FUNC_NOT_ALLOWED";
+ case TUP(PCEP_ERRT_POLICY_VIOLATION, PCEP_ERRV_RP_OF_BIT_SET):
+ return "RP_OF_BIT_SET";
+ case TUP(PCEP_ERRT_POLICY_VIOLATION,
+ PCEP_ERRV_GLOBAL_CONCURRENCY_NOT_ALLOWED):
+ return "GLOBAL_CONCURRENCY_NOT_ALLOWED";
+ case TUP(PCEP_ERRT_POLICY_VIOLATION, PCEP_ERRV_MONITORING_MSG_REJECTED):
+ return "MONITORING_MSG_REJECTED";
+ case TUP(PCEP_ERRT_POLICY_VIOLATION,
+ PCEP_ERRV_P2MP_PATH_COMP_NOT_ALLOWED):
+ return "P2MP_PATH_COMP_NOT_ALLOWED";
+ case TUP(PCEP_ERRT_POLICY_VIOLATION,
+ PCEP_ERRV_UNALLOWED_NW_PERF_CONSTRAINT):
+ return "UNALLOWED_NW_PERF_CONSTRAINT";
+
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_RP_OBJECT_MISSING):
+ return "RP_OBJECT_MISSING";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_RRO_OBJECT_MISSING_FOR_REOP):
+ return "RRO_OBJECT_MISSING_FOR_REOP";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_EP_OBJECT_MISSING):
+ return "EP_OBJECT_MISSING";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_MONITOR_OBJECT_MISSING):
+ return "MONITOR_OBJECT_MISSING";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_OBJECT_MISSING):
+ return "LSP_OBJECT_MISSING";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_ERO_OBJECT_MISSING):
+ return "ERO_OBJECT_MISSING";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_SRP_OBJECT_MISSING):
+ return "SRP_OBJECT_MISSING";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_ID_TLV_MISSING):
+ return "LSP_ID_TLV_MISSING";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_LSP_DB_TLV_MISSING):
+ return "LSP_DB_TLV_MISSING";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_S2LS_OBJECT_MISSING):
+ return "S2LS_OBJECT_MISSING";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_P2MP_LSP_ID_TLV_MISSING):
+ return "P2MP_LSP_ID_TLV_MISSING";
+ case TUP(PCEP_ERRT_MANDATORY_OBJECT_MISSING,
+ PCEP_ERRV_DISJOINTED_CONF_TLV_MISSING):
+ return "DISJOINTED_CONF_TLV_MISSING";
+
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_P_FLAG_NOT_CORRECT_IN_OBJECT):
+ return "P_FLAG_NOT_CORRECT_IN_OBJECT";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_BAD_LABEL_VALUE):
+ return "BAD_LABEL_VALUE";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_UNSUPPORTED_NUM_SR_ERO_SUBOBJECTS):
+ return "UNSUPPORTED_NUM_SR_ERO_SUBOBJECTS";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_BAD_LABEL_FORMAT):
+ return "BAD_LABEL_FORMAT";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_ERO_SR_ERO_MIX):
+ return "ERO_SR_ERO_MIX";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_SR_ERO_SID_NAI_ABSENT):
+ return "SR_ERO_SID_NAI_ABSENT";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_SR_RRO_SID_NAI_ABSENT):
+ return "SR_RRO_SID_NAI_ABSENT";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_SYMBOLIC_PATH_NAME_TLV_MISSING):
+ return "SYMBOLIC_PATH_NAME_TLV_MISSING";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_MSD_EXCEEDS_PCEP_SESSION_MAX):
+ return "MSD_EXCEEDS_PCEP_SESSION_MAX";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_RRO_SR_RRO_MIX):
+ return "RRO_SR_RRO_MIX";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_MALFORMED_OBJECT):
+ return "MALFORMED_OBJECT";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_MISSING_PCE_SR_CAP_TLV):
+ return "MISSING_PCE_SR_CAP_TLV";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_UNSUPPORTED_NAI):
+ return "UNSUPPORTED_NAI";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_UNKNOWN_SID):
+ return "UNKNOWN_SID";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_CANNOT_RESOLVE_NAI_TO_SID):
+ return "CANNOT_RESOLVE_NAI_TO_SID";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_COULD_NOT_FIND_SRGB):
+ return "COULD_NOT_FIND_SRGB";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_SID_EXCEEDS_SRGB):
+ return "SID_EXCEEDS_SRGB";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_COULD_NOT_FIND_SRLB):
+ return "COULD_NOT_FIND_SRLB";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_SID_EXCEEDS_SRLB):
+ return "SID_EXCEEDS_SRLB";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT, PCEP_ERRV_INCONSISTENT_SID):
+ return "INCONSISTENT_SID";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_MSD_MUST_BE_NONZERO):
+ return "MSD_MUST_BE_NONZERO";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_MISMATCH_O_S2LS_LSP):
+ return "MISMATCH_O_S2LS_LSP";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_INCOMPATIBLE_H_PCE_OF):
+ return "INCOMPATIBLE_H_PCE_OF";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_BAD_BANDWIDTH_TYPE_3_4):
+ return "BAD_BANDWIDTH_TYPE_3_4";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_UNSUPPORTED_LSP_PROT_FLAGS):
+ return "UNSUPPORTED_LSP_PROT_FLAGS";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_UNSUPPORTED_2ND_LSP_PROT_FLAGS):
+ return "UNSUPPORTED_2ND_LSP_PROT_FLAGS";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_UNSUPPORTED_LINK_PROT_TYPE):
+ return "UNSUPPORTED_LINK_PROT_TYPE";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_LABEL_SET_TLV_NO_RP_R):
+ return "LABEL_SET_TLV_NO_RP_R";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_WRONG_LABEL_SET_TLV_O_L_SET):
+ return "WRONG_LABEL_SET_TLV_O_L_SET";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_WRONG_LABEL_SET_O_SET):
+ return "WRONG_LABEL_SET_O_SET";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_MISSING_GMPLS_CAP_TLV):
+ return "MISSING_GMPLS_CAP_TLV";
+ case TUP(PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_INCOMPATIBLE_OF_CODE):
+ return "INCOMPATIBLE_OF_CODE";
+
+ case TUP(PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR,
+ PCEP_ERRV_UNSUPPORTED_CLASS_TYPE):
+ return "UNSUPPORTED_CLASS_TYPE";
+ case TUP(PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR,
+ PCEP_ERRV_INVALID_CLASS_TYPE):
+ return "INVALID_CLASS_TYPE";
+ case TUP(PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR,
+ PCEP_ERRV_CLASS_SETUP_TYPE_NOT_TE_CLASS):
+ return "CLASS_SETUP_TYPE_NOT_TE_CLASS";
+
+ case TUP(PCEP_ERRT_BRPC_PROC_COMPLETION_ERROR,
+ PCEP_ERRV_BRPC_PROC_NOT_SUPPORTED):
+ return "BRPC_PROC_NOT_SUPPORTED";
+
+ case TUP(PCEP_ERRT_GLOBAL_CONCURRENT_ERROR,
+ PCEP_ERRV_INSUFFICIENT_MEMORY):
+ return "INSUFFICIENT_MEMORY";
+ case TUP(PCEP_ERRT_GLOBAL_CONCURRENT_ERROR,
+ PCEP_ERRV_GLOBAL_CONCURRENT_OPT_NOT_SUPPORTED):
+ return "GLOBAL_CONCURRENT_OPT_NOT_SUPPORTED";
+
+ case TUP(PCEP_ERRT_P2PMP_CAP_ERROR, PCEP_ERRV_PCE_INSUFFICIENT_MEMORY):
+ return "PCE_INSUFFICIENT_MEMORY";
+ case TUP(PCEP_ERRT_P2PMP_CAP_ERROR,
+ PCEP_ERRV_PCE_NOT_CAPABLE_P2MP_COMP):
+ return "PCE_NOT_CAPABLE_P2MP_COMP";
+
+ case TUP(PCEP_ERRT_P2P_ENDPOINTS_ERROR,
+ PCEP_ERRV_NO_EP_WITH_LEAF_TYPE2):
+ return "NO_EP_WITH_LEAF_TYPE2";
+ case TUP(PCEP_ERRT_P2P_ENDPOINTS_ERROR,
+ PCEP_ERRV_NO_EP_WITH_LEAF_TYPE3):
+ return "NO_EP_WITH_LEAF_TYPE3";
+ case TUP(PCEP_ERRT_P2P_ENDPOINTS_ERROR,
+ PCEP_ERRV_NO_EP_WITH_LEAF_TYPE4):
+ return "NO_EP_WITH_LEAF_TYPE4";
+ case TUP(PCEP_ERRT_P2P_ENDPOINTS_ERROR, PCEP_ERRV_INCONSITENT_EP):
+ return "INCONSITENT_EP";
+
+ case TUP(PCEP_ERRT_P2P_FRAGMENTATION_ERROR,
+ PCEP_ERRV_FRAG_REQUEST_FAILURE):
+ return "FRAG_REQUEST_FAILURE";
+ case TUP(PCEP_ERRT_P2P_FRAGMENTATION_ERROR,
+ PCEP_ERRV_FRAG_REPORT_FAILURE):
+ return "FRAG_REPORT_FAILURE";
+ case TUP(PCEP_ERRT_P2P_FRAGMENTATION_ERROR,
+ PCEP_ERRV_FRAG_UPDATE_FAILURE):
+ return "FRAG_UPDATE_FAILURE";
+ case TUP(PCEP_ERRT_P2P_FRAGMENTATION_ERROR,
+ PCEP_ERRV_FRAG_INSTANTIATION_FAILURE):
+ return "FRAG_INSTANTIATION_FAILURE";
+
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_UPDATE_FOR_NON_DELEGATED_LSP):
+ return "LSP_UPDATE_FOR_NON_DELEGATED_LS";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_UPDATE_NON_ADVERTISED_PCE):
+ return "LSP_UPDATE_NON_ADVERTISED_PC";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_UPDATE_UNKNOWN_PLSP_ID):
+ return "LSP_UPDATE_UNKNOWN_PLSP_I";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_REPORT_NON_ADVERTISED_PCE):
+ return "LSP_REPORT_NON_ADVERTISED_PC";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_PCE_INIT_LSP_LIMIT_REACHED):
+ return "PCE_INIT_LSP_LIMIT_REACHE";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_PCE_INIT_LSP_DELEGATION_CANT_REVOKE):
+ return "PCE_INIT_LSP_DELEGATION_CANT_REVOK";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_INIT_NON_ZERO_PLSP_ID):
+ return "LSP_INIT_NON_ZERO_PLSP_I";
+ case TUP(PCEP_ERRT_INVALID_OPERATION, PCEP_ERRV_LSP_NOT_PCE_INITIATED):
+ return "LSP_NOT_PCE_INITIATE";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_PCE_INIT_OP_FREQ_LIMIT_REACHED):
+ return "PCE_INIT_OP_FREQ_LIMIT_REACHE";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_REPORT_P2MP_NOT_ADVERTISED):
+ return "LSP_REPORT_P2MP_NOT_ADVERTISE";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_UPDATE_P2MP_NOT_ADVERTISED):
+ return "LSP_UPDATE_P2MP_NOT_ADVERTISE";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_INSTANTIATION_P2MP_NOT_ADVERTISED):
+ return "LSP_INSTANTIATION_P2MP_NOT_ADVERTISE";
+ case TUP(PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_AUTO_BW_CAP_NOT_ADVERTISED):
+ return "AUTO_BW_CAP_NOT_ADVERTISE";
+
+ case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR,
+ PCEP_ERRV_PCE_CANT_PROCESS_LSP_REPORT):
+ return "PCE_CANT_PROCESS_LSP_REPORT";
+ case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR,
+ PCEP_ERRV_LSP_DB_VERSION_MISMATCH):
+ return "LSP_DB_VERSION_MISMATCH";
+ case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR,
+ PCEP_ERRV_TRIGGER_ATTEMPT_BEFORE_PCE_TRIGGER):
+ return "TRIGGER_ATTEMPT_BEFORE_PCE_TRIGGER";
+ case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR,
+ PCEP_ERRV_TRIGGER_ATTEMPT_NO_PCE_TRIGGER_CAP):
+ return "TRIGGER_ATTEMPT_NO_PCE_TRIGGER_CAP";
+ case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR,
+ PCEP_ERRV_PCC_CANT_COMPLETE_STATE_SYNC):
+ return "PCC_CANT_COMPLETE_STATE_SYNC";
+ case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR,
+ PCEP_ERRV_INVALID_LSP_DB_VERSION_NUMBER):
+ return "INVALID_LSP_DB_VERSION_NUMBER";
+ case TUP(PCEP_ERRT_LSP_STATE_SYNC_ERROR,
+ PCEP_ERRV_INVALID_SPEAKER_ENTITY_ID):
+ return "INVALID_SPEAKER_ENTITY_ID";
+
+ case TUP(PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE,
+ PCEP_ERRV_UNSUPPORTED_PATH_SETUP_TYPE):
+ return "UNSUPPORTED_PATH_SETUP_TYPE";
+ case TUP(PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE,
+ PCEP_ERRV_MISMATCHED_PATH_SETUP_TYPE):
+ return "MISMATCHED_PATH_SETUP_TYPE";
+
+ case TUP(PCEP_ERRT_BAD_PARAMETER_VALUE,
+ PCEP_ERRV_SYMBOLIC_PATH_NAME_IN_USE):
+ return "SYMBOLIC_PATH_NAME_IN_USE";
+ case TUP(PCEP_ERRT_BAD_PARAMETER_VALUE,
+ PCEP_ERRV_LSP_SPEAKER_ID_NOT_PCE_INITIATED):
+ return "LSP_SPEAKER_ID_NOT_PCE_INITIATED";
+
+ case TUP(PCEP_ERRT_LSP_INSTANTIATE_ERROR,
+ PCEP_ERRV_UNACCEPTABLE_INSTANTIATE_ERROR):
+ return "UNACCEPTABLE_INSTANTIATE_ERROR";
+ case TUP(PCEP_ERRT_LSP_INSTANTIATE_ERROR, PCEP_ERRV_INTERNAL_ERROR):
+ return "INTERNAL_ERROR";
+ case TUP(PCEP_ERRT_LSP_INSTANTIATE_ERROR, PCEP_ERRV_SIGNALLING_ERROR):
+ return "SIGNALLING_ERROR";
+
+ case TUP(PCEP_ERRT_START_TLS_FAILURE,
+ PCEP_ERRV_START_TLS_AFTER_PCEP_EXCHANGE):
+ return "START_TLS_AFTER_PCEP_EXCHANGE";
+ case TUP(PCEP_ERRT_START_TLS_FAILURE,
+ PCEP_ERRV_MSG_NOT_START_TLS_OPEN_ERROR):
+ return "MSG_NOT_START_TLS_OPEN_ERROR";
+ case TUP(PCEP_ERRT_START_TLS_FAILURE,
+ PCEP_ERRV_CONNECTION_WO_TLS_NOT_POSSIBLE):
+ return "CONNECTION_WO_TLS_NOT_POSSIBLE";
+ case TUP(PCEP_ERRT_START_TLS_FAILURE,
+ PCEP_ERRV_CONNECTION_WO_TLS_IS_POSSIBLE):
+ return "CONNECTION_WO_TLS_IS_POSSIBLE";
+ case TUP(PCEP_ERRT_START_TLS_FAILURE,
+ PCEP_ERRV_NO_START_TLS_BEFORE_START_TLS_WAIT_TIMER):
+ return "NO_START_TLS_BEFORE_START_TLS_WAIT_TIMER";
+
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR,
+ PCEP_ERRV_ASSOC_TYPE_NOT_SUPPORTED):
+ return "ASSOC_TYPE_NOT_SUPPORTED";
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR,
+ PCEP_ERRV_TOO_MANY_LSPS_IN_ASSOC_GRP):
+ return "TOO_MANY_LSPS_IN_ASSOC_GRP";
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR, PCEP_ERRV_TOO_MANY_ASSOC_GROUPS):
+ return "TOO_MANY_ASSOC_GROUPS";
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR, PCEP_ERRV_ASSOCIATION_UNKNOWN):
+ return "ASSOCIATION_UNKNOWN";
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR,
+ PCEP_ERRV_OP_CONF_ASSOC_INFO_MISMATCH):
+ return "OP_CONF_ASSOC_INFO_MISMATCH";
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR, PCEP_ERRV_ASSOC_INFO_MISMATCH):
+ return "ASSOC_INFO_MISMATCH";
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR,
+ PCEP_ERRV_CANNOT_JOIN_ASSOC_GROUP):
+ return "CANNOT_JOIN_ASSOC_GROUP";
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR, PCEP_ERRV_ASSOC_ID_NOT_IN_RANGE):
+ return "ASSOC_ID_NOT_IN_RANGE";
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR,
+ PCEP_ERRV_TUNNEL_EP_MISMATCH_PATH_PROT_ASSOC):
+ return "TUNNEL_EP_MISMATCH_PATH_PROT_ASSOC";
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR,
+ PCEP_ERRV_ATTEMPTED_ADD_LSP_PATH_PROT_ASSOC):
+ return "ATTEMPTED_ADD_LSP_PATH_PROT_ASSOC";
+ case TUP(PCEP_ERRT_ASSOCIATION_ERROR,
+ PCEP_ERRV_PROTECTION_TYPE_NOT_SUPPORTED):
+ return "PROTECTION_TYPE_NOT_SUPPORTED";
+
+ case TUP(PCEP_ERRT_WSON_RWA_ERROR, PCEP_ERRV_RWA_INSUFFICIENT_MEMORY):
+ return "RWA_INSUFFICIENT_MEMORY";
+ case TUP(PCEP_ERRT_WSON_RWA_ERROR, PCEP_ERRV_RWA_COMP_NOT_SUPPORTED):
+ return "RWA_COMP_NOT_SUPPORTED";
+ case TUP(PCEP_ERRT_WSON_RWA_ERROR, PCEP_ERRV_SYNTAX_ENC_ERROR):
+ return "SYNTAX_ENC_ERROR";
+
+ case TUP(PCEP_ERRT_H_PCE_ERROR, PCEP_ERRV_H_PCE_CAP_NOT_ADVERTISED):
+ return "H_PCE_CAP_NOT_ADVERTISED";
+ case TUP(PCEP_ERRT_H_PCE_ERROR,
+ PCEP_ERRV_PARENT_PCE_CAP_CANT_BE_PROVIDED):
+ return "PARENT_PCE_CAP_CANT_BE_PROVIDED";
+
+ case TUP(PCEP_ERRT_PATH_COMP_FAILURE,
+ PCEP_ERRV_UNACCEPTABLE_REQUEST_MSG):
+ return "UNACCEPTABLE_REQUEST_MSG";
+ case TUP(PCEP_ERRT_PATH_COMP_FAILURE,
+ PCEP_ERRV_GENERALIZED_BW_VAL_NOT_SUPPORTED):
+ return "GENERALIZED_BW_VAL_NOT_SUPPORTED";
+ case TUP(PCEP_ERRT_PATH_COMP_FAILURE,
+ PCEP_ERRV_LABEL_SET_CONSTRAINT_COULD_NOT_BE_MET):
+ return "LABEL_SET_CONSTRAINT_COULD_NOT_BE_MET";
+ case TUP(PCEP_ERRT_PATH_COMP_FAILURE,
+ PCEP_ERRV_LABEL_CONSTRAINT_COULD_NOT_BE_MET):
+ return "LABEL_CONSTRAINT_COULD_NOT_BE_MET";
+
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_message_type_name(enum pcep_message_types pcep_message_type)
+{
+ switch (pcep_message_type) {
+
+ case PCEP_TYPE_OPEN:
+ return "OPEN";
+ case PCEP_TYPE_KEEPALIVE:
+ return "KEEPALIVE";
+ case PCEP_TYPE_PCREQ:
+ return "PCREQ";
+ case PCEP_TYPE_PCREP:
+ return "PCREP";
+ case PCEP_TYPE_PCNOTF:
+ return "PCNOTF";
+ case PCEP_TYPE_ERROR:
+ return "ERROR";
+ case PCEP_TYPE_CLOSE:
+ return "CLOSE";
+ case PCEP_TYPE_REPORT:
+ return "REPORT";
+ case PCEP_TYPE_UPDATE:
+ return "UPDATE";
+ case PCEP_TYPE_INITIATE:
+ return "INITIATE";
+ case PCEP_TYPE_UNKOWN_MSG:
+ return "UNKOWN_MSG";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_object_class_name(enum pcep_object_classes obj_class)
+{
+ switch (obj_class) {
+ case PCEP_OBJ_CLASS_OPEN:
+ return "OPEN";
+ case PCEP_OBJ_CLASS_RP:
+ return "RP";
+ case PCEP_OBJ_CLASS_NOPATH:
+ return "NOPATH";
+ case PCEP_OBJ_CLASS_ENDPOINTS:
+ return "ENDPOINTS";
+ case PCEP_OBJ_CLASS_BANDWIDTH:
+ return "BANDWIDTH";
+ case PCEP_OBJ_CLASS_METRIC:
+ return "METRIC";
+ case PCEP_OBJ_CLASS_ERO:
+ return "ERO";
+ case PCEP_OBJ_CLASS_RRO:
+ return "RRO";
+ case PCEP_OBJ_CLASS_LSPA:
+ return "LSPA";
+ case PCEP_OBJ_CLASS_IRO:
+ return "IRO";
+ case PCEP_OBJ_CLASS_SVEC:
+ return "SVEC";
+ case PCEP_OBJ_CLASS_NOTF:
+ return "NOTF";
+ case PCEP_OBJ_CLASS_ERROR:
+ return "ERROR";
+ case PCEP_OBJ_CLASS_CLOSE:
+ return "CLOSE";
+ case PCEP_OBJ_CLASS_OF:
+ return "OF";
+ case PCEP_OBJ_CLASS_LSP:
+ return "LSP";
+ case PCEP_OBJ_CLASS_SRP:
+ return "SRP";
+ case PCEP_OBJ_CLASS_VENDOR_INFO:
+ return "VENDOR_INFO";
+ case PCEP_OBJ_CLASS_INTER_LAYER:
+ return "INTER_LAYER";
+ case PCEP_OBJ_CLASS_SWITCH_LAYER:
+ return "SWITCH_LAYER";
+ case PCEP_OBJ_CLASS_REQ_ADAP_CAP:
+ return "REQ_ADAP_CAP";
+ case PCEP_OBJ_CLASS_SERVER_IND:
+ return "SERVER_IND";
+ case PCEP_OBJ_CLASS_ASSOCIATION:
+ return "ASSOCIATION";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_object_type_name(enum pcep_object_classes obj_class,
+ enum pcep_object_types obj_type)
+{
+ switch (TUP(obj_class, obj_type)) {
+ case TUP(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN):
+ return "OPEN";
+ case TUP(PCEP_OBJ_CLASS_RP, PCEP_OBJ_TYPE_RP):
+ return "RP";
+ case TUP(PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH):
+ return "NOPATH";
+ case TUP(PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV4):
+ return "ENDPOINT_IPV4";
+ case TUP(PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV6):
+ return "ENDPOINT_IPV6";
+ case TUP(PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_REQ):
+ return "BANDWIDTH_REQ";
+ case TUP(PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_TELSP):
+ return "BANDWIDTH_TELSP";
+ case TUP(PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_CISCO):
+ return "BANDWIDTH_CISCO";
+ case TUP(PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC):
+ return "METRIC";
+ case TUP(PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO):
+ return "ERO";
+ case TUP(PCEP_OBJ_CLASS_RRO, PCEP_OBJ_TYPE_RRO):
+ return "RRO";
+ case TUP(PCEP_OBJ_CLASS_LSPA, PCEP_OBJ_TYPE_LSPA):
+ return "LSPA";
+ case TUP(PCEP_OBJ_CLASS_IRO, PCEP_OBJ_TYPE_IRO):
+ return "IRO";
+ case TUP(PCEP_OBJ_CLASS_SVEC, PCEP_OBJ_TYPE_SVEC):
+ return "SVEC";
+ case TUP(PCEP_OBJ_CLASS_NOTF, PCEP_OBJ_TYPE_NOTF):
+ return "NOTF";
+ case TUP(PCEP_OBJ_CLASS_ERROR, PCEP_OBJ_TYPE_ERROR):
+ return "ERROR";
+ case TUP(PCEP_OBJ_CLASS_CLOSE, PCEP_OBJ_TYPE_CLOSE):
+ return "CLOSE";
+ case TUP(PCEP_OBJ_CLASS_INTER_LAYER, PCEP_OBJ_TYPE_INTER_LAYER):
+ return "INTER_LAYER";
+ case TUP(PCEP_OBJ_CLASS_SWITCH_LAYER, PCEP_OBJ_TYPE_SWITCH_LAYER):
+ return "SWITCH_LAYER";
+ case TUP(PCEP_OBJ_CLASS_REQ_ADAP_CAP, PCEP_OBJ_TYPE_REQ_ADAP_CAP):
+ return "REQ_ADAP_CAP";
+ case TUP(PCEP_OBJ_CLASS_SERVER_IND, PCEP_OBJ_TYPE_SERVER_IND):
+ return "SERVER_IND";
+ case TUP(PCEP_OBJ_CLASS_ASSOCIATION, PCEP_OBJ_TYPE_ASSOCIATION_IPV4):
+ return "ASSOCIATION_IPV4";
+ case TUP(PCEP_OBJ_CLASS_ASSOCIATION, PCEP_OBJ_TYPE_ASSOCIATION_IPV6):
+ return "ASSOCIATION_IPV6";
+ case TUP(PCEP_OBJ_CLASS_OF, PCEP_OBJ_TYPE_OF):
+ return "OF";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_lsp_status_name(enum pcep_lsp_operational_status status)
+{
+ switch (status) {
+ case PCEP_LSP_OPERATIONAL_DOWN:
+ return "DOWN";
+ case PCEP_LSP_OPERATIONAL_UP:
+ return "UP";
+ case PCEP_LSP_OPERATIONAL_ACTIVE:
+ return "ACTIVE";
+ case PCEP_LSP_OPERATIONAL_GOING_DOWN:
+ return "GOING_DOWN";
+ case PCEP_LSP_OPERATIONAL_GOING_UP:
+ return "GOING_UP";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+const char *pcep_tlv_type_name(enum pcep_object_tlv_types tlv_type)
+{
+ switch (tlv_type) {
+ case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR:
+ return "NO_PATH_VECTOR";
+ case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
+ return "STATEFUL_PCE_CAPABILITY";
+ case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
+ return "SYMBOLIC_PATH_NAME";
+ case PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS:
+ return "IPV4_LSP_IDENTIFIERS";
+ case PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS:
+ return "IPV6_LSP_IDENTIFIERS";
+ case PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE:
+ return "LSP_ERROR_CODE";
+ case PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC:
+ return "RSVP_ERROR_SPEC";
+ case PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION:
+ return "LSP_DB_VERSION";
+ case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID:
+ return "SPEAKER_ENTITY_ID";
+ case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY:
+ return "SR_PCE_CAPABILITY";
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE:
+ return "PATH_SETUP_TYPE";
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
+ return "PATH_SETUP_TYPE_CAPABILITY";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_ro_type_name(enum pcep_ro_subobj_types ro_type)
+{
+ switch (ro_type) {
+
+ case RO_SUBOBJ_TYPE_IPV4:
+ return "IPV4";
+ case RO_SUBOBJ_TYPE_IPV6:
+ return "IPV6";
+ case RO_SUBOBJ_TYPE_LABEL:
+ return "LABEL";
+ case RO_SUBOBJ_TYPE_UNNUM:
+ return "UNNUM";
+ case RO_SUBOBJ_TYPE_ASN:
+ return "ASN";
+ case RO_SUBOBJ_TYPE_SR:
+ return "SR";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_nai_type_name(enum pcep_sr_subobj_nai nai_type)
+{
+ switch (nai_type) {
+ case PCEP_SR_SUBOBJ_NAI_ABSENT:
+ return "ABSENT";
+ case PCEP_SR_SUBOBJ_NAI_IPV4_NODE:
+ return "IPV4_NODE";
+ case PCEP_SR_SUBOBJ_NAI_IPV6_NODE:
+ return "IPV6_NODE";
+ case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY:
+ return "IPV4_ADJACENCY";
+ case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY:
+ return "IPV6_ADJACENCY";
+ case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY:
+ return "UNNUMBERED_IPV4_ADJACENCY";
+ case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY:
+ return "LINK_LOCAL_IPV6_ADJACENCY";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_metric_type_name(enum pcep_metric_types type)
+{
+ switch (type) {
+ case PCEP_METRIC_IGP:
+ return "IGP";
+ case PCEP_METRIC_TE:
+ return "TE";
+ case PCEP_METRIC_HOP_COUNT:
+ return "HOP_COUNT";
+ case PCEP_METRIC_AGGREGATE_BW:
+ return "AGGREGATE_BW";
+ case PCEP_METRIC_MOST_LOADED_LINK:
+ return "MOST_LOADED_LINK";
+ case PCEP_METRIC_CUMULATIVE_IGP:
+ return "CUMULATIVE_IGP";
+ case PCEP_METRIC_CUMULATIVE_TE:
+ return "CUMULATIVE_TE";
+ case PCEP_METRIC_P2MP_IGP:
+ return "P2MP_IGP";
+ case PCEP_METRIC_P2MP_TE:
+ return "P2MP_TE";
+ case PCEP_METRIC_P2MP_HOP_COUNT:
+ return "P2MP_HOP_COUNT";
+ case PCEP_METRIC_SEGMENT_ID_DEPTH:
+ return "SEGMENT_ID_DEPTH";
+ case PCEP_METRIC_PATH_DELAY:
+ return "PATH_DELAY";
+ case PCEP_METRIC_PATH_DELAY_VARIATION:
+ return "PATH_DELAY_VARIATION";
+ case PCEP_METRIC_PATH_LOSS:
+ return "PATH_LOSS";
+ case PCEP_METRIC_P2MP_PATH_DELAY:
+ return "P2MP_PATH_DELAY";
+ case PCEP_METRIC_P2MP_PATH_DELAY_VARIATION:
+ return "P2MP_PATH_DELAY_VARIATION";
+ case PCEP_METRIC_P2MP_PATH_LOSS:
+ return "P2MP_PATH_LOSS";
+ case PCEP_METRIC_NUM_PATH_ADAPTATIONS:
+ return "NUM_PATH_ADAPTATIONS";
+ case PCEP_METRIC_NUM_PATH_LAYERS:
+ return "NUM_PATH_LAYERS";
+ case PCEP_METRIC_DOMAIN_COUNT:
+ return "DOMAIN_COUNT";
+ case PCEP_METRIC_BORDER_NODE_COUNT:
+ return "BORDER_NODE_COUNT";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *pcep_nopath_tlv_err_code_name(enum pcep_nopath_tlv_err_codes type)
+{
+ switch (type) {
+ case PCEP_NOPATH_TLV_ERR_NO_TLV:
+ return "NO_TLV";
+ case PCEP_NOPATH_TLV_ERR_PCE_UNAVAILABLE:
+ return "PCE_UNAVAILABLE";
+ case PCEP_NOPATH_TLV_ERR_UNKNOWN_DST:
+ return "UNKNOWN_DST";
+ case PCEP_NOPATH_TLV_ERR_UNKNOWN_SRC:
+ return "UNKNOWN_SRC";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *format_objfun_set(uint32_t flags)
+{
+ int i, c;
+ PATHD_FORMAT_INIT();
+ for (i = 1, c = 0; i <= MAX_OBJFUN_TYPE; i++) {
+ if (CHECK_FLAG(flags, i)) {
+ if (c > 0)
+ PATHD_FORMAT(", %s", objfun_type_name(i));
+ else
+ PATHD_FORMAT("%s", objfun_type_name(i));
+ c++;
+ }
+ }
+ return PATHD_FORMAT_FINI();
+}
+
+
+const char *format_pcc_opts(struct pcc_opts *opts)
+{
+ PATHD_FORMAT_INIT();
+ _format_pcc_opts(0, opts);
+ return PATHD_FORMAT_FINI();
+}
+
+const char *format_pcc_state(struct pcc_state *state)
+{
+ PATHD_FORMAT_INIT();
+ _format_pcc_state(0, state);
+ return PATHD_FORMAT_FINI();
+}
+
+const char *format_ctrl_state(struct ctrl_state *state)
+{
+ PATHD_FORMAT_INIT();
+ _format_ctrl_state(0, state);
+ return PATHD_FORMAT_FINI();
+}
+
+const char *format_path(struct path *path)
+{
+ PATHD_FORMAT_INIT();
+ _format_path(0, path);
+ return PATHD_FORMAT_FINI();
+}
+
+const char *format_pcep_event(pcep_event *event)
+{
+ PATHD_FORMAT_INIT();
+ _format_pcep_event(0, event);
+ return PATHD_FORMAT_FINI();
+}
+
+const char *format_pcep_message(struct pcep_message *msg)
+{
+ PATHD_FORMAT_INIT();
+ _format_pcep_message(0, msg);
+ return PATHD_FORMAT_FINI();
+}
+
+const char *format_yang_dnode(struct lyd_node *dnode)
+{
+ char *buff;
+ int len;
+
+ lyd_print_mem(&buff, dnode, LYD_JSON, LYP_FORMAT);
+ len = strlen(buff);
+ memcpy(_debug_buff, buff, len);
+ free(buff);
+ return _debug_buff;
+}
+
+void _format_pcc_opts(int ps, struct pcc_opts *opts)
+{
+ if (opts == NULL) {
+ PATHD_FORMAT("NULL\n");
+ } else {
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ PATHD_FORMAT("\n");
+ if (IS_IPADDR_V4(&opts->addr)) {
+ PATHD_FORMAT("%*saddr_v4: %pI4\n", ps2, "",
+ &opts->addr.ipaddr_v4);
+ } else {
+ PATHD_FORMAT("%*saddr_v4: undefined", ps2, "");
+ }
+ if (IS_IPADDR_V6(&opts->addr)) {
+ PATHD_FORMAT("%*saddr_v6: %pI6\n", ps2, "",
+ &opts->addr.ipaddr_v6);
+ } else {
+ PATHD_FORMAT("%*saddr_v6: undefined", ps2, "");
+ }
+ PATHD_FORMAT("%*sport: %i\n", ps2, "", opts->port);
+ PATHD_FORMAT("%*smsd: %i\n", ps2, "", opts->msd);
+ }
+}
+
+void _format_pce_opts(int ps, struct pce_opts *opts)
+{
+ if (opts == NULL) {
+ PATHD_FORMAT("NULL\n");
+ } else {
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ PATHD_FORMAT("\n");
+ if (IS_IPADDR_V6(&opts->addr)) {
+ PATHD_FORMAT("%*saddr: %pI6\n", ps2, "",
+ &opts->addr.ipaddr_v6);
+ } else {
+ PATHD_FORMAT("%*saddr: %pI4\n", ps2, "",
+ &opts->addr.ipaddr_v4);
+ }
+ PATHD_FORMAT("%*sport: %i\n", ps2, "", opts->port);
+ }
+}
+
+void _format_pcc_caps(int ps, struct pcep_caps *caps)
+{
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ PATHD_FORMAT("\n");
+ PATHD_FORMAT("%*sis_stateful: %d\n", ps2, "", caps->is_stateful);
+}
+
+void _format_pcc_state(int ps, struct pcc_state *state)
+{
+ if (state == NULL) {
+ PATHD_FORMAT("NULL\n");
+ } else {
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ PATHD_FORMAT("\n");
+ PATHD_FORMAT("%*sstatus: %s\n", ps2, "",
+ pcc_status_name(state->status));
+ PATHD_FORMAT("%*spcc_opts: ", ps2, "");
+ _format_pcc_opts(ps2, state->pcc_opts);
+ PATHD_FORMAT("%*spce_opts: ", ps2, "");
+ _format_pce_opts(ps2, state->pce_opts);
+ if (state->sess == NULL) {
+ PATHD_FORMAT("%*ssess: NULL\n", ps2, "");
+ } else {
+ PATHD_FORMAT("%*ssess: <PCC SESSION %p>\n", ps2, "",
+ state->sess);
+ }
+ PATHD_FORMAT("%*scaps: ", ps2, "");
+ _format_pcc_caps(ps2, &state->caps);
+ }
+}
+
+void _format_ctrl_state(int ps, struct ctrl_state *state)
+{
+ if (state == NULL) {
+ PATHD_FORMAT("NULL\n");
+ } else {
+ int i;
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ int ps3 = ps2 + DEBUG_IDENT_SIZE;
+ PATHD_FORMAT("\n");
+ if (state->main == NULL) {
+ PATHD_FORMAT("%*smain: NULL\n", ps2, "");
+ } else {
+ PATHD_FORMAT("%*smain: <THREAD MASTER %p>\n", ps2, "",
+ state->main);
+ }
+ if (state->self == NULL) {
+ PATHD_FORMAT("%*sself: NULL\n", ps2, "");
+ } else {
+ PATHD_FORMAT("%*sself: <THREAD MASTER %p>\n", ps2, "",
+ state->self);
+ }
+ PATHD_FORMAT("%*spcc_count: %d\n", ps2, "", state->pcc_count);
+ PATHD_FORMAT("%*spcc:\n", ps2, "");
+ for (i = 0; i < MAX_PCC; i++) {
+ if (state->pcc[i]) {
+ PATHD_FORMAT("%*s- ", ps3 - 2, "");
+ _format_pcc_state(ps3, state->pcc[i]);
+ }
+ }
+ }
+}
+
+void _format_path(int ps, struct path *path)
+{
+ if (path == NULL) {
+ PATHD_FORMAT("NULL\n");
+ } else {
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ int ps3 = ps2 + DEBUG_IDENT_SIZE;
+ PATHD_FORMAT("\n");
+ PATHD_FORMAT("%*snbkey: \n", ps2, "");
+ PATHD_FORMAT("%*scolor: %u\n", ps3, "", path->nbkey.color);
+ switch (path->nbkey.endpoint.ipa_type) {
+ case IPADDR_V4:
+ PATHD_FORMAT("%*sendpoint: %pI4\n", ps3, "",
+ &path->nbkey.endpoint.ipaddr_v4);
+ break;
+ case IPADDR_V6:
+ PATHD_FORMAT("%*sendpoint: %pI6\n", ps3, "",
+ &path->nbkey.endpoint.ipaddr_v6);
+ break;
+ default:
+ PATHD_FORMAT("%*sendpoint: NONE\n", ps3, "");
+ break;
+ }
+ PATHD_FORMAT("%*spreference: %u\n", ps3, "",
+ path->nbkey.preference);
+
+ if (path->sender.ipa_type == IPADDR_V4) {
+ PATHD_FORMAT("%*ssender: %pI4\n", ps2, "",
+ &path->sender.ipaddr_v4);
+ } else if (path->sender.ipa_type == IPADDR_V6) {
+ PATHD_FORMAT("%*ssender: %pI6\n", ps2, "",
+ &path->sender.ipaddr_v6);
+ } else {
+ PATHD_FORMAT("%*ssender: UNDEFINED\n", ps2, "");
+ }
+ if (path->pcc_addr.ipa_type == IPADDR_V4) {
+ PATHD_FORMAT("%*spcc_addr: %pI4\n", ps2, "",
+ &path->pcc_addr.ipaddr_v4);
+ } else if (path->pcc_addr.ipa_type == IPADDR_V6) {
+ PATHD_FORMAT("%*spcc_addr: %pI6\n", ps2, "",
+ &path->pcc_addr.ipaddr_v6);
+ } else {
+ PATHD_FORMAT("%*spcc_addr: UNDEFINED\n", ps2, "");
+ }
+ PATHD_FORMAT("%*spcc_id: %u\n", ps2, "", path->pcc_id);
+ PATHD_FORMAT("%*screate_origin: %s (%u)\n", ps2, "",
+ srte_protocol_origin_name(path->create_origin),
+ path->create_origin);
+ PATHD_FORMAT("%*supdate_origin: %s (%u)\n", ps2, "",
+ srte_protocol_origin_name(path->update_origin),
+ path->update_origin);
+ if (path->originator != NULL) {
+ PATHD_FORMAT("%*soriginator: %s\n", ps2, "",
+ path->originator);
+ } else {
+ PATHD_FORMAT("%*soriginator: UNDEFINED\n", ps2, "");
+ }
+ PATHD_FORMAT("%*stype: %s (%u)\n", ps2, "",
+ srte_candidate_type_name(path->type), path->type);
+ PATHD_FORMAT("%*splsp_id: %u\n", ps2, "", path->plsp_id);
+ if (path->name == NULL) {
+ PATHD_FORMAT("%*sname: NULL\n", ps2, "");
+ } else {
+ PATHD_FORMAT("%*sname: %s\n", ps2, "", path->name);
+ }
+ PATHD_FORMAT("%*ssrp_id: %u\n", ps2, "", path->srp_id);
+ PATHD_FORMAT("%*sreq_id: %u\n", ps2, "", path->req_id);
+ PATHD_FORMAT("%*sstatus: %s (%u)\n", ps2, "",
+ pcep_lsp_status_name(path->status), path->status);
+ PATHD_FORMAT("%*sdo_remove: %u\n", ps2, "", path->do_remove);
+ PATHD_FORMAT("%*sgo_active: %u\n", ps2, "", path->go_active);
+ PATHD_FORMAT("%*swas_created: %u\n", ps2, "",
+ path->was_created);
+ PATHD_FORMAT("%*swas_removed: %u\n", ps2, "",
+ path->was_removed);
+ PATHD_FORMAT("%*sis_synching: %u\n", ps2, "",
+ path->is_synching);
+ PATHD_FORMAT("%*sis_delegated: %u\n", ps2, "",
+ path->is_delegated);
+ PATHD_FORMAT("%*shas_bandwidth: %u\n", ps2, "",
+ path->has_bandwidth);
+ if (path->has_bandwidth) {
+ PATHD_FORMAT("%*senforce_bandwidth: %u\n", ps2, "",
+ path->enforce_bandwidth);
+ PATHD_FORMAT("%*sbandwidth: %f\n", ps2, "",
+ path->bandwidth);
+ }
+ PATHD_FORMAT("%*shas_pcc_objfun: %u\n", ps2, "",
+ path->has_pcc_objfun);
+ if (path->has_pcc_objfun) {
+ PATHD_FORMAT("%*senforce_pcc_objfun: %d\n", ps2, "",
+ path->enforce_pcc_objfun);
+ PATHD_FORMAT("%*spcc_objfun: %s (%u)\n", ps2, "",
+ objfun_type_name(path->pcc_objfun),
+ path->pcc_objfun);
+ }
+ PATHD_FORMAT("%*shas_pce_objfun: %u\n", ps2, "",
+ path->has_pce_objfun);
+ if (path->has_pce_objfun)
+ PATHD_FORMAT("%*spce_objfun: %s (%u)\n", ps2, "",
+ objfun_type_name(path->pce_objfun),
+ path->pce_objfun);
+ PATHD_FORMAT("%*shas_affinity_filters: %u\n", ps2, "",
+ path->has_affinity_filters);
+ if (path->has_affinity_filters) {
+ PATHD_FORMAT("%*sexclude_any: 0x%08x\n", ps2, "",
+ path->affinity_filters
+ [AFFINITY_FILTER_EXCLUDE_ANY - 1]);
+ PATHD_FORMAT("%*sinclude_any: 0x%08x\n", ps2, "",
+ path->affinity_filters
+ [AFFINITY_FILTER_INCLUDE_ANY - 1]);
+ PATHD_FORMAT("%*sinclude_all: 0x%08x\n", ps2, "",
+ path->affinity_filters
+ [AFFINITY_FILTER_INCLUDE_ALL - 1]);
+ }
+
+ if (path->first_hop == NULL) {
+ PATHD_FORMAT("%*shops: []\n", ps2, "");
+ } else {
+ PATHD_FORMAT("%*shops: \n", ps2, "");
+ for (struct path_hop *hop = path->first_hop;
+ hop != NULL; hop = hop->next) {
+ PATHD_FORMAT("%*s- ", ps3 - 2, "");
+ _format_path_hop(ps3, hop);
+ }
+ }
+ if (path->first_metric == NULL) {
+ PATHD_FORMAT("%*smetrics: []\n", ps2, "");
+ } else {
+ PATHD_FORMAT("%*smetrics: \n", ps2, "");
+ for (struct path_metric *metric = path->first_metric;
+ NULL != metric; metric = metric->next) {
+ PATHD_FORMAT("%*s- ", ps3 - 2, "");
+ _format_path_metric(ps3, metric);
+ }
+ }
+ }
+}
+
+void _format_path_metric(int ps, struct path_metric *metric)
+{
+ PATHD_FORMAT("type: %s (%u)\n", pcep_metric_type_name(metric->type),
+ metric->type);
+ PATHD_FORMAT("%*senforce: %u\n", ps, "", metric->enforce);
+ PATHD_FORMAT("%*sis_bound: %u\n", ps, "", metric->is_bound);
+ PATHD_FORMAT("%*sis_computed: %u\n", ps, "", metric->is_computed);
+ PATHD_FORMAT("%*svalue: %f\n", ps, "", metric->value);
+}
+
+void _format_path_hop(int ps, struct path_hop *hop)
+{
+ PATHD_FORMAT("is_loose: %u\n", hop->is_loose);
+ PATHD_FORMAT("%*shas_sid: %u\n", ps, "", hop->has_sid);
+
+ if (hop->has_sid) {
+ PATHD_FORMAT("%*sis_mpls: %u\n", ps, "", hop->is_mpls);
+ if (hop->is_mpls) {
+ PATHD_FORMAT("%*shas_attribs: %u\n", ps, "",
+ hop->has_attribs);
+ PATHD_FORMAT("%*slabel: %u\n", ps, "",
+ hop->sid.mpls.label);
+ if (hop->has_attribs) {
+ PATHD_FORMAT("%*straffic_class: %u\n", ps, "",
+ hop->sid.mpls.traffic_class);
+ PATHD_FORMAT("%*sis_bottom: %u\n", ps, "",
+ hop->sid.mpls.is_bottom);
+ PATHD_FORMAT("%*sttl: %u\n", ps, "",
+ hop->sid.mpls.ttl);
+ }
+ } else {
+ PATHD_FORMAT("%*sSID: %u\n", ps, "", hop->sid.value);
+ }
+ }
+
+ PATHD_FORMAT("%*shas_nai: %u\n", ps, "", hop->has_nai);
+ if (hop->has_nai) {
+ PATHD_FORMAT("%*snai_type: %s (%u)\n", ps, "",
+ pcep_nai_type_name(hop->nai.type), hop->nai.type);
+ switch (hop->nai.type) {
+ case PCEP_SR_SUBOBJ_NAI_IPV4_NODE:
+ PATHD_FORMAT("%*sNAI: %pI4\n", ps, "",
+ &hop->nai.local_addr.ipaddr_v4);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV6_NODE:
+ PATHD_FORMAT("%*sNAI: %pI6\n", ps, "",
+ &hop->nai.local_addr.ipaddr_v6);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY:
+ PATHD_FORMAT("%*sNAI: %pI4/%pI4\n", ps, "",
+ &hop->nai.local_addr.ipaddr_v4,
+ &hop->nai.remote_addr.ipaddr_v4);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY:
+ PATHD_FORMAT("%*sNAI: %pI6/%pI6\n", ps, "",
+ &hop->nai.local_addr.ipaddr_v6,
+ &hop->nai.remote_addr.ipaddr_v6);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY:
+ PATHD_FORMAT("%*sNAI: %pI4(%u)/%pI4(%u)\n", ps, "",
+ &hop->nai.local_addr.ipaddr_v6,
+ hop->nai.local_iface,
+ &hop->nai.remote_addr.ipaddr_v6,
+ hop->nai.remote_iface);
+ break;
+ default:
+ PATHD_FORMAT("%*sNAI: UNSUPPORTED\n", ps, "");
+ break;
+ }
+ }
+}
+
+void _format_pcep_event(int ps, pcep_event *event)
+{
+ if (event == NULL) {
+ PATHD_FORMAT("NULL\n");
+ } else {
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ PATHD_FORMAT("\n");
+ PATHD_FORMAT("%*sevent_type: %s\n", ps2, "",
+ pcep_event_type_name(event->event_type));
+ PATHD_FORMAT("%*sevent_time: %s", ps2, "",
+ ctime(&event->event_time));
+ if (event->session == NULL) {
+ PATHD_FORMAT("%*ssession: NULL\n", ps2, "");
+ } else {
+ PATHD_FORMAT("%*ssession: <PCC SESSION %p>\n", ps2, "",
+ event->session);
+ }
+ PATHD_FORMAT("%*smessage: ", ps2, "");
+ _format_pcep_message(ps2, event->message);
+ }
+}
+
+void _format_pcep_message(int ps, struct pcep_message *msg)
+{
+ if (msg == NULL) {
+ PATHD_FORMAT("NULL\n");
+ } else {
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ PATHD_FORMAT("\n");
+ PATHD_FORMAT("%*spcep_version: %u\n", ps2, "",
+ msg->msg_header->pcep_version);
+ PATHD_FORMAT("%*stype: %s (%u)\n", ps2, "",
+ pcep_message_type_name(msg->msg_header->type),
+ msg->msg_header->type);
+ PATHD_FORMAT("%*sobjects: ", ps2, "");
+ _format_pcep_objects(ps2, msg->obj_list);
+ }
+}
+
+void _format_pcep_objects(int ps, double_linked_list *objs)
+{
+ if (objs == NULL) {
+ PATHD_FORMAT("NULL\n");
+ } else {
+ double_linked_list_node *node;
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ int i;
+
+ if (objs->num_entries == 0) {
+ PATHD_FORMAT("[]\n");
+ return;
+ }
+
+ PATHD_FORMAT("\n");
+ for (node = objs->head, i = 0; node != NULL;
+ node = node->next_node, i++) {
+ struct pcep_object_header *obj =
+ (struct pcep_object_header *)node->data;
+ PATHD_FORMAT("%*s- ", ps2 - 2, "");
+ _format_pcep_object(ps2, obj);
+ }
+ }
+}
+
+void _format_pcep_object(int ps, struct pcep_object_header *obj)
+{
+ if (obj == NULL) {
+ PATHD_FORMAT("NULL\n");
+ } else {
+ PATHD_FORMAT("object_class: %s (%u)\n",
+ pcep_object_class_name(obj->object_class),
+ obj->object_class);
+ PATHD_FORMAT("%*sobject_type: %s (%u)\n", ps, "",
+ pcep_object_type_name(obj->object_class,
+ obj->object_type),
+ obj->object_type);
+ PATHD_FORMAT("%*sflag_p: %u\n", ps, "", obj->flag_p);
+ PATHD_FORMAT("%*sflag_i: %u\n", ps, "", obj->flag_i);
+ _format_pcep_object_details(ps, obj);
+ _format_pcep_object_tlvs(ps, obj);
+ }
+}
+
+void _format_pcep_object_details(int ps, struct pcep_object_header *obj)
+{
+ switch (TUP(obj->object_class, obj->object_type)) {
+ case TUP(PCEP_OBJ_CLASS_ERROR, PCEP_OBJ_TYPE_ERROR):
+ _format_pcep_object_error(ps, (struct pcep_object_error *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN):
+ _format_pcep_object_open(ps, (struct pcep_object_open *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_RP, PCEP_OBJ_TYPE_RP):
+ _format_pcep_object_rp(ps, (struct pcep_object_rp *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_SRP, PCEP_OBJ_TYPE_SRP):
+ _format_pcep_object_srp(ps, (struct pcep_object_srp *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_LSP, PCEP_OBJ_TYPE_LSP):
+ _format_pcep_object_lsp(ps, (struct pcep_object_lsp *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_LSPA, PCEP_OBJ_TYPE_LSPA):
+ _format_pcep_object_lspa(ps, (struct pcep_object_lspa *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV4):
+ _format_pcep_object_ipv4_endpoint(
+ ps, (struct pcep_object_endpoints_ipv4 *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO):
+ _format_pcep_object_ro(ps, (struct pcep_object_ro *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC):
+ _format_pcep_object_metric(ps,
+ (struct pcep_object_metric *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_REQ):
+ case TUP(PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_CISCO):
+ _format_pcep_object_bandwidth(
+ ps, (struct pcep_object_bandwidth *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH):
+ _format_pcep_object_nopath(ps,
+ (struct pcep_object_nopath *)obj);
+ break;
+ case TUP(PCEP_OBJ_CLASS_OF, PCEP_OBJ_TYPE_OF):
+ _format_pcep_object_objfun(
+ ps, (struct pcep_object_objective_function *)obj);
+ break;
+ default:
+ PATHD_FORMAT("%*s...\n", ps, "");
+ break;
+ }
+}
+
+void _format_pcep_object_error(int ps, struct pcep_object_error *obj)
+{
+ PATHD_FORMAT("%*serror_type: %s (%u)\n", ps, "",
+ pcep_error_type_name(obj->error_type), obj->error_type);
+ PATHD_FORMAT("%*serror_value: %s (%u)\n", ps, "",
+ pcep_error_value_name(obj->error_type, obj->error_value),
+ obj->error_value);
+}
+
+
+void _format_pcep_object_open(int ps, struct pcep_object_open *obj)
+{
+ PATHD_FORMAT("%*sopen_version: %u\n", ps, "", obj->open_version);
+ PATHD_FORMAT("%*sopen_keepalive: %u\n", ps, "", obj->open_keepalive);
+ PATHD_FORMAT("%*sopen_deadtimer: %u\n", ps, "", obj->open_deadtimer);
+ PATHD_FORMAT("%*sopen_sid: %u\n", ps, "", obj->open_sid);
+}
+
+void _format_pcep_object_rp(int ps, struct pcep_object_rp *obj)
+{
+ PATHD_FORMAT("%*spriority: %u\n", ps, "", obj->priority);
+ PATHD_FORMAT("%*sflag_reoptimization: %u\n", ps, "",
+ obj->flag_reoptimization);
+ PATHD_FORMAT("%*sflag_bidirectional: %u\n", ps, "",
+ obj->flag_bidirectional);
+ PATHD_FORMAT("%*sflag_strict: %u\n", ps, "", obj->flag_strict);
+ PATHD_FORMAT("%*sflag_of: %u\n", ps, "", obj->flag_of);
+ PATHD_FORMAT("%*srequest_id: %u\n", ps, "", obj->request_id);
+}
+
+
+void _format_pcep_object_srp(int ps, struct pcep_object_srp *obj)
+{
+ PATHD_FORMAT("%*sflag_lsp_remove: %u\n", ps, "", obj->flag_lsp_remove);
+ PATHD_FORMAT("%*ssrp_id_number: %u\n", ps, "", obj->srp_id_number);
+}
+
+void _format_pcep_object_lsp(int ps, struct pcep_object_lsp *obj)
+{
+ PATHD_FORMAT("%*splsp_id: %u\n", ps, "", obj->plsp_id);
+ PATHD_FORMAT("%*sstatus: %s\n", ps, "",
+ pcep_lsp_status_name(obj->operational_status));
+ PATHD_FORMAT("%*sflag_d: %u\n", ps, "", obj->flag_d);
+ PATHD_FORMAT("%*sflag_s: %u\n", ps, "", obj->flag_s);
+ PATHD_FORMAT("%*sflag_r: %u\n", ps, "", obj->flag_r);
+ PATHD_FORMAT("%*sflag_a: %u\n", ps, "", obj->flag_a);
+ PATHD_FORMAT("%*sflag_c: %u\n", ps, "", obj->flag_c);
+}
+
+void _format_pcep_object_lspa(int ps, struct pcep_object_lspa *obj)
+{
+ PATHD_FORMAT("%*slspa_exclude_any: 0x%08x\n", ps, "",
+ obj->lspa_exclude_any);
+ PATHD_FORMAT("%*slspa_include_any: 0x%08x\n", ps, "",
+ obj->lspa_include_any);
+ PATHD_FORMAT("%*slspa_include_all: 0x%08x\n", ps, "",
+ obj->lspa_include_all);
+ PATHD_FORMAT("%*ssetup_priority: %u\n", ps, "", obj->setup_priority);
+ PATHD_FORMAT("%*sholding_priority: %u\n", ps, "",
+ obj->holding_priority);
+ PATHD_FORMAT("%*sflag_local_protection: %u\n", ps, "",
+ obj->flag_local_protection);
+}
+
+void _format_pcep_object_ipv4_endpoint(int ps,
+ struct pcep_object_endpoints_ipv4 *obj)
+{
+ PATHD_FORMAT("%*ssrc_ipv4: %pI4\n", ps, "", &obj->src_ipv4);
+ PATHD_FORMAT("%*sdst_ipv4: %pI4\n", ps, "", &obj->dst_ipv4);
+}
+
+void _format_pcep_object_metric(int ps, struct pcep_object_metric *obj)
+{
+ PATHD_FORMAT("%*stype: %s (%u)\n", ps, "",
+ pcep_metric_type_name(obj->type), obj->type);
+ PATHD_FORMAT("%*sflag_b: %u\n", ps, "", obj->flag_b);
+ PATHD_FORMAT("%*sflag_c: %u\n", ps, "", obj->flag_c);
+ PATHD_FORMAT("%*svalue: %f\n", ps, "", obj->value);
+}
+
+void _format_pcep_object_bandwidth(int ps, struct pcep_object_bandwidth *obj)
+{
+ PATHD_FORMAT("%*sbandwidth: %f\n", ps, "", obj->bandwidth);
+}
+
+void _format_pcep_object_nopath(int ps, struct pcep_object_nopath *obj)
+{
+ PATHD_FORMAT("%*sni: %u\n", ps, "", obj->ni);
+ PATHD_FORMAT("%*sflag_c: %u\n", ps, "", obj->flag_c);
+ PATHD_FORMAT("%*serr_code: %s (%u)\n", ps, "",
+ pcep_nopath_tlv_err_code_name(obj->err_code),
+ obj->err_code);
+}
+
+void _format_pcep_object_objfun(int ps,
+ struct pcep_object_objective_function *obj)
+{
+ PATHD_FORMAT("%*sof_code: %s (%u)\n", ps, "",
+ objfun_type_name(obj->of_code), obj->of_code);
+}
+
+void _format_pcep_object_ro(int ps, struct pcep_object_ro *obj)
+{
+ double_linked_list *obj_list = obj->sub_objects;
+ double_linked_list_node *node;
+ struct pcep_object_ro_subobj *sub_obj;
+
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ int i;
+
+ if ((obj_list == NULL) || (obj_list->num_entries == 0)) {
+ PATHD_FORMAT("%*ssub_objects: []\n", ps, "");
+ return;
+ }
+
+ PATHD_FORMAT("%*ssub_objects:\n", ps, "");
+
+ for (node = obj_list->head, i = 0; node != NULL;
+ node = node->next_node, i++) {
+ sub_obj = (struct pcep_object_ro_subobj *)node->data;
+ PATHD_FORMAT("%*s- flag_subobj_loose_hop: %u\n", ps2 - 2, "",
+ sub_obj->flag_subobj_loose_hop);
+ PATHD_FORMAT("%*sro_subobj_type: %s (%u)\n", ps2, "",
+ pcep_ro_type_name(sub_obj->ro_subobj_type),
+ sub_obj->ro_subobj_type);
+ _format_pcep_object_ro_details(ps2, sub_obj);
+ }
+}
+
+void _format_pcep_object_ro_details(int ps, struct pcep_object_ro_subobj *ro)
+{
+ switch (ro->ro_subobj_type) {
+ case RO_SUBOBJ_TYPE_IPV4:
+ _format_pcep_object_ro_ipv4(ps,
+ (struct pcep_ro_subobj_ipv4 *)ro);
+ break;
+ case RO_SUBOBJ_TYPE_SR:
+ _format_pcep_object_ro_sr(ps, (struct pcep_ro_subobj_sr *)ro);
+ break;
+ default:
+ PATHD_FORMAT("%*s...\n", ps, "");
+ break;
+ }
+}
+
+void _format_pcep_object_ro_ipv4(int ps, struct pcep_ro_subobj_ipv4 *obj)
+{
+ PATHD_FORMAT("%*sip_addr: %pI4\n", ps, "", &obj->ip_addr);
+ PATHD_FORMAT("%*sprefix_length: %u\n", ps, "", obj->prefix_length);
+ PATHD_FORMAT("%*sflag_local_protection: %u\n", ps, "",
+ obj->flag_local_protection);
+}
+
+void _format_pcep_object_ro_sr(int ps, struct pcep_ro_subobj_sr *obj)
+{
+ PATHD_FORMAT("%*snai_type = %s (%u)\n", ps, "",
+ pcep_nai_type_name(obj->nai_type), obj->nai_type);
+ PATHD_FORMAT("%*sflag_f: %u\n", ps, "", obj->flag_f);
+ PATHD_FORMAT("%*sflag_s: %u\n", ps, "", obj->flag_s);
+ PATHD_FORMAT("%*sflag_c: %u\n", ps, "", obj->flag_c);
+ PATHD_FORMAT("%*sflag_m: %u\n", ps, "", obj->flag_m);
+
+ if (!obj->flag_s) {
+ PATHD_FORMAT("%*sSID: %u\n", ps, "", obj->sid);
+ if (obj->flag_m) {
+ PATHD_FORMAT("%*slabel: %u\n", ps, "",
+ GET_SR_ERO_SID_LABEL(obj->sid));
+ if (obj->flag_c) {
+ PATHD_FORMAT("%*sTC: %u\n", ps, "",
+ GET_SR_ERO_SID_TC(obj->sid));
+ PATHD_FORMAT("%*sS: %u\n", ps, "",
+ GET_SR_ERO_SID_S(obj->sid));
+ PATHD_FORMAT("%*sTTL: %u\n", ps, "",
+ GET_SR_ERO_SID_TTL(obj->sid));
+ }
+ }
+ }
+
+ if (!obj->flag_f) {
+ struct in_addr *laddr4, *raddr4;
+ struct in6_addr *laddr6, *raddr6;
+ uint32_t *liface, *riface;
+ assert(obj->nai_list != NULL);
+ double_linked_list_node *n = obj->nai_list->head;
+ assert(n != NULL);
+ assert(n->data != NULL);
+ switch (obj->nai_type) {
+ case PCEP_SR_SUBOBJ_NAI_IPV4_NODE:
+ laddr4 = (struct in_addr *)n->data;
+ PATHD_FORMAT("%*sNAI: %pI4\n", ps, "", laddr4);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV6_NODE:
+ laddr6 = (struct in6_addr *)n->data;
+ PATHD_FORMAT("%*sNAI: %pI6\n", ps, "", laddr6);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY:
+ assert(n->next_node != NULL);
+ assert(n->next_node->data != NULL);
+ laddr4 = (struct in_addr *)n->data;
+ raddr4 = (struct in_addr *)n->next_node->data;
+ PATHD_FORMAT("%*sNAI: %pI4/%pI4\n", ps, "", laddr4,
+ raddr4);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY:
+ assert(n->next_node != NULL);
+ assert(n->next_node->data != NULL);
+ laddr6 = (struct in6_addr *)n->data;
+ raddr6 = (struct in6_addr *)n->next_node->data;
+ PATHD_FORMAT("%*sNAI: %pI6/%pI6\n", ps, "", laddr6,
+ raddr6);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY:
+ laddr4 = (struct in_addr *)n->data;
+ n = n->next_node;
+ assert(n != NULL);
+ assert(n->data != NULL);
+ liface = (uint32_t *)n->data;
+ n = n->next_node;
+ assert(n != NULL);
+ assert(n->data != NULL);
+ raddr4 = (struct in_addr *)n->data;
+ assert(n != NULL);
+ assert(n->data != NULL);
+ riface = (uint32_t *)n->data;
+ PATHD_FORMAT("%*sNAI: %pI4(%u)/%pI4(%u)\n", ps, "",
+ laddr4, *liface, raddr4, *riface);
+ break;
+ default:
+ PATHD_FORMAT("%*sNAI: UNSUPPORTED\n", ps, "");
+ break;
+ }
+ }
+}
+
+void _format_pcep_object_tlvs(int ps, struct pcep_object_header *obj)
+{
+ double_linked_list *tlv_list = obj->tlv_list;
+ struct pcep_object_tlv_header *tlv;
+ double_linked_list_node *node;
+ int ps2 = ps + DEBUG_IDENT_SIZE;
+ int i = 0;
+
+ if (tlv_list == NULL)
+ return;
+ if (tlv_list->num_entries == 0) {
+ PATHD_FORMAT("%*stlvs: []\n", ps, "");
+ return;
+ }
+
+ PATHD_FORMAT("%*stlvs:\n", ps, "");
+
+ for (node = tlv_list->head, i = 0; node != NULL;
+ node = node->next_node, i++) {
+ tlv = (struct pcep_object_tlv_header *)node->data;
+ PATHD_FORMAT("%*s- ", ps2 - 2, "");
+ _format_pcep_object_tlv(ps2, tlv);
+ }
+}
+
+void _format_pcep_object_tlv(int ps, struct pcep_object_tlv_header *tlv_header)
+{
+ PATHD_FORMAT("type: %s (%u)\n", pcep_tlv_type_name(tlv_header->type),
+ tlv_header->type);
+ _format_pcep_object_tlv_details(ps, tlv_header);
+}
+
+void _format_pcep_object_tlv_details(int ps,
+ struct pcep_object_tlv_header *tlv_header)
+{
+ switch (tlv_header->type) {
+ case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
+ _format_pcep_object_tlv_symbolic_path_name(
+ ps, (struct pcep_object_tlv_symbolic_path_name *)
+ tlv_header);
+ break;
+ case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
+ _format_pcep_object_tlv_stateful_pce_capability(
+ ps, (struct pcep_object_tlv_stateful_pce_capability *)
+ tlv_header);
+ break;
+ case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY:
+ _format_pcep_object_tlv_sr_pce_capability(
+ ps,
+ (struct pcep_object_tlv_sr_pce_capability *)tlv_header);
+ break;
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE:
+ _format_pcep_object_tlv_path_setup_type(
+ ps,
+ (struct pcep_object_tlv_path_setup_type *)tlv_header);
+ break;
+ default:
+ PATHD_FORMAT("%*s...\n", ps, "");
+ break;
+ }
+}
+
+void _format_pcep_object_tlv_symbolic_path_name(
+ int ps, struct pcep_object_tlv_symbolic_path_name *tlv)
+{
+ PATHD_FORMAT("%*ssymbolic_path_name: %.*s\n", ps, "",
+ tlv->symbolic_path_name_length, tlv->symbolic_path_name);
+}
+
+void _format_pcep_object_tlv_stateful_pce_capability(
+ int ps, struct pcep_object_tlv_stateful_pce_capability *tlv)
+{
+ PATHD_FORMAT("%*sflag_u_lsp_update_capability: %u\n", ps, "",
+ tlv->flag_u_lsp_update_capability);
+ PATHD_FORMAT("%*sflag_s_include_db_version: %u\n", ps, "",
+ tlv->flag_s_include_db_version);
+ PATHD_FORMAT("%*sflag_i_lsp_instantiation_capability: %u\n", ps, "",
+ tlv->flag_i_lsp_instantiation_capability);
+ PATHD_FORMAT("%*sflag_t_triggered_resync: %u\n", ps, "",
+ tlv->flag_t_triggered_resync);
+ PATHD_FORMAT("%*sflag_d_delta_lsp_sync: %u\n", ps, "",
+ tlv->flag_d_delta_lsp_sync);
+ PATHD_FORMAT("%*sflag_f_triggered_initial_sync: %u\n", ps, "",
+ tlv->flag_f_triggered_initial_sync);
+}
+
+void _format_pcep_object_tlv_sr_pce_capability(
+ int ps, struct pcep_object_tlv_sr_pce_capability *tlv)
+{
+
+ PATHD_FORMAT("%*sflag_n: %u\n", ps, "", tlv->flag_n);
+ PATHD_FORMAT("%*sflag_x: %u\n", ps, "", tlv->flag_x);
+ PATHD_FORMAT("%*smax_sid_depth: %u\n", ps, "", tlv->max_sid_depth);
+}
+
+void _format_pcep_object_tlv_path_setup_type(
+ int ps, struct pcep_object_tlv_path_setup_type *tlv)
+{
+ PATHD_FORMAT("%*spath_setup_type: %u\n", ps, "", tlv->path_setup_type);
+}
diff --git a/pathd/path_pcep_debug.h b/pathd/path_pcep_debug.h
new file mode 100644
index 0000000000..68b29ab657
--- /dev/null
+++ b/pathd/path_pcep_debug.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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_DEBUG_H_
+#define _PATH_PCEP_DEBUG_H_
+
+#include "pathd/path_debug.h"
+#include <pcep_pcc_api.h>
+#include <pcep-objects.h>
+#include "pathd/path_pcep.h"
+#include "pathd/path_pcep_controller.h"
+#include "pathd/path_pcep_pcc.h"
+#include "pathd/path_pcep_lib.h"
+
+const char *pcc_status_name(enum pcc_status status);
+
+const char *pcep_error_type_name(enum pcep_error_type error_type);
+const char *pcep_error_value_name(enum pcep_error_type error_type,
+ enum pcep_error_value error_value);
+const char *pcep_event_type_name(pcep_event_type event_type);
+const char *pcep_message_type_name(enum pcep_message_types pcep_message_type);
+const char *pcep_object_class_name(enum pcep_object_classes obj_class);
+const char *pcep_object_type_name(enum pcep_object_classes obj_class,
+ enum pcep_object_types obj_type);
+const char *pcep_lsp_status_name(enum pcep_lsp_operational_status status);
+const char *pcep_tlv_type_name(enum pcep_object_tlv_types tlv_type);
+const char *pcep_ro_type_name(enum pcep_ro_subobj_types ro_type);
+const char *pcep_nai_type_name(enum pcep_sr_subobj_nai nai_type);
+const char *pcep_metric_type_name(enum pcep_metric_types type);
+const char *pcep_nopath_tlv_err_code_name(enum pcep_nopath_tlv_err_codes code);
+
+const char *format_objfun_set(uint32_t flags);
+const char *format_pcc_opts(struct pcc_opts *ops);
+const char *format_pcc_state(struct pcc_state *state);
+const char *format_ctrl_state(struct ctrl_state *state);
+const char *format_path(struct path *path);
+const char *format_pcep_event(pcep_event *event);
+const char *format_pcep_message(struct pcep_message *msg);
+const char *format_yang_dnode(struct lyd_node *dnode);
+
+#endif // _PATH_PCEP_DEBUG_H_
diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c
new file mode 100644
index 0000000000..bb6bfb1336
--- /dev/null
+++ b/pathd/path_pcep_lib.c
@@ -0,0 +1,1146 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <debug.h>
+#include <pcep_utils_counters.h>
+#include <pcep_timers.h>
+#include "pathd/path_errors.h"
+#include "pathd/path_memory.h"
+#include "pathd/path_pcep.h"
+#include "pathd/path_pcep_lib.h"
+#include "pathd/path_pcep_debug.h"
+#include "pathd/path_pcep_memory.h"
+
+#define CLASS_TYPE(CLASS, TYPE) (((CLASS) << 16) | (TYPE))
+#define DEFAULT_LSAP_SETUP_PRIO 4
+#define DEFAULT_LSAP_HOLDING_PRIO 4
+#define DEFAULT_LSAP_LOCAL_PRETECTION false
+
+/* pceplib logging callback */
+static int pceplib_logging_cb(int level, const char *fmt, va_list args);
+
+/* Socket callbacks */
+static int pcep_lib_pceplib_socket_read_cb(void *fpt, void **thread, int fd,
+ void *payload);
+static int pcep_lib_pceplib_socket_write_cb(void *fpt, void **thread, int fd,
+ void *payload);
+static int pcep_lib_socket_read_ready(struct thread *thread);
+static int pcep_lib_socket_write_ready(struct thread *thread);
+
+/* pceplib pcep_event callbacks */
+static void pcep_lib_pceplib_event_cb(void *fpt, pcep_event *event);
+
+/* pceplib pthread creation callback */
+static int pcep_lib_pthread_create_cb(pthread_t *pthread_id,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void *),
+ void *data, const char *thread_name);
+void *pcep_lib_pthread_start_passthrough(void *data);
+int pcep_lib_pthread_stop_cb(struct frr_pthread *, void **);
+
+/* Internal functions */
+static double_linked_list *pcep_lib_format_path(struct pcep_caps *caps,
+ struct path *path);
+static void pcep_lib_format_constraints(struct path *path,
+ double_linked_list *objs);
+static void pcep_lib_parse_open(struct pcep_caps *caps,
+ struct pcep_object_open *open);
+static void
+pcep_lib_parse_open_pce_capability(struct pcep_caps *caps,
+ struct pcep_object_tlv_header *tlv_header);
+static void
+pcep_lib_parse_open_objfun_list(struct pcep_caps *caps,
+ struct pcep_object_tlv_header *tlv_header);
+static void pcep_lib_parse_rp(struct path *path, struct pcep_object_rp *rp);
+static void pcep_lib_parse_srp(struct path *path, struct pcep_object_srp *srp);
+static void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp);
+static void pcep_lib_parse_lspa(struct path *path,
+ struct pcep_object_lspa *lspa);
+static void pcep_lib_parse_metric(struct path *path,
+ struct pcep_object_metric *obj);
+static void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero);
+static struct path_hop *pcep_lib_parse_ero_sr(struct path_hop *next,
+ struct pcep_ro_subobj_sr *sr);
+static struct counters_group *copy_counter_group(struct counters_group *from);
+static struct counters_subgroup *
+copy_counter_subgroup(struct counters_subgroup *from);
+static struct counter *copy_counter(struct counter *from);
+static void free_counter_group(struct counters_group *group);
+static void free_counter_subgroup(struct counters_subgroup *subgroup);
+static void free_counter(struct counter *counter);
+
+struct pcep_lib_pthread_passthrough_data {
+ void *(*start_routine)(void *data);
+ void *data;
+};
+
+/* ------------ API Functions ------------ */
+
+int pcep_lib_initialize(struct frr_pthread *fpt)
+{
+ PCEP_DEBUG("Initializing pceplib");
+
+ /* Register pceplib logging callback */
+ register_logger(pceplib_logging_cb);
+
+ /* Its ok that this object goes out of scope, as it
+ * wont be stored, and its values will be copied */
+ struct pceplib_infra_config infra = {
+ /* Memory infrastructure */
+ .pceplib_infra_mt = MTYPE_PCEPLIB_INFRA,
+ .pceplib_messages_mt = MTYPE_PCEPLIB_MESSAGES,
+ .malloc_func = (pceplib_malloc_func)qmalloc,
+ .calloc_func = (pceplib_calloc_func)qcalloc,
+ .realloc_func = (pceplib_realloc_func)qrealloc,
+ .strdup_func = (pceplib_strdup_func)qstrdup,
+ .free_func = (pceplib_free_func)qfree,
+ /* Timers infrastructure */
+ .external_infra_data = fpt,
+ .socket_read_func = pcep_lib_pceplib_socket_read_cb,
+ .socket_write_func = pcep_lib_pceplib_socket_write_cb,
+ /* PCEP events */
+ .pcep_event_func = pcep_lib_pceplib_event_cb,
+ /* PCEPlib pthread creation callback */
+ .pthread_create_func = pcep_lib_pthread_create_cb};
+ if (!initialize_pcc_infra(&infra)) {
+ flog_err(EC_PATH_PCEP_PCC_INIT, "failed to initialize pceplib");
+ return 1;
+ }
+
+ return 0;
+}
+
+void pcep_lib_finalize(void)
+{
+ PCEP_DEBUG("Finalizing pceplib");
+ if (!destroy_pcc()) {
+ flog_err(EC_PATH_PCEP_PCC_FINI, "failed to finalize pceplib");
+ }
+}
+
+
+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;
+
+ config = create_default_pcep_configuration();
+ config->dst_pcep_port = dst_port;
+ config->src_pcep_port = src_port;
+ if (IS_IPADDR_V6(src_addr)) {
+ config->is_src_ipv6 = true;
+ memcpy(&config->src_ip.src_ipv6, &src_addr->ipaddr_v6,
+ sizeof(struct in6_addr));
+ } else {
+ config->is_src_ipv6 = false;
+ config->src_ip.src_ipv4 = src_addr->ipaddr_v4;
+ }
+
+ config->support_stateful_pce_lsp_update = true;
+ config->support_pce_lsp_instantiation = false;
+ config->support_include_db_version = false;
+ config->support_lsp_triggered_resync = false;
+ config->support_lsp_delta_sync = false;
+ config->support_pce_triggered_initial_sync = false;
+ config->support_sr_te_pst = true;
+ config->pcc_can_resolve_nai_to_sid = false;
+
+ 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 (pcep_options->tcp_md5_auth != NULL
+ && pcep_options->tcp_md5_auth[0] != '\0') {
+ config->is_tcp_auth_md5 = true;
+ strncpy(config->tcp_authentication_str,
+ pcep_options->tcp_md5_auth, TCP_MD5SIG_MAXKEYLEN);
+ } else {
+ config->is_tcp_auth_md5 = false;
+ }
+
+ if (IS_IPADDR_V6(dst_addr)) {
+ sess = connect_pce_ipv6(config, &dst_addr->ipaddr_v6);
+ } else {
+ sess = connect_pce(config, &dst_addr->ipaddr_v4);
+ }
+ destroy_pcep_configuration(config);
+ return sess;
+}
+
+void pcep_lib_disconnect(pcep_session *sess)
+{
+ assert(sess != NULL);
+ disconnect_pce(sess);
+}
+
+/* Callback passed to pceplib to write to a socket.
+ * When the socket is ready to be written to,
+ * pcep_lib_socket_write_ready() will be called */
+
+int pcep_lib_pceplib_socket_write_cb(void *fpt, void **thread, int fd,
+ void *payload)
+{
+ return pcep_thread_socket_write(fpt, thread, fd, payload,
+ pcep_lib_socket_write_ready);
+}
+
+/* Callback passed to pceplib to read from a socket.
+ * When the socket is ready to be read from,
+ * pcep_lib_socket_read_ready() will be called */
+
+int pcep_lib_pceplib_socket_read_cb(void *fpt, void **thread, int fd,
+ void *payload)
+{
+ return pcep_thread_socket_read(fpt, thread, fd, payload,
+ pcep_lib_socket_read_ready);
+}
+
+/* Callbacks called by path_pcep_controller when a socket is ready to read/write
+ */
+
+int pcep_lib_socket_write_ready(struct thread *thread)
+{
+ struct pcep_ctrl_socket_data *data = THREAD_ARG(thread);
+ assert(data != NULL);
+
+ int retval = pceplib_external_socket_write(data->fd, data->payload);
+ XFREE(MTYPE_PCEP, data);
+
+ return retval;
+}
+
+int pcep_lib_socket_read_ready(struct thread *thread)
+{
+ struct pcep_ctrl_socket_data *data = THREAD_ARG(thread);
+ assert(data != NULL);
+
+ int retval = pceplib_external_socket_read(data->fd, data->payload);
+ XFREE(MTYPE_PCEP, data);
+
+ return retval;
+}
+
+/* Callback passed to pceplib when a pcep_event is ready */
+void pcep_lib_pceplib_event_cb(void *fpt, pcep_event *event)
+{
+ pcep_thread_send_ctrl_event(fpt, event, pcep_thread_pcep_event);
+}
+
+/* Wrapper function around the actual pceplib thread start function */
+void *pcep_lib_pthread_start_passthrough(void *data)
+{
+ struct frr_pthread *fpt = data;
+ struct pcep_lib_pthread_passthrough_data *passthrough_data = fpt->data;
+ void *start_routine_data = passthrough_data->data;
+ void *(*start_routine)(void *) = passthrough_data->start_routine;
+ XFREE(MTYPE_PCEP, passthrough_data);
+
+ if (start_routine != NULL) {
+ return start_routine(start_routine_data);
+ }
+
+ return NULL;
+}
+
+int pcep_lib_pthread_create_cb(pthread_t *thread_id, const pthread_attr_t *attr,
+ void *(*start_routine)(void *), void *data,
+ const char *thread_name)
+{
+ /* Since FRR calls the start_routine with a struct frr_pthread,
+ * we have to store the real data and callback in a passthrough
+ * and pass the actual data the start_routine needs */
+ struct pcep_lib_pthread_passthrough_data *passthrough_data = XMALLOC(
+ MTYPE_PCEP, sizeof(struct pcep_lib_pthread_passthrough_data));
+ passthrough_data->data = data;
+ passthrough_data->start_routine = start_routine;
+
+ struct frr_pthread_attr fpt_attr = {
+ .start = pcep_lib_pthread_start_passthrough,
+ .stop = pcep_lib_pthread_stop_cb};
+ struct frr_pthread *fpt =
+ frr_pthread_new(&fpt_attr, thread_name, "pcep_lib");
+ if (fpt == NULL) {
+ return 1;
+ }
+
+ fpt->data = passthrough_data;
+ int retval = frr_pthread_run(fpt, attr);
+ if (retval) {
+ return retval;
+ }
+
+ *thread_id = fpt->thread;
+
+ return 0;
+}
+
+int pcep_lib_pthread_stop_cb(struct frr_pthread *fpt, void **res)
+{
+ pcep_lib_finalize();
+ frr_pthread_destroy(fpt);
+
+ return 0;
+}
+
+struct pcep_message *pcep_lib_format_report(struct pcep_caps *caps,
+ struct path *path)
+{
+ double_linked_list *objs = pcep_lib_format_path(caps, path);
+ return pcep_msg_create_report(objs);
+}
+
+static struct pcep_object_rp *create_rp(uint32_t reqid)
+{
+ double_linked_list *rp_tlvs;
+ struct pcep_object_tlv_path_setup_type *setup_type_tlv;
+ struct pcep_object_rp *rp;
+
+ rp_tlvs = dll_initialize();
+ setup_type_tlv = pcep_tlv_create_path_setup_type(SR_TE_PST);
+ dll_append(rp_tlvs, setup_type_tlv);
+
+ rp = pcep_obj_create_rp(0, false, false, false, true, reqid, rp_tlvs);
+
+ return rp;
+}
+
+struct pcep_message *pcep_lib_format_request(struct pcep_caps *caps,
+ struct path *path)
+{
+ struct ipaddr *src = &path->pcc_addr;
+ struct ipaddr *dst = &path->nbkey.endpoint;
+ double_linked_list *objs;
+ struct pcep_object_rp *rp;
+ struct pcep_object_endpoints_ipv4 *endpoints_ipv4;
+ struct pcep_object_endpoints_ipv6 *endpoints_ipv6;
+ struct pcep_object_objective_function *of = NULL;
+ enum objfun_type objfun = OBJFUN_UNDEFINED;
+
+ assert(src->ipa_type == dst->ipa_type);
+
+ objs = dll_initialize();
+ rp = create_rp(path->req_id);
+ rp->header.flag_p = true;
+
+ pcep_lib_format_constraints(path, objs);
+
+ /* Objective Function */
+ if (path->has_pcc_objfun) {
+ objfun = path->pcc_objfun;
+ }
+
+ if (objfun != OBJFUN_UNDEFINED) {
+ of = pcep_obj_create_objective_function(objfun, NULL);
+ assert(of != NULL);
+ of->header.flag_p = path->enforce_pcc_objfun;
+ dll_append(objs, of);
+ }
+
+ if (IS_IPADDR_V6(src)) {
+ endpoints_ipv6 = pcep_obj_create_endpoint_ipv6(&src->ipaddr_v6,
+ &dst->ipaddr_v6);
+ endpoints_ipv6->header.flag_p = true;
+ return pcep_msg_create_request_ipv6(rp, endpoints_ipv6, objs);
+ } else {
+ endpoints_ipv4 = pcep_obj_create_endpoint_ipv4(&src->ipaddr_v4,
+ &dst->ipaddr_v4);
+ endpoints_ipv4->header.flag_p = true;
+ return pcep_msg_create_request(rp, endpoints_ipv4, objs);
+ }
+}
+
+struct pcep_message *pcep_lib_format_error(int error_type, int error_value)
+{
+ return pcep_msg_create_error(error_type, error_value);
+}
+
+struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid)
+{
+ struct pcep_object_notify *notify;
+ double_linked_list *objs;
+ struct pcep_object_rp *rp;
+
+ notify = pcep_obj_create_notify(
+ PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED,
+ PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST);
+ objs = dll_initialize();
+ rp = create_rp(reqid);
+ dll_append(objs, rp);
+
+ return pcep_msg_create_notify(notify, objs);
+}
+
+struct path *pcep_lib_parse_path(struct pcep_message *msg)
+{
+ struct path *path;
+ double_linked_list *objs = msg->obj_list;
+ double_linked_list_node *node;
+
+ struct pcep_object_header *obj;
+ struct pcep_object_rp *rp = NULL;
+ struct pcep_object_srp *srp = NULL;
+ struct pcep_object_lsp *lsp = NULL;
+ struct pcep_object_lspa *lspa = NULL;
+ struct pcep_object_ro *ero = NULL;
+ struct pcep_object_metric *metric = NULL;
+ struct pcep_object_bandwidth *bandwidth = NULL;
+ struct pcep_object_objective_function *of = NULL;
+
+ path = pcep_new_path();
+
+ for (node = objs->head; node != NULL; node = node->next_node) {
+ obj = (struct pcep_object_header *)node->data;
+ switch (CLASS_TYPE(obj->object_class, obj->object_type)) {
+ case CLASS_TYPE(PCEP_OBJ_CLASS_RP, PCEP_OBJ_TYPE_RP):
+ assert(rp == NULL);
+ rp = (struct pcep_object_rp *)obj;
+ pcep_lib_parse_rp(path, rp);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_SRP, PCEP_OBJ_TYPE_SRP):
+ assert(srp == NULL);
+ srp = (struct pcep_object_srp *)obj;
+ pcep_lib_parse_srp(path, srp);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_LSP, PCEP_OBJ_TYPE_LSP):
+ /* Only support single LSP per message */
+ assert(lsp == NULL);
+ lsp = (struct pcep_object_lsp *)obj;
+ pcep_lib_parse_lsp(path, lsp);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_LSPA, PCEP_OBJ_TYPE_LSPA):
+ assert(lspa == NULL);
+ lspa = (struct pcep_object_lspa *)obj;
+ pcep_lib_parse_lspa(path, lspa);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO):
+ /* Only support single ERO per message */
+ assert(ero == NULL);
+ ero = (struct pcep_object_ro *)obj;
+ pcep_lib_parse_ero(path, ero);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC):
+ metric = (struct pcep_object_metric *)obj;
+ pcep_lib_parse_metric(path, metric);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_BANDWIDTH,
+ PCEP_OBJ_TYPE_BANDWIDTH_REQ):
+ case CLASS_TYPE(PCEP_OBJ_CLASS_BANDWIDTH,
+ PCEP_OBJ_TYPE_BANDWIDTH_CISCO):
+ bandwidth = (struct pcep_object_bandwidth *)obj;
+ path->has_bandwidth = true;
+ path->bandwidth = bandwidth->bandwidth;
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH):
+ path->no_path = true;
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_OF, PCEP_OBJ_TYPE_OF):
+ of = (struct pcep_object_objective_function *)obj;
+ path->has_pce_objfun = true;
+ path->pce_objfun = of->of_code;
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT,
+ "Unexpected PCEP object %s (%u) / %s (%u)",
+ pcep_object_class_name(obj->object_class),
+ obj->object_class,
+ pcep_object_type_name(obj->object_class,
+ obj->object_type),
+ obj->object_type);
+ break;
+ }
+ }
+
+ return path;
+}
+
+void pcep_lib_parse_capabilities(struct pcep_message *msg,
+ struct pcep_caps *caps)
+{
+ double_linked_list *objs = msg->obj_list;
+ double_linked_list_node *node;
+
+ struct pcep_object_header *obj;
+ struct pcep_object_open *open = NULL;
+
+ for (node = objs->head; node != NULL; node = node->next_node) {
+ obj = (struct pcep_object_header *)node->data;
+ switch (CLASS_TYPE(obj->object_class, obj->object_type)) {
+ case CLASS_TYPE(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN):
+ assert(open == NULL);
+ open = (struct pcep_object_open *)obj;
+ pcep_lib_parse_open(caps, open);
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT,
+ "Unexpected PCEP object %s (%u) / %s (%u)",
+ pcep_object_class_name(obj->object_class),
+ obj->object_class,
+ pcep_object_type_name(obj->object_class,
+ obj->object_type),
+ obj->object_type);
+ break;
+ }
+ }
+}
+
+struct counters_group *pcep_lib_copy_counters(pcep_session *sess)
+{
+ if (!sess || !sess->pcep_session_counters) {
+ return NULL;
+ }
+
+ return copy_counter_group(sess->pcep_session_counters);
+}
+
+void pcep_lib_free_counters(struct counters_group *counters)
+{
+ free_counter_group(counters);
+}
+
+pcep_session *pcep_lib_copy_pcep_session(pcep_session *sess)
+{
+ if (!sess) {
+ return NULL;
+ }
+
+ pcep_session *copy;
+ copy = XCALLOC(MTYPE_PCEP, sizeof(*copy));
+ memcpy(copy, sess, sizeof(*copy));
+ /* These fields should not be accessed */
+ copy->num_unknown_messages_time_queue = NULL;
+ copy->socket_comm_session = NULL;
+ copy->pcep_session_counters = NULL;
+
+ return copy;
+}
+
+/* ------------ pceplib logging callback ------------ */
+
+int pceplib_logging_cb(int priority, const char *fmt, va_list args)
+{
+ char buffer[1024];
+ vsnprintf(buffer, sizeof(buffer), fmt, args);
+ PCEP_DEBUG_PCEPLIB(priority, "pceplib: %s", buffer);
+ return 0;
+}
+
+/* ------------ Internal Functions ------------ */
+
+double_linked_list *pcep_lib_format_path(struct pcep_caps *caps,
+ struct path *path)
+{
+ struct in_addr addr_null;
+ double_linked_list *objs, *srp_tlvs, *lsp_tlvs, *ero_objs;
+ struct pcep_object_tlv_header *tlv;
+ struct pcep_object_ro_subobj *ero_obj;
+ struct pcep_object_srp *srp;
+ struct pcep_object_lsp *lsp;
+ struct pcep_object_ro *ero;
+ uint32_t encoded_binding_sid;
+ char binding_sid_lsp_tlv_data[6];
+
+ memset(&addr_null, 0, sizeof(addr_null));
+
+ objs = dll_initialize();
+
+ if (path->plsp_id != 0) {
+ /* SRP object */
+ srp_tlvs = dll_initialize();
+ tlv = (struct pcep_object_tlv_header *)
+ pcep_tlv_create_path_setup_type(SR_TE_PST);
+ assert(tlv != NULL);
+ dll_append(srp_tlvs, tlv);
+ srp = pcep_obj_create_srp(path->do_remove, path->srp_id,
+ srp_tlvs);
+ assert(srp != NULL);
+ srp->header.flag_p = true;
+ dll_append(objs, srp);
+ }
+
+ /* LSP object */
+ lsp_tlvs = dll_initialize();
+
+ if (path->plsp_id == 0 || IS_IPADDR_NONE(&path->nbkey.endpoint)
+ || IS_IPADDR_NONE(&path->pcc_addr)) {
+ tlv = (struct pcep_object_tlv_header *)
+ pcep_tlv_create_ipv4_lsp_identifiers(
+ &addr_null, &addr_null, 0, 0, &addr_null);
+ } else {
+ assert(path->pcc_addr.ipa_type
+ == path->nbkey.endpoint.ipa_type);
+ if (IS_IPADDR_V4(&path->pcc_addr)) {
+ tlv = (struct pcep_object_tlv_header *)
+ pcep_tlv_create_ipv4_lsp_identifiers(
+ &path->pcc_addr.ipaddr_v4,
+ &path->nbkey.endpoint.ipaddr_v4, 0, 0,
+ &path->pcc_addr.ipaddr_v4);
+ } else {
+ tlv = (struct pcep_object_tlv_header *)
+ pcep_tlv_create_ipv6_lsp_identifiers(
+ &path->pcc_addr.ipaddr_v6,
+ &path->nbkey.endpoint.ipaddr_v6, 0, 0,
+ &path->pcc_addr.ipaddr_v6);
+ }
+ }
+ assert(tlv != NULL);
+ dll_append(lsp_tlvs, tlv);
+ if (path->name != NULL) {
+ tlv = (struct pcep_object_tlv_header *)
+ /*FIXME: Remove the typecasty when pceplib is changed
+ to take a const char* */
+ pcep_tlv_create_symbolic_path_name((char *)path->name,
+ strlen(path->name));
+ assert(tlv != NULL);
+ dll_append(lsp_tlvs, tlv);
+ }
+ if ((path->plsp_id != 0) && (path->binding_sid != MPLS_LABEL_NONE)) {
+ memset(binding_sid_lsp_tlv_data, 0, 2);
+ encoded_binding_sid = htonl(path->binding_sid << 12);
+ memcpy(binding_sid_lsp_tlv_data + 2, &encoded_binding_sid, 4);
+ tlv = (struct pcep_object_tlv_header *)
+ pcep_tlv_create_tlv_arbitrary(
+ binding_sid_lsp_tlv_data,
+ sizeof(binding_sid_lsp_tlv_data), 65505);
+ assert(tlv != NULL);
+ dll_append(lsp_tlvs, tlv);
+ }
+ lsp = pcep_obj_create_lsp(
+ path->plsp_id, path->status, path->was_created /* C Flag */,
+ path->go_active /* A Flag */, path->was_removed /* R Flag */,
+ path->is_synching /* S Flag */, path->is_delegated /* D Flag */,
+ lsp_tlvs);
+ assert(lsp != NULL);
+ lsp->header.flag_p = true;
+ dll_append(objs, lsp);
+ /* ERO object */
+ ero_objs = dll_initialize();
+ for (struct path_hop *hop = path->first_hop; hop != NULL;
+ hop = hop->next) {
+ uint32_t sid;
+
+ /* Only supporting MPLS hops with both sid and nai */
+ assert(hop->is_mpls);
+ assert(hop->has_sid);
+
+ if (hop->has_attribs) {
+ sid = ENCODE_SR_ERO_SID(hop->sid.mpls.label,
+ hop->sid.mpls.traffic_class,
+ hop->sid.mpls.is_bottom,
+ hop->sid.mpls.ttl);
+ } else {
+ sid = ENCODE_SR_ERO_SID(hop->sid.mpls.label, 0, 0, 0);
+ }
+
+ ero_obj = NULL;
+ if (hop->has_nai) {
+ assert(hop->nai.type != PCEP_SR_SUBOBJ_NAI_ABSENT);
+ assert(hop->nai.type
+ != PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY);
+ assert(hop->nai.type != PCEP_SR_SUBOBJ_NAI_UNKNOWN);
+ switch (hop->nai.type) {
+ case PCEP_SR_SUBOBJ_NAI_IPV4_NODE:
+ ero_obj = (struct pcep_object_ro_subobj *)
+ pcep_obj_create_ro_subobj_sr_ipv4_node(
+ hop->is_loose, !hop->has_sid,
+ hop->has_attribs, /* C Flag */
+ hop->is_mpls, /* M Flag */
+ sid,
+ &hop->nai.local_addr.ipaddr_v4);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV6_NODE:
+ ero_obj = (struct pcep_object_ro_subobj *)
+ pcep_obj_create_ro_subobj_sr_ipv6_node(
+ hop->is_loose, !hop->has_sid,
+ hop->has_attribs, /* C Flag */
+ hop->is_mpls, /* M Flag */
+ sid,
+ &hop->nai.local_addr.ipaddr_v6);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY:
+ ero_obj = (struct pcep_object_ro_subobj *)
+ pcep_obj_create_ro_subobj_sr_ipv4_adj(
+ hop->is_loose, !hop->has_sid,
+ hop->has_attribs, /* C Flag */
+ hop->is_mpls, /* M Flag */
+ sid,
+ &hop->nai.local_addr.ipaddr_v4,
+ &hop->nai.remote_addr
+ .ipaddr_v4);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY:
+ ero_obj = (struct pcep_object_ro_subobj *)
+ pcep_obj_create_ro_subobj_sr_ipv6_adj(
+ hop->is_loose, !hop->has_sid,
+ hop->has_attribs, /* C Flag */
+ hop->is_mpls, /* M Flag */
+ sid,
+ &hop->nai.local_addr.ipaddr_v6,
+ &hop->nai.remote_addr
+ .ipaddr_v6);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY:
+ ero_obj = (struct pcep_object_ro_subobj *)
+ pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(
+ hop->is_loose, !hop->has_sid,
+ hop->has_attribs, /* C Flag */
+ hop->is_mpls, /* M Flag */
+ sid,
+ hop->nai.local_addr.ipaddr_v4
+ .s_addr,
+ hop->nai.local_iface,
+ hop->nai.remote_addr.ipaddr_v4
+ .s_addr,
+ hop->nai.remote_iface);
+ break;
+ default:
+ break;
+ }
+ }
+ if (ero_obj == NULL) {
+ ero_obj = (struct pcep_object_ro_subobj *)
+ pcep_obj_create_ro_subobj_sr_nonai(
+ hop->is_loose, sid,
+ hop->has_attribs, /* C Flag */
+ hop->is_mpls); /* M Flag */
+ }
+ dll_append(ero_objs, ero_obj);
+ }
+ ero = pcep_obj_create_ero(ero_objs);
+ assert(ero != NULL);
+ ero->header.flag_p = true;
+ dll_append(objs, ero);
+
+ if (path->plsp_id == 0) {
+ return objs;
+ }
+
+ pcep_lib_format_constraints(path, objs);
+
+ return objs;
+}
+
+void pcep_lib_format_constraints(struct path *path, double_linked_list *objs)
+{
+ struct pcep_object_metric *metric;
+ struct pcep_object_bandwidth *bandwidth;
+ struct pcep_object_lspa *lspa;
+
+ /* LSPA object */
+ if (path->has_affinity_filters) {
+ lspa = pcep_obj_create_lspa(
+ path->affinity_filters[AFFINITY_FILTER_EXCLUDE_ANY - 1],
+ path->affinity_filters[AFFINITY_FILTER_INCLUDE_ANY - 1],
+ path->affinity_filters[AFFINITY_FILTER_INCLUDE_ALL - 1],
+ DEFAULT_LSAP_SETUP_PRIO, DEFAULT_LSAP_HOLDING_PRIO,
+ DEFAULT_LSAP_LOCAL_PRETECTION);
+ assert(lspa != NULL);
+ lspa->header.flag_p = true;
+ dll_append(objs, lspa);
+ }
+
+ /* Bandwidth Objects */
+ if (path->has_bandwidth) {
+ /* Requested Bandwidth */
+ bandwidth = pcep_obj_create_bandwidth(path->bandwidth);
+ assert(bandwidth != NULL);
+ bandwidth->header.flag_p = path->enforce_bandwidth;
+ dll_append(objs, bandwidth);
+ }
+
+ /* Metric Objects */
+ for (struct path_metric *m = path->first_metric; m != NULL;
+ m = m->next) {
+ metric = pcep_obj_create_metric(m->type, m->is_bound,
+ m->is_computed, m->value);
+ assert(metric != NULL);
+ metric->header.flag_p = m->enforce;
+ dll_append(objs, metric);
+ }
+}
+
+void pcep_lib_parse_open(struct pcep_caps *caps, struct pcep_object_open *open)
+{
+ double_linked_list *tlvs = open->header.tlv_list;
+ double_linked_list_node *node;
+ struct pcep_object_tlv_header *tlv_header;
+
+ caps->is_stateful = false;
+ caps->supported_ofs_are_known = false;
+ caps->supported_ofs = 0;
+
+ for (node = tlvs->head; node != NULL; node = node->next_node) {
+ tlv_header = (struct pcep_object_tlv_header *)node->data;
+ switch (tlv_header->type) {
+ case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
+ pcep_lib_parse_open_pce_capability(caps, tlv_header);
+ break;
+ case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY:
+ break;
+ case PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST:
+ pcep_lib_parse_open_objfun_list(caps, tlv_header);
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected OPEN's TLV %s (%u)",
+ pcep_tlv_type_name(tlv_header->type),
+ tlv_header->type);
+ break;
+ }
+ }
+}
+
+void pcep_lib_parse_open_pce_capability(
+ struct pcep_caps *caps, struct pcep_object_tlv_header *tlv_header)
+{
+ struct pcep_object_tlv_stateful_pce_capability *tlv;
+ tlv = (struct pcep_object_tlv_stateful_pce_capability *)tlv_header;
+ caps->is_stateful = tlv->flag_u_lsp_update_capability;
+}
+
+void pcep_lib_parse_open_objfun_list(struct pcep_caps *caps,
+ struct pcep_object_tlv_header *tlv_header)
+{
+ double_linked_list_node *node;
+ struct pcep_object_tlv_of_list *tlv;
+ tlv = (struct pcep_object_tlv_of_list *)tlv_header;
+ uint16_t of_code;
+ caps->supported_ofs_are_known = true;
+ for (node = tlv->of_list->head; node != NULL; node = node->next_node) {
+ of_code = *(uint16_t *)node->data;
+ if (of_code >= 32) {
+ zlog_warn(
+ "Ignoring unexpected objective function with code %u",
+ of_code);
+ continue;
+ }
+ SET_FLAG(caps->supported_ofs, of_code);
+ }
+}
+
+void pcep_lib_parse_rp(struct path *path, struct pcep_object_rp *rp)
+{
+ double_linked_list *tlvs = rp->header.tlv_list;
+ double_linked_list_node *node;
+ struct pcep_object_tlv_header *tlv;
+
+ /* We ignore the other flags and priority for now */
+ path->req_id = rp->request_id;
+ path->has_pce_objfun = false;
+ path->pce_objfun = OBJFUN_UNDEFINED;
+
+ for (node = tlvs->head; node != NULL; node = node->next_node) {
+ tlv = (struct pcep_object_tlv_header *)node->data;
+ switch (tlv->type) {
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE:
+ // TODO: enforce the path setup type is SR_TE_PST
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected RP's TLV %s (%u)",
+ pcep_tlv_type_name(tlv->type), tlv->type);
+ break;
+ }
+ }
+}
+
+void pcep_lib_parse_srp(struct path *path, struct pcep_object_srp *srp)
+{
+ double_linked_list *tlvs = srp->header.tlv_list;
+ double_linked_list_node *node;
+ struct pcep_object_tlv_header *tlv;
+
+ path->do_remove = srp->flag_lsp_remove;
+ path->srp_id = srp->srp_id_number;
+
+ for (node = tlvs->head; node != NULL; node = node->next_node) {
+ tlv = (struct pcep_object_tlv_header *)node->data;
+ switch (tlv->type) {
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE:
+ // TODO: enforce the path setup type is SR_TE_PST
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected SRP's TLV %s (%u)",
+ pcep_tlv_type_name(tlv->type), tlv->type);
+ break;
+ }
+ }
+}
+
+void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp)
+{
+ double_linked_list *tlvs = lsp->header.tlv_list;
+ double_linked_list_node *node;
+ struct pcep_object_tlv_header *tlv;
+
+ path->plsp_id = lsp->plsp_id;
+ path->status = lsp->operational_status;
+ path->go_active = lsp->flag_a;
+ path->was_created = lsp->flag_c;
+ path->was_removed = lsp->flag_r;
+ path->is_synching = lsp->flag_s;
+ path->is_delegated = lsp->flag_d;
+
+ if (tlvs == NULL)
+ return;
+
+ for (node = tlvs->head; node != NULL; node = node->next_node) {
+ tlv = (struct pcep_object_tlv_header *)node->data;
+ switch (tlv->type) {
+ default:
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected LSP TLV %s (%u)",
+ pcep_tlv_type_name(tlv->type), tlv->type);
+ break;
+ }
+ }
+}
+
+void pcep_lib_parse_lspa(struct path *path, struct pcep_object_lspa *lspa)
+{
+ path->has_affinity_filters = true;
+ path->affinity_filters[AFFINITY_FILTER_EXCLUDE_ANY - 1] =
+ lspa->lspa_exclude_any;
+ path->affinity_filters[AFFINITY_FILTER_INCLUDE_ANY - 1] =
+ lspa->lspa_include_any;
+ path->affinity_filters[AFFINITY_FILTER_INCLUDE_ALL - 1] =
+ lspa->lspa_include_all;
+}
+
+void pcep_lib_parse_metric(struct path *path, struct pcep_object_metric *obj)
+{
+ struct path_metric *metric;
+
+ metric = pcep_new_metric();
+ metric->type = obj->type;
+ metric->is_bound = obj->flag_b;
+ metric->is_computed = obj->flag_c;
+ metric->value = obj->value;
+ metric->next = path->first_metric;
+ path->first_metric = metric;
+}
+
+void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero)
+{
+ struct path_hop *hop = NULL;
+ double_linked_list *objs = ero->sub_objects;
+ double_linked_list_node *node;
+ struct pcep_object_ro_subobj *obj;
+
+ for (node = objs->tail; node != NULL; node = node->prev_node) {
+ obj = (struct pcep_object_ro_subobj *)node->data;
+ switch (obj->ro_subobj_type) {
+ case RO_SUBOBJ_TYPE_SR:
+ hop = pcep_lib_parse_ero_sr(
+ hop, (struct pcep_ro_subobj_sr *)obj);
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_ERO_SUBOBJ,
+ "Unexpected ERO sub-object %s (%u)",
+ pcep_ro_type_name(obj->ro_subobj_type),
+ obj->ro_subobj_type);
+ break;
+ }
+ }
+
+ path->first_hop = hop;
+}
+
+struct path_hop *pcep_lib_parse_ero_sr(struct path_hop *next,
+ struct pcep_ro_subobj_sr *sr)
+{
+ struct path_hop *hop = NULL;
+ union sid sid;
+
+ /* Only support IPv4 node with SID */
+ assert(!sr->flag_s);
+
+ if (sr->flag_m) {
+ sid.mpls = (struct sid_mpls){
+ .label = GET_SR_ERO_SID_LABEL(sr->sid),
+ .traffic_class = GET_SR_ERO_SID_TC(sr->sid),
+ .is_bottom = GET_SR_ERO_SID_S(sr->sid),
+ .ttl = GET_SR_ERO_SID_TTL(sr->sid)};
+ } else {
+ sid.value = sr->sid;
+ }
+
+ hop = pcep_new_hop();
+ *hop = (struct path_hop){.next = next,
+ .is_loose =
+ sr->ro_subobj.flag_subobj_loose_hop,
+ .has_sid = !sr->flag_s,
+ .is_mpls = sr->flag_m,
+ .has_attribs = sr->flag_c,
+ .sid = sid,
+ .has_nai = !sr->flag_f,
+ .nai = {.type = sr->nai_type}};
+
+ if (!sr->flag_f) {
+ assert(sr->nai_list != NULL);
+ double_linked_list_node *n = sr->nai_list->head;
+ assert(n != NULL);
+ assert(n->data != NULL);
+ switch (sr->nai_type) {
+ case PCEP_SR_SUBOBJ_NAI_IPV4_NODE:
+ hop->nai.local_addr.ipa_type = IPADDR_V4;
+ memcpy(&hop->nai.local_addr.ipaddr_v4, n->data,
+ sizeof(struct in_addr));
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV6_NODE:
+ hop->nai.local_addr.ipa_type = IPADDR_V6;
+ memcpy(&hop->nai.local_addr.ipaddr_v6, n->data,
+ sizeof(struct in6_addr));
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY:
+ hop->nai.local_addr.ipa_type = IPADDR_V4;
+ memcpy(&hop->nai.local_addr.ipaddr_v4, n->data,
+ sizeof(struct in_addr));
+ n = n->next_node;
+ assert(n != NULL);
+ assert(n->data != NULL);
+ hop->nai.remote_addr.ipa_type = IPADDR_V4;
+ memcpy(&hop->nai.remote_addr.ipaddr_v4, n->data,
+ sizeof(struct in_addr));
+ break;
+ case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY:
+ hop->nai.local_addr.ipa_type = IPADDR_V6;
+ memcpy(&hop->nai.local_addr.ipaddr_v6, n->data,
+ sizeof(struct in6_addr));
+ n = n->next_node;
+ assert(n != NULL);
+ assert(n->data != NULL);
+ hop->nai.remote_addr.ipa_type = IPADDR_V6;
+ memcpy(&hop->nai.remote_addr.ipaddr_v6, n->data,
+ sizeof(struct in6_addr));
+ break;
+ case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY:
+ hop->nai.local_addr.ipa_type = IPADDR_V4;
+ memcpy(&hop->nai.local_addr.ipaddr_v4, n->data,
+ sizeof(struct in_addr));
+ n = n->next_node;
+ assert(n != NULL);
+ assert(n->data != NULL);
+ hop->nai.local_iface = *(uint32_t *)n->data;
+ n = n->next_node;
+ assert(n != NULL);
+ assert(n->data != NULL);
+ hop->nai.remote_addr.ipa_type = IPADDR_V4;
+ memcpy(&hop->nai.remote_addr.ipaddr_v4, n->data,
+ sizeof(struct in_addr));
+ n = n->next_node;
+ assert(n != NULL);
+ assert(n->data != NULL);
+ hop->nai.remote_iface = *(uint32_t *)n->data;
+ break;
+ default:
+ hop->has_nai = false;
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_SR_NAI,
+ "Unexpected SR segment NAI type %s (%u)",
+ pcep_nai_type_name(sr->nai_type),
+ sr->nai_type);
+ break;
+ }
+ }
+
+ return hop;
+}
+
+struct counters_group *copy_counter_group(struct counters_group *from)
+{
+ int size, i;
+ struct counters_group *result;
+ if (from == NULL)
+ return NULL;
+ assert(from->max_subgroups >= from->num_subgroups);
+ result = XCALLOC(MTYPE_PCEP, sizeof(*result));
+ memcpy(result, from, sizeof(*result));
+ size = sizeof(struct counters_subgroup *) * (from->max_subgroups + 1);
+ result->subgroups = XCALLOC(MTYPE_PCEP, size);
+ for (i = 0; i <= from->max_subgroups; i++)
+ result->subgroups[i] =
+ copy_counter_subgroup(from->subgroups[i]);
+ return result;
+}
+
+struct counters_subgroup *copy_counter_subgroup(struct counters_subgroup *from)
+{
+ int size, i;
+ struct counters_subgroup *result;
+ if (from == NULL)
+ return NULL;
+ assert(from->max_counters >= from->num_counters);
+ result = XCALLOC(MTYPE_PCEP, sizeof(*result));
+ memcpy(result, from, sizeof(*result));
+ size = sizeof(struct counter *) * (from->max_counters + 1);
+ result->counters = XCALLOC(MTYPE_PCEP, size);
+ for (i = 0; i <= from->max_counters; i++)
+ result->counters[i] = copy_counter(from->counters[i]);
+ return result;
+}
+
+struct counter *copy_counter(struct counter *from)
+{
+ struct counter *result;
+ if (from == NULL)
+ return NULL;
+ result = XCALLOC(MTYPE_PCEP, sizeof(*result));
+ memcpy(result, from, sizeof(*result));
+ return result;
+}
+
+void free_counter_group(struct counters_group *group)
+{
+ int i;
+ if (group == NULL)
+ return;
+ for (i = 0; i <= group->max_subgroups; i++)
+ free_counter_subgroup(group->subgroups[i]);
+ XFREE(MTYPE_PCEP, group->subgroups);
+ XFREE(MTYPE_PCEP, group);
+}
+
+void free_counter_subgroup(struct counters_subgroup *subgroup)
+{
+ int i;
+ if (subgroup == NULL)
+ return;
+ for (i = 0; i <= subgroup->max_counters; i++)
+ free_counter(subgroup->counters[i]);
+ XFREE(MTYPE_PCEP, subgroup->counters);
+ XFREE(MTYPE_PCEP, subgroup);
+}
+
+void free_counter(struct counter *counter)
+{
+ if (counter == NULL)
+ return;
+ XFREE(MTYPE_PCEP, counter);
+}
diff --git a/pathd/path_pcep_lib.h b/pathd/path_pcep_lib.h
new file mode 100644
index 0000000000..3bea28432d
--- /dev/null
+++ b/pathd/path_pcep_lib.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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_LIB_H_
+#define _PATH_PCEP_LIB_H_
+
+#include <stdbool.h>
+#include <pcep_pcc_api.h>
+#include "frr_pthread.h"
+#include "pathd/path_pcep.h"
+
+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, 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 pcep_caps *caps,
+ struct path *path);
+struct pcep_message *pcep_lib_format_request(struct pcep_caps *caps,
+ struct path *path);
+struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid);
+
+struct pcep_message *pcep_lib_format_error(int error_type, int error_value);
+struct path *pcep_lib_parse_path(struct pcep_message *msg);
+void pcep_lib_parse_capabilities(struct pcep_message *msg,
+ struct pcep_caps *caps);
+struct counters_group *pcep_lib_copy_counters(pcep_session *sess);
+void pcep_lib_free_counters(struct counters_group *counters);
+pcep_session *pcep_lib_copy_pcep_session(pcep_session *sess);
+
+#endif // _PATH_PCEP_LIB_H_
diff --git a/pathd/path_pcep_memory.c b/pathd/path_pcep_memory.c
new file mode 100644
index 0000000000..8f608090a6
--- /dev/null
+++ b/pathd/path_pcep_memory.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <memory.h>
+
+#include "pathd/path_pcep_memory.h"
+
+DEFINE_MTYPE(PATHD, PCEP, "PCEP module")
+DEFINE_MTYPE(PATHD, PCEPLIB_INFRA, "PCEPlib Infrastructure")
+DEFINE_MTYPE(PATHD, PCEPLIB_MESSAGES, "PCEPlib PCEP Messages")
diff --git a/pathd/path_pcep_memory.h b/pathd/path_pcep_memory.h
new file mode 100644
index 0000000000..05c5e2d82b
--- /dev/null
+++ b/pathd/path_pcep_memory.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_PATH_PCEP_MEMORY_H_
+#define _FRR_PATH_PCEP_MEMORY_H_
+
+#include "pathd/path_memory.h"
+
+DECLARE_MTYPE(PCEP)
+DECLARE_MTYPE(PCEPLIB_INFRA)
+DECLARE_MTYPE(PCEPLIB_MESSAGES)
+
+#endif /* _FRR_PATH_PCEP_MEMORY_H_ */
diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c
new file mode 100644
index 0000000000..c1f60edd22
--- /dev/null
+++ b/pathd/path_pcep_pcc.c
@@ -0,0 +1,1818 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* TODOS AND KNOWN ISSUES:
+ - Delete mapping from NB keys to PLSPID when an LSP is deleted either
+ by the PCE or by NB.
+ - Revert the hacks to work around ODL requiring a report with
+ operational status DOWN when an LSP is activated.
+ - Enforce only the PCE a policy has been delegated to can update it.
+ - If the router-id is used because the PCC IP is not specified
+ (either IPv4 or IPv6), the connection to the PCE is not reset
+ when the router-id changes.
+*/
+
+#include <zebra.h>
+
+#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 "pathd/pathd.h"
+#include "pathd/path_zebra.h"
+#include "pathd/path_errors.h"
+#include "pathd/path_pcep_memory.h"
+#include "pathd/path_pcep.h"
+#include "pathd/path_pcep_controller.h"
+#include "pathd/path_pcep_lib.h"
+#include "pathd/path_pcep_config.h"
+#include "pathd/path_pcep_debug.h"
+
+
+/* The number of time we will skip connecting if we are missing the PCC
+ * address for an inet family different from the selected transport one*/
+#define OTHER_FAMILY_MAX_RETRIES 4
+#define MAX_ERROR_MSG_SIZE 256
+#define MAX_COMPREQ_TRIES 3
+
+
+/* PCEP Event Handler */
+static void handle_pcep_open(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct pcep_message *msg);
+static void handle_pcep_message(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct pcep_message *msg);
+static void handle_pcep_lsp_update(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct pcep_message *msg);
+static void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct pcep_message *msg);
+static void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct pcep_message *msg);
+
+/* Internal Functions */
+static const char *ipaddr_type_name(struct ipaddr *addr);
+static bool filter_path(struct pcc_state *pcc_state, struct path *path);
+static void select_pcc_addresses(struct pcc_state *pcc_state);
+static void select_transport_address(struct pcc_state *pcc_state);
+static void update_tag(struct pcc_state *pcc_state);
+static void update_originator(struct pcc_state *pcc_state);
+static void schedule_reconnect(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state);
+static void schedule_session_timeout(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state);
+static void cancel_session_timeout(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state);
+static void send_pcep_message(struct pcc_state *pcc_state,
+ struct pcep_message *msg);
+static void send_pcep_error(struct pcc_state *pcc_state,
+ enum pcep_error_type error_type,
+ enum pcep_error_value error_value);
+static void send_report(struct pcc_state *pcc_state, struct path *path);
+static void send_comp_request(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct req_entry *req);
+static void cancel_comp_requests(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state);
+static void cancel_comp_request(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct req_entry *req);
+static void specialize_outgoing_path(struct pcc_state *pcc_state,
+ struct path *path);
+static void specialize_incoming_path(struct pcc_state *pcc_state,
+ struct path *path);
+static bool validate_incoming_path(struct pcc_state *pcc_state,
+ struct path *path, char *errbuff,
+ size_t buffsize);
+static void set_pcc_address(struct pcc_state *pcc_state,
+ struct lsp_nb_key *nbkey, struct ipaddr *addr);
+static int compare_pcc_opts(struct pcc_opts *lhs, struct pcc_opts *rhs);
+static int compare_pce_opts(struct pce_opts *lhs, struct pce_opts *rhs);
+static int get_previous_best_pce(struct pcc_state **pcc);
+static int get_best_pce(struct pcc_state **pcc);
+static int get_pce_count_connected(struct pcc_state **pcc);
+static bool update_best_pce(struct pcc_state **pcc, int best);
+
+/* Data Structure Helper Functions */
+static void lookup_plspid(struct pcc_state *pcc_state, struct path *path);
+static void lookup_nbkey(struct pcc_state *pcc_state, struct path *path);
+static void free_req_entry(struct req_entry *req);
+static struct req_entry *push_new_req(struct pcc_state *pcc_state,
+ struct path *path);
+static void repush_req(struct pcc_state *pcc_state, struct req_entry *req);
+static struct req_entry *pop_req(struct pcc_state *pcc_state, uint32_t reqid);
+static bool add_reqid_mapping(struct pcc_state *pcc_state, struct path *path);
+static void remove_reqid_mapping(struct pcc_state *pcc_state,
+ struct path *path);
+static uint32_t lookup_reqid(struct pcc_state *pcc_state, struct path *path);
+static bool has_pending_req_for(struct pcc_state *pcc_state, struct path *path);
+
+/* Data Structure Callbacks */
+static int plspid_map_cmp(const struct plspid_map_data *a,
+ const struct plspid_map_data *b);
+static uint32_t plspid_map_hash(const struct plspid_map_data *e);
+static int nbkey_map_cmp(const struct nbkey_map_data *a,
+ const struct nbkey_map_data *b);
+static uint32_t nbkey_map_hash(const struct nbkey_map_data *e);
+static int req_map_cmp(const struct req_map_data *a,
+ const struct req_map_data *b);
+static uint32_t req_map_hash(const struct req_map_data *e);
+
+/* Data Structure Declarations */
+DECLARE_HASH(plspid_map, struct plspid_map_data, mi, plspid_map_cmp,
+ plspid_map_hash)
+DECLARE_HASH(nbkey_map, struct nbkey_map_data, mi, nbkey_map_cmp,
+ nbkey_map_hash)
+DECLARE_HASH(req_map, struct req_map_data, mi, req_map_cmp, req_map_hash)
+
+static inline int req_entry_compare(const struct req_entry *a,
+ const struct req_entry *b)
+{
+ return a->path->req_id - b->path->req_id;
+}
+RB_GENERATE(req_entry_head, req_entry, entry, req_entry_compare)
+
+
+/* ------------ API Functions ------------ */
+
+struct pcc_state *pcep_pcc_initialize(struct ctrl_state *ctrl_state, int index)
+{
+ struct pcc_state *pcc_state = XCALLOC(MTYPE_PCEP, sizeof(*pcc_state));
+
+ pcc_state->id = index;
+ pcc_state->status = PCEP_PCC_DISCONNECTED;
+ pcc_state->next_reqid = 1;
+ pcc_state->next_plspid = 1;
+
+ RB_INIT(req_entry_head, &pcc_state->requests);
+
+ update_tag(pcc_state);
+ update_originator(pcc_state);
+
+ PCEP_DEBUG("%s PCC initialized", pcc_state->tag);
+
+ return pcc_state;
+}
+
+void pcep_pcc_finalize(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state)
+{
+ PCEP_DEBUG("%s PCC finalizing...", pcc_state->tag);
+
+ pcep_pcc_disable(ctrl_state, pcc_state);
+
+ if (pcc_state->pcc_opts != NULL) {
+ XFREE(MTYPE_PCEP, pcc_state->pcc_opts);
+ pcc_state->pcc_opts = NULL;
+ }
+ if (pcc_state->pce_opts != NULL) {
+ XFREE(MTYPE_PCEP, pcc_state->pce_opts);
+ pcc_state->pce_opts = NULL;
+ }
+ if (pcc_state->originator != NULL) {
+ XFREE(MTYPE_PCEP, pcc_state->originator);
+ pcc_state->originator = NULL;
+ }
+
+ if (pcc_state->t_reconnect != NULL) {
+ thread_cancel(&pcc_state->t_reconnect);
+ pcc_state->t_reconnect = NULL;
+ }
+
+ if (pcc_state->t_update_best != NULL) {
+ thread_cancel(&pcc_state->t_update_best);
+ pcc_state->t_update_best = NULL;
+ }
+
+ if (pcc_state->t_session_timeout != NULL) {
+ thread_cancel(&pcc_state->t_session_timeout);
+ pcc_state->t_session_timeout = NULL;
+ }
+
+ XFREE(MTYPE_PCEP, pcc_state);
+}
+
+int compare_pcc_opts(struct pcc_opts *lhs, struct pcc_opts *rhs)
+{
+ int retval;
+
+ if (lhs == NULL) {
+ return 1;
+ }
+
+ if (rhs == NULL) {
+ return -1;
+ }
+
+ retval = lhs->port - rhs->port;
+ if (retval != 0) {
+ return retval;
+ }
+
+ retval = lhs->msd - rhs->msd;
+ if (retval != 0) {
+ return retval;
+ }
+
+ 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;
+ }
+ } 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;
+ }
+ }
+
+ return 0;
+}
+
+int compare_pce_opts(struct pce_opts *lhs, struct pce_opts *rhs)
+{
+ if (lhs == NULL) {
+ return 1;
+ }
+
+ if (rhs == NULL) {
+ return -1;
+ }
+
+ int retval = lhs->port - rhs->port;
+ if (retval != 0) {
+ return retval;
+ }
+
+ retval = strcmp(lhs->pce_name, rhs->pce_name);
+ if (retval != 0) {
+ return retval;
+ }
+
+ retval = lhs->precedence - rhs->precedence;
+ if (retval != 0) {
+ return retval;
+ }
+
+ retval = memcmp(&lhs->addr, &rhs->addr, sizeof(lhs->addr));
+ if (retval != 0) {
+ return retval;
+ }
+
+ return 0;
+}
+
+int pcep_pcc_update(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state,
+ struct pcc_opts *pcc_opts, struct pce_opts *pce_opts)
+{
+ int ret = 0;
+
+ // If the options did not change, then there is nothing to do
+ if ((compare_pce_opts(pce_opts, pcc_state->pce_opts) == 0)
+ && (compare_pcc_opts(pcc_opts, pcc_state->pcc_opts) == 0)) {
+ return ret;
+ }
+
+ if ((ret = pcep_pcc_disable(ctrl_state, pcc_state))) {
+ XFREE(MTYPE_PCEP, pcc_opts);
+ XFREE(MTYPE_PCEP, pce_opts);
+ return ret;
+ }
+
+ if (pcc_state->pcc_opts != NULL) {
+ XFREE(MTYPE_PCEP, pcc_state->pcc_opts);
+ }
+ if (pcc_state->pce_opts != NULL) {
+ XFREE(MTYPE_PCEP, pcc_state->pce_opts);
+ }
+
+ pcc_state->pcc_opts = pcc_opts;
+ pcc_state->pce_opts = pce_opts;
+
+ 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 (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);
+ }
+
+ update_tag(pcc_state);
+ update_originator(pcc_state);
+
+ return pcep_pcc_enable(ctrl_state, pcc_state);
+}
+
+void pcep_pcc_reconnect(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state)
+{
+ if (pcc_state->status == PCEP_PCC_DISCONNECTED)
+ pcep_pcc_enable(ctrl_state, pcc_state);
+}
+
+int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state)
+{
+ char pcc_buff[40];
+ char pce_buff[40];
+
+ assert(pcc_state->status == PCEP_PCC_DISCONNECTED);
+ assert(pcc_state->sess == NULL);
+
+ if (pcc_state->t_reconnect != NULL) {
+ thread_cancel(&pcc_state->t_reconnect);
+ pcc_state->t_reconnect = NULL;
+ }
+
+ select_transport_address(pcc_state);
+
+ /* Even though we are connecting using IPv6. we want to have an IPv4
+ * address so we can handle candidate path with IPv4 endpoints */
+ if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)) {
+ if (pcc_state->retry_count < OTHER_FAMILY_MAX_RETRIES) {
+ flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
+ "skipping connection to PCE %s:%d due to "
+ "missing PCC IPv4 address",
+ ipaddr2str(&pcc_state->pce_opts->addr,
+ pce_buff, sizeof(pce_buff)),
+ pcc_state->pce_opts->port);
+ schedule_reconnect(ctrl_state, pcc_state);
+ return 0;
+ } else {
+ flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
+ "missing IPv4 PCC address, IPv4 candidate "
+ "paths will be ignored");
+ }
+ }
+
+ /* Even though we are connecting using IPv4. we want to have an IPv6
+ * address so we can handle candidate path with IPv6 endpoints */
+ if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)) {
+ if (pcc_state->retry_count < OTHER_FAMILY_MAX_RETRIES) {
+ flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
+ "skipping connection to PCE %s:%d due to "
+ "missing PCC IPv6 address",
+ ipaddr2str(&pcc_state->pce_opts->addr,
+ pce_buff, sizeof(pce_buff)),
+ pcc_state->pce_opts->port);
+ schedule_reconnect(ctrl_state, pcc_state);
+ return 0;
+ } else {
+ flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
+ "missing IPv6 PCC address, IPv6 candidate "
+ "paths will be ignored");
+ }
+ }
+
+ /* Even if the maximum retries to try to have all the familly addresses
+ * have been spent, we still need the one for the transport familly */
+ if (pcc_state->pcc_addr_tr.ipa_type == IPADDR_NONE) {
+ flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
+ "skipping connection to PCE %s:%d due to missing "
+ "PCC address",
+ ipaddr2str(&pcc_state->pce_opts->addr, pce_buff,
+ sizeof(pce_buff)),
+ pcc_state->pce_opts->port);
+ schedule_reconnect(ctrl_state, pcc_state);
+ return 0;
+ }
+
+ PCEP_DEBUG("%s PCC connecting", pcc_state->tag);
+ 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->pcc_opts->msd, &pcc_state->pce_opts->config_opts);
+
+ if (pcc_state->sess == NULL) {
+ flog_warn(EC_PATH_PCEP_LIB_CONNECT,
+ "failed to connect to PCE %s:%d from %s:%d",
+ ipaddr2str(&pcc_state->pce_opts->addr, pce_buff,
+ sizeof(pce_buff)),
+ pcc_state->pce_opts->port,
+ ipaddr2str(&pcc_state->pcc_addr_tr, pcc_buff,
+ sizeof(pcc_buff)),
+ pcc_state->pcc_opts->port);
+ schedule_reconnect(ctrl_state, pcc_state);
+ return 0;
+ }
+
+ // In case some best pce alternative were waiting to activate
+ if (pcc_state->t_update_best != NULL) {
+ thread_cancel(&pcc_state->t_update_best);
+ pcc_state->t_update_best = NULL;
+ }
+
+ pcc_state->status = PCEP_PCC_CONNECTING;
+
+ return 0;
+}
+
+int pcep_pcc_disable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state)
+{
+ switch (pcc_state->status) {
+ case PCEP_PCC_DISCONNECTED:
+ return 0;
+ case PCEP_PCC_CONNECTING:
+ case PCEP_PCC_SYNCHRONIZING:
+ case PCEP_PCC_OPERATING:
+ PCEP_DEBUG("%s Disconnecting PCC...", pcc_state->tag);
+ cancel_comp_requests(ctrl_state, pcc_state);
+ pcep_lib_disconnect(pcc_state->sess);
+ /* No need to remove if any PCEs is connected */
+ if (get_pce_count_connected(ctrl_state->pcc) == 0) {
+ pcep_thread_remove_candidate_path_segments(ctrl_state,
+ pcc_state);
+ }
+ pcc_state->sess = NULL;
+ pcc_state->status = PCEP_PCC_DISCONNECTED;
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+void pcep_pcc_sync_path(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct path *path)
+{
+ if (pcc_state->status == PCEP_PCC_SYNCHRONIZING) {
+ path->is_synching = true;
+ } else if (pcc_state->status == PCEP_PCC_OPERATING)
+ path->is_synching = false;
+ else
+ return;
+
+ path->go_active = true;
+
+ /* Accumulate the dynamic paths without any LSP so computation
+ * requests can be performed after synchronization */
+ if ((path->type == SRTE_CANDIDATE_TYPE_DYNAMIC)
+ && (path->first_hop == NULL)
+ && !has_pending_req_for(pcc_state, path)) {
+ PCEP_DEBUG("%s Scheduling computation request for path %s",
+ pcc_state->tag, path->name);
+ push_new_req(pcc_state, path);
+ return;
+ }
+
+ /* Synchronize the path if the PCE supports LSP updates and the
+ * endpoint address familly is supported */
+ if (pcc_state->caps.is_stateful) {
+ if (filter_path(pcc_state, path)) {
+ PCEP_DEBUG("%s Synchronizing path %s", pcc_state->tag,
+ path->name);
+ send_report(pcc_state, path);
+ } else {
+ PCEP_DEBUG(
+ "%s Skipping %s candidate path %s "
+ "synchronization",
+ pcc_state->tag,
+ ipaddr_type_name(&path->nbkey.endpoint),
+ path->name);
+ }
+ }
+}
+
+void pcep_pcc_sync_done(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state)
+{
+ struct req_entry *req;
+
+ if (pcc_state->status != PCEP_PCC_SYNCHRONIZING
+ && pcc_state->status != PCEP_PCC_OPERATING)
+ return;
+
+ if (pcc_state->caps.is_stateful
+ && pcc_state->status == PCEP_PCC_SYNCHRONIZING) {
+ struct path *path = pcep_new_path();
+ *path = (struct path){.name = NULL,
+ .srp_id = 0,
+ .plsp_id = 0,
+ .status = PCEP_LSP_OPERATIONAL_DOWN,
+ .do_remove = false,
+ .go_active = false,
+ .was_created = false,
+ .was_removed = false,
+ .is_synching = false,
+ .is_delegated = false,
+ .first_hop = NULL,
+ .first_metric = NULL};
+ send_report(pcc_state, path);
+ pcep_free_path(path);
+ }
+
+ pcc_state->synchronized = true;
+ pcc_state->status = PCEP_PCC_OPERATING;
+
+ PCEP_DEBUG("%s Synchronization done", pcc_state->tag);
+
+ /* Start the computation request accumulated during synchronization */
+ RB_FOREACH (req, req_entry_head, &pcc_state->requests) {
+ send_comp_request(ctrl_state, pcc_state, req);
+ }
+}
+
+void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct path *path)
+{
+ if (pcc_state->status != PCEP_PCC_OPERATING)
+ return;
+
+ if (pcc_state->caps.is_stateful) {
+ PCEP_DEBUG("%s Send report for candidate path %s",
+ pcc_state->tag, path->name);
+ send_report(pcc_state, path);
+ }
+}
+
+/* ------------ Timeout handler ------------ */
+
+void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ enum pcep_ctrl_timer_type type, void *param)
+{
+ struct req_entry *req;
+
+ switch (type) {
+ case TO_COMPUTATION_REQUEST:
+ assert(param != NULL);
+ req = (struct req_entry *)param;
+ pop_req(pcc_state, req->path->req_id);
+ flog_warn(EC_PATH_PCEP_COMPUTATION_REQUEST_TIMEOUT,
+ "Computation request %d timeout", req->path->req_id);
+ cancel_comp_request(ctrl_state, pcc_state, req);
+ if (req->retry_count++ < MAX_COMPREQ_TRIES) {
+ repush_req(pcc_state, req);
+ send_comp_request(ctrl_state, pcc_state, req);
+ return;
+ }
+ if (pcc_state->caps.is_stateful) {
+ struct path *path;
+ PCEP_DEBUG(
+ "%s Delegating undefined dynamic path %s to PCE %s",
+ pcc_state->tag, req->path->name,
+ pcc_state->originator);
+ path = pcep_copy_path(req->path);
+ path->is_delegated = true;
+ send_report(pcc_state, path);
+ free_req_entry(req);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+
+/* ------------ Pathd event handler ------------ */
+
+void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ enum pcep_pathd_event_type type,
+ struct path *path)
+{
+ struct req_entry *req;
+
+ if (pcc_state->status != PCEP_PCC_OPERATING)
+ return;
+
+ /* Skipping candidate path with endpoint that do not match the
+ * configured or deduced PCC IP version */
+ if (!filter_path(pcc_state, path)) {
+ PCEP_DEBUG("%s Skipping %s candidate path %s event",
+ pcc_state->tag,
+ ipaddr_type_name(&path->nbkey.endpoint), path->name);
+ return;
+ }
+
+ switch (type) {
+ case PCEP_PATH_CREATED:
+ if (has_pending_req_for(pcc_state, path)) {
+ PCEP_DEBUG(
+ "%s Candidate path %s created, computation request already sent",
+ pcc_state->tag, path->name);
+ return;
+ }
+ PCEP_DEBUG("%s Candidate path %s created", pcc_state->tag,
+ path->name);
+ if ((path->first_hop == NULL)
+ && (path->type == SRTE_CANDIDATE_TYPE_DYNAMIC)) {
+ req = push_new_req(pcc_state, path);
+ send_comp_request(ctrl_state, pcc_state, req);
+ } else if (pcc_state->caps.is_stateful)
+ send_report(pcc_state, path);
+ return;
+ case PCEP_PATH_UPDATED:
+ PCEP_DEBUG("%s Candidate path %s updated", pcc_state->tag,
+ path->name);
+ if (pcc_state->caps.is_stateful)
+ send_report(pcc_state, path);
+ return;
+ case PCEP_PATH_REMOVED:
+ PCEP_DEBUG("%s Candidate path %s removed", pcc_state->tag,
+ path->name);
+ path->was_removed = true;
+ if (pcc_state->caps.is_stateful)
+ send_report(pcc_state, path);
+ return;
+ default:
+ flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ "Unexpected pathd event received by pcc %s: %u",
+ pcc_state->tag, type);
+ return;
+ }
+}
+
+
+/* ------------ PCEP event handler ------------ */
+
+void pcep_pcc_pcep_event_handler(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, pcep_event *event)
+{
+ PCEP_DEBUG("%s Received PCEP event: %s", pcc_state->tag,
+ pcep_event_type_name(event->event_type));
+ switch (event->event_type) {
+ case PCC_CONNECTED_TO_PCE:
+ assert(PCEP_PCC_CONNECTING == pcc_state->status);
+ PCEP_DEBUG("%s Connection established", pcc_state->tag);
+ pcc_state->status = PCEP_PCC_SYNCHRONIZING;
+ pcc_state->retry_count = 0;
+ pcc_state->synchronized = false;
+ PCEP_DEBUG("%s Starting PCE synchronization", pcc_state->tag);
+ cancel_session_timeout(ctrl_state, pcc_state);
+ pcep_pcc_calculate_best_pce(ctrl_state->pcc);
+ pcep_thread_start_sync(ctrl_state, pcc_state->id);
+ break;
+ case PCC_SENT_INVALID_OPEN:
+ PCEP_DEBUG("%s Sent invalid OPEN message", pcc_state->tag);
+ PCEP_DEBUG(
+ "%s Reconciling values: keep alive (%d) dead timer (%d) seconds ",
+ pcc_state->tag,
+ pcc_state->sess->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds,
+ pcc_state->sess->pcc_config
+ .dead_timer_pce_negotiated_seconds);
+ pcc_state->pce_opts->config_opts.keep_alive_seconds =
+ pcc_state->sess->pcc_config
+ .keep_alive_pce_negotiated_timer_seconds;
+ pcc_state->pce_opts->config_opts.dead_timer_seconds =
+ pcc_state->sess->pcc_config
+ .dead_timer_pce_negotiated_seconds;
+ break;
+
+ case PCC_RCVD_INVALID_OPEN:
+ PCEP_DEBUG("%s Received invalid OPEN message", pcc_state->tag);
+ PCEP_DEBUG_PCEP("%s PCEP message: %s", pcc_state->tag,
+ format_pcep_message(event->message));
+ break;
+ case PCE_DEAD_TIMER_EXPIRED:
+ case PCE_CLOSED_SOCKET:
+ case PCE_SENT_PCEP_CLOSE:
+ case PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED:
+ case PCC_PCEP_SESSION_CLOSED:
+ case PCC_RCVD_MAX_INVALID_MSGS:
+ case PCC_RCVD_MAX_UNKOWN_MSGS:
+ pcep_pcc_disable(ctrl_state, pcc_state);
+ schedule_reconnect(ctrl_state, pcc_state);
+ schedule_session_timeout(ctrl_state, pcc_state);
+ break;
+ case MESSAGE_RECEIVED:
+ PCEP_DEBUG_PCEP("%s Received PCEP message: %s", pcc_state->tag,
+ format_pcep_message(event->message));
+ if (pcc_state->status == PCEP_PCC_CONNECTING) {
+ if (event->message->msg_header->type == PCEP_TYPE_OPEN)
+ handle_pcep_open(ctrl_state, pcc_state,
+ event->message);
+ break;
+ }
+ assert(pcc_state->status == PCEP_PCC_SYNCHRONIZING
+ || pcc_state->status == PCEP_PCC_OPERATING);
+ handle_pcep_message(ctrl_state, pcc_state, event->message);
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEPLIB_EVENT,
+ "Unexpected event from pceplib: %s",
+ format_pcep_event(event));
+ break;
+ }
+}
+
+
+/*------------------ Multi-PCE --------------------- */
+
+/* Internal util function, returns true if sync is necessary, false otherwise */
+bool update_best_pce(struct pcc_state **pcc, int best)
+{
+ PCEP_DEBUG(" recalculating pce precedence ");
+ if (best) {
+ struct pcc_state *best_pcc_state =
+ pcep_pcc_get_pcc_by_id(pcc, best);
+ if (best_pcc_state->previous_best != best_pcc_state->is_best) {
+ PCEP_DEBUG(" %s Resynch best (%i) previous best (%i)",
+ best_pcc_state->tag, best_pcc_state->id,
+ best_pcc_state->previous_best);
+ return true;
+ } else {
+ PCEP_DEBUG(
+ " %s No Resynch best (%i) previous best (%i)",
+ best_pcc_state->tag, best_pcc_state->id,
+ best_pcc_state->previous_best);
+ }
+ } else {
+ PCEP_DEBUG(" No best pce available, all pce seem disconnected");
+ }
+
+ return false;
+}
+
+int get_best_pce(struct pcc_state **pcc)
+{
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (pcc[i] && pcc[i]->pce_opts) {
+ if (pcc[i]->is_best == true) {
+ return pcc[i]->id;
+ }
+ }
+ }
+ return 0;
+}
+
+int get_pce_count_connected(struct pcc_state **pcc)
+{
+ int count = 0;
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (pcc[i] && pcc[i]->pce_opts
+ && pcc[i]->status != PCEP_PCC_DISCONNECTED) {
+ count++;
+ }
+ }
+ return count;
+}
+
+int get_previous_best_pce(struct pcc_state **pcc)
+{
+ int previous_best_pce = -1;
+
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (pcc[i] && pcc[i]->pce_opts && pcc[i]->previous_best == true
+ && pcc[i]->status != PCEP_PCC_DISCONNECTED) {
+ previous_best_pce = i;
+ break;
+ }
+ }
+ return previous_best_pce != -1 ? pcc[previous_best_pce]->id : 0;
+}
+
+/* Called by path_pcep_controller EV_REMOVE_PCC
+ * Event handler when a PCC is removed. */
+int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state,
+ struct pcc_state **pcc)
+{
+ int new_best_pcc_id = -1;
+ new_best_pcc_id = pcep_pcc_calculate_best_pce(pcc);
+ if (new_best_pcc_id) {
+ if (update_best_pce(ctrl_state->pcc, new_best_pcc_id) == true) {
+ pcep_thread_start_sync(ctrl_state, new_best_pcc_id);
+ }
+ }
+
+ return 0;
+}
+
+/* Called by path_pcep_controller EV_SYNC_PATH
+ * Event handler when a path is sync'd. */
+int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct pcc_state **pcc)
+{
+ int previous_best_pcc_id = -1;
+
+ if (pcc_id == get_best_pce(pcc)) {
+ previous_best_pcc_id = get_previous_best_pce(pcc);
+ if (previous_best_pcc_id != 0) {
+ /* while adding new pce, path has to resync to the
+ * previous best. pcep_thread_start_sync() will be
+ * called by the calling function */
+ if (update_best_pce(ctrl_state->pcc,
+ previous_best_pcc_id)
+ == true) {
+ cancel_comp_requests(
+ ctrl_state,
+ pcep_pcc_get_pcc_by_id(
+ pcc, previous_best_pcc_id));
+ pcep_thread_start_sync(ctrl_state,
+ previous_best_pcc_id);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Called by path_pcep_controller when the TM_CALCULATE_BEST_PCE
+ * timer expires */
+int pcep_pcc_timer_update_best_pce(struct ctrl_state *ctrl_state, int pcc_id)
+{
+ int ret = 0;
+ /* resync whatever was the new best */
+ int prev_best = get_best_pce(ctrl_state->pcc);
+ int best_id = pcep_pcc_calculate_best_pce(ctrl_state->pcc);
+ if (best_id && prev_best != best_id) { // Avoid Multiple call
+ struct pcc_state *pcc_state =
+ pcep_pcc_get_pcc_by_id(ctrl_state->pcc, best_id);
+ if (update_best_pce(ctrl_state->pcc, pcc_state->id) == true) {
+ pcep_thread_start_sync(ctrl_state, pcc_state->id);
+ }
+ }
+
+ return ret;
+}
+
+/* Called by path_pcep_controller::pcep_thread_event_update_pce_options()
+ * Returns the best PCE id */
+int pcep_pcc_calculate_best_pce(struct pcc_state **pcc)
+{
+ int best_precedence = 255; // DEFAULT_PCE_PRECEDENCE;
+ int best_pce = -1;
+ int one_connected_pce = -1;
+ int previous_best_pce = -1;
+ int step_0_best = -1;
+ int step_0_previous = -1;
+ int pcc_count = 0;
+
+ // Get state
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (pcc[i] && pcc[i]->pce_opts) {
+ zlog_debug(
+ "multi-pce: calculate all : i (%i) is_best (%i) previous_best (%i) ",
+ i, pcc[i]->is_best, pcc[i]->previous_best);
+ pcc_count++;
+
+ if (pcc[i]->is_best == true) {
+ step_0_best = i;
+ }
+ if (pcc[i]->previous_best == true) {
+ step_0_previous = i;
+ }
+ }
+ }
+
+ if (!pcc_count) {
+ return 0;
+ }
+
+ // Calculate best
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (pcc[i] && pcc[i]->pce_opts
+ && pcc[i]->status != PCEP_PCC_DISCONNECTED) {
+ one_connected_pce = i; // In case none better
+ if (pcc[i]->pce_opts->precedence <= best_precedence) {
+ if (best_pce != -1
+ && pcc[best_pce]->pce_opts->precedence
+ == pcc[i]->pce_opts
+ ->precedence) {
+ if (ipaddr_cmp(
+ &pcc[i]->pce_opts->addr,
+ &pcc[best_pce]
+ ->pce_opts->addr)
+ > 0)
+ // collide of precedences so
+ // compare ip
+ best_pce = i;
+ } else {
+ if (!pcc[i]->previous_best) {
+ best_precedence =
+ pcc[i]->pce_opts
+ ->precedence;
+ best_pce = i;
+ }
+ }
+ }
+ }
+ }
+
+ zlog_debug(
+ "multi-pce: calculate data : sb (%i) sp (%i) oc (%i) b (%i) ",
+ step_0_best, step_0_previous, one_connected_pce, best_pce);
+
+ // Changed of state so ...
+ if (step_0_best != best_pce) {
+ // Calculate previous
+ previous_best_pce = step_0_best;
+ // Clean state
+ if (step_0_best != -1) {
+ pcc[step_0_best]->is_best = false;
+ }
+ if (step_0_previous != -1) {
+ pcc[step_0_previous]->previous_best = false;
+ }
+
+ // Set previous
+ if (previous_best_pce != -1
+ && pcc[previous_best_pce]->status
+ == PCEP_PCC_DISCONNECTED) {
+ pcc[previous_best_pce]->previous_best = true;
+ zlog_debug("multi-pce: previous best pce (%i) ",
+ previous_best_pce + 1);
+ }
+
+
+ // Set best
+ if (best_pce != -1) {
+ pcc[best_pce]->is_best = true;
+ zlog_debug("multi-pce: best pce (%i) ", best_pce + 1);
+ } else {
+ if (one_connected_pce != -1) {
+ best_pce = one_connected_pce;
+ pcc[one_connected_pce]->is_best = true;
+ zlog_debug(
+ "multi-pce: one connected best pce (default) (%i) ",
+ one_connected_pce + 1);
+ } else {
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (pcc[i] && pcc[i]->pce_opts) {
+ best_pce = i;
+ pcc[i]->is_best = true;
+ zlog_debug(
+ "(disconnected) best pce (default) (%i) ",
+ i + 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ((best_pce == -1) ? 0 : pcc[best_pce]->id);
+}
+
+int pcep_pcc_get_pcc_id_by_ip_port(struct pcc_state **pcc,
+ struct pce_opts *pce_opts)
+{
+ if (pcc == NULL) {
+ return 0;
+ }
+
+ for (int idx = 0; idx < MAX_PCC; idx++) {
+ if (pcc[idx]) {
+ if ((ipaddr_cmp((const struct ipaddr *)&pcc[idx]
+ ->pce_opts->addr,
+ (const struct ipaddr *)&pce_opts->addr)
+ == 0)
+ && pcc[idx]->pce_opts->port == pce_opts->port) {
+ zlog_debug("found pcc_id (%d) idx (%d)",
+ pcc[idx]->id, idx);
+ return pcc[idx]->id;
+ }
+ }
+ }
+ return 0;
+}
+
+int pcep_pcc_get_pcc_id_by_idx(struct pcc_state **pcc, int idx)
+{
+ if (pcc == NULL || idx < 0) {
+ return 0;
+ }
+
+ return pcc[idx] ? pcc[idx]->id : 0;
+}
+
+struct pcc_state *pcep_pcc_get_pcc_by_id(struct pcc_state **pcc, int id)
+{
+ if (pcc == NULL || id < 0) {
+ return NULL;
+ }
+
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (pcc[i]) {
+ if (pcc[i]->id == id) {
+ zlog_debug("found id (%d) pcc_idx (%d)",
+ pcc[i]->id, i);
+ return pcc[i];
+ }
+ }
+ }
+
+ return NULL;
+}
+
+struct pcc_state *pcep_pcc_get_pcc_by_name(struct pcc_state **pcc,
+ const char *pce_name)
+{
+ if (pcc == NULL || pce_name == NULL) {
+ return NULL;
+ }
+
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (pcc[i] == NULL) {
+ continue;
+ }
+
+ if (strcmp(pcc[i]->pce_opts->pce_name, pce_name) == 0) {
+ return pcc[i];
+ }
+ }
+
+ return NULL;
+}
+
+int pcep_pcc_get_pcc_idx_by_id(struct pcc_state **pcc, int id)
+{
+ if (pcc == NULL) {
+ return -1;
+ }
+
+ for (int idx = 0; idx < MAX_PCC; idx++) {
+ if (pcc[idx]) {
+ if (pcc[idx]->id == id) {
+ zlog_debug("found pcc_id (%d) array_idx (%d)",
+ pcc[idx]->id, idx);
+ return idx;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int pcep_pcc_get_free_pcc_idx(struct pcc_state **pcc)
+{
+ assert(pcc != NULL);
+
+ for (int idx = 0; idx < MAX_PCC; idx++) {
+ if (pcc[idx] == NULL) {
+ zlog_debug("new pcc_idx (%d)", idx);
+ return idx;
+ }
+ }
+
+ return -1;
+}
+
+int pcep_pcc_get_pcc_id(struct pcc_state *pcc)
+{
+ return ((pcc == NULL) ? 0 : pcc->id);
+}
+
+void pcep_pcc_copy_pcc_info(struct pcc_state **pcc,
+ struct pcep_pcc_info *pcc_info)
+{
+ struct pcc_state *pcc_state =
+ pcep_pcc_get_pcc_by_name(pcc, pcc_info->pce_name);
+ if (!pcc_state) {
+ return;
+ }
+
+ pcc_info->ctrl_state = NULL;
+ pcc_info->msd = pcc_state->pcc_opts->msd;
+ pcc_info->pcc_port = pcc_state->pcc_opts->port;
+ pcc_info->next_plspid = pcc_state->next_plspid;
+ pcc_info->next_reqid = pcc_state->next_reqid;
+ pcc_info->status = pcc_state->status;
+ pcc_info->pcc_id = pcc_state->id;
+ pcc_info->is_best_multi_pce = pcc_state->is_best;
+ pcc_info->previous_best = pcc_state->previous_best;
+ pcc_info->precedence =
+ pcc_state->pce_opts ? pcc_state->pce_opts->precedence : 0;
+ memcpy(&pcc_info->pcc_addr, &pcc_state->pcc_addr_tr,
+ sizeof(struct ipaddr));
+}
+
+
+/*------------------ PCEP Message handlers --------------------- */
+
+void handle_pcep_open(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct pcep_message *msg)
+{
+ assert(msg->msg_header->type == PCEP_TYPE_OPEN);
+ pcep_lib_parse_capabilities(msg, &pcc_state->caps);
+ PCEP_DEBUG("PCE capabilities: %s, %s%s",
+ pcc_state->caps.is_stateful ? "stateful" : "stateless",
+ pcc_state->caps.supported_ofs_are_known
+ ? (pcc_state->caps.supported_ofs == 0
+ ? "no objective functions supported"
+ : "supported objective functions are ")
+ : "supported objective functions are unknown",
+ format_objfun_set(pcc_state->caps.supported_ofs));
+}
+
+void handle_pcep_message(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct pcep_message *msg)
+{
+ if (pcc_state->status != PCEP_PCC_OPERATING)
+ return;
+
+ switch (msg->msg_header->type) {
+ case PCEP_TYPE_INITIATE:
+ handle_pcep_lsp_initiate(ctrl_state, pcc_state, msg);
+ break;
+ case PCEP_TYPE_UPDATE:
+ handle_pcep_lsp_update(ctrl_state, pcc_state, msg);
+ break;
+ case PCEP_TYPE_PCREP:
+ handle_pcep_comp_reply(ctrl_state, pcc_state, msg);
+ break;
+ default:
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_MESSAGE,
+ "Unexpected pcep message from pceplib: %s",
+ format_pcep_message(msg));
+ break;
+ }
+}
+
+void handle_pcep_lsp_update(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct pcep_message *msg)
+{
+ char err[MAX_ERROR_MSG_SIZE] = "";
+ struct path *path;
+ path = pcep_lib_parse_path(msg);
+ lookup_nbkey(pcc_state, path);
+ /* TODO: Investigate if this is safe to do in the controller thread */
+ path_pcep_config_lookup(path);
+ specialize_incoming_path(pcc_state, path);
+ PCEP_DEBUG("%s Received LSP update", pcc_state->tag);
+ PCEP_DEBUG_PATH("%s", format_path(path));
+
+ if (validate_incoming_path(pcc_state, path, err, sizeof(err)))
+ pcep_thread_update_path(ctrl_state, pcc_state->id, path);
+ else {
+ /* FIXME: Monitor the amount of errors from the PCE and
+ * possibly disconnect and blacklist */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "Unsupported PCEP protocol feature: %s", err);
+ pcep_free_path(path);
+ }
+}
+
+void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct pcep_message *msg)
+{
+ PCEP_DEBUG("%s Received LSP initiate, not supported yet",
+ pcc_state->tag);
+
+ /* TODO when we support both PCC and PCE initiated sessions,
+ * we should first check the session type before
+ * rejecting this message. */
+ send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_NOT_PCE_INITIATED);
+}
+
+void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct pcep_message *msg)
+{
+ char err[MAX_ERROR_MSG_SIZE] = "";
+ struct req_entry *req;
+ struct path *path;
+
+ path = pcep_lib_parse_path(msg);
+ req = pop_req(pcc_state, path->req_id);
+ if (req == NULL) {
+ /* TODO: check the rate of bad computation reply and close
+ * the connection if more that a given rate.
+ */
+ PCEP_DEBUG(
+ "%s Received computation reply for unknown request "
+ "%d",
+ pcc_state->tag, path->req_id);
+ PCEP_DEBUG_PATH("%s", format_path(path));
+ send_pcep_error(pcc_state, PCEP_ERRT_UNKNOWN_REQ_REF,
+ PCEP_ERRV_UNASSIGNED);
+ return;
+ }
+
+ /* Cancel the computation request timeout */
+ pcep_thread_cancel_timer(&req->t_retry);
+
+ /* Transfer relevent metadata from the request to the response */
+ path->nbkey = req->path->nbkey;
+ path->plsp_id = req->path->plsp_id;
+ path->type = req->path->type;
+ path->name = XSTRDUP(MTYPE_PCEP, req->path->name);
+ specialize_incoming_path(pcc_state, path);
+
+ PCEP_DEBUG("%s Received computation reply %d (no-path: %s)",
+ pcc_state->tag, path->req_id,
+ path->no_path ? "true" : "false");
+ PCEP_DEBUG_PATH("%s", format_path(path));
+
+ if (path->no_path) {
+ PCEP_DEBUG("%s Computation for path %s did not find any result",
+ pcc_state->tag, path->name);
+ } else if (validate_incoming_path(pcc_state, path, err, sizeof(err))) {
+ /* Updating a dynamic path will automatically delegate it */
+ pcep_thread_update_path(ctrl_state, pcc_state->id, path);
+ free_req_entry(req);
+ return;
+ } else {
+ /* FIXME: Monitor the amount of errors from the PCE and
+ * possibly disconnect and blacklist */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "Unsupported PCEP protocol feature: %s", err);
+ }
+
+ pcep_free_path(path);
+
+ /* Delegate the path regardless of the outcome */
+ /* TODO: For now we are using the path from the request, when
+ * pathd API is thread safe, we could get a new path */
+ if (pcc_state->caps.is_stateful) {
+ PCEP_DEBUG("%s Delegating undefined dynamic path %s to PCE %s",
+ pcc_state->tag, path->name, pcc_state->originator);
+ path = pcep_copy_path(req->path);
+ path->is_delegated = true;
+ send_report(pcc_state, path);
+ pcep_free_path(path);
+ }
+
+ free_req_entry(req);
+}
+
+
+/* ------------ Internal Functions ------------ */
+
+const char *ipaddr_type_name(struct ipaddr *addr)
+{
+ if (IS_IPADDR_V4(addr))
+ return "IPv4";
+ if (IS_IPADDR_V6(addr))
+ return "IPv6";
+ return "undefined";
+}
+
+bool filter_path(struct pcc_state *pcc_state, struct path *path)
+{
+ return (IS_IPADDR_V4(&path->nbkey.endpoint)
+ && CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4))
+ || (IS_IPADDR_V6(&path->nbkey.endpoint)
+ && CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6));
+}
+
+void select_pcc_addresses(struct pcc_state *pcc_state)
+{
+ /* If no IPv4 address was specified, try to get one from zebra */
+ if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)) {
+ if (get_ipv4_router_id(&pcc_state->pcc_addr_v4)) {
+ SET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4);
+ }
+ }
+
+ /* If no IPv6 address was specified, try to get one from zebra */
+ if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)) {
+ if (get_ipv6_router_id(&pcc_state->pcc_addr_v6)) {
+ SET_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6);
+ }
+ }
+}
+
+void select_transport_address(struct pcc_state *pcc_state)
+{
+ struct ipaddr *taddr = &pcc_state->pcc_addr_tr;
+
+ select_pcc_addresses(pcc_state);
+
+ taddr->ipa_type = IPADDR_NONE;
+
+ /* Select a transport source address in function of the configured PCE
+ * address */
+ if (IS_IPADDR_V4(&pcc_state->pce_opts->addr)) {
+ if (CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)) {
+ taddr->ipa_type = IPADDR_V4;
+ taddr->ipaddr_v4 = pcc_state->pcc_addr_v4;
+ }
+ } else {
+ if (CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)) {
+ taddr->ipa_type = IPADDR_V6;
+ taddr->ipaddr_v6 = pcc_state->pcc_addr_v6;
+ }
+ }
+}
+
+void update_tag(struct pcc_state *pcc_state)
+{
+ if (pcc_state->pce_opts != NULL) {
+ assert(!IS_IPADDR_NONE(&pcc_state->pce_opts->addr));
+ if (IS_IPADDR_V6(&pcc_state->pce_opts->addr)) {
+ snprintfrr(pcc_state->tag, sizeof(pcc_state->tag),
+ "%pI6:%i (%u)",
+ &pcc_state->pce_opts->addr.ipaddr_v6,
+ pcc_state->pce_opts->port, pcc_state->id);
+ } else {
+ snprintfrr(pcc_state->tag, sizeof(pcc_state->tag),
+ "%pI4:%i (%u)",
+ &pcc_state->pce_opts->addr.ipaddr_v4,
+ pcc_state->pce_opts->port, pcc_state->id);
+ }
+ } else {
+ snprintfrr(pcc_state->tag, sizeof(pcc_state->tag), "(%u)",
+ pcc_state->id);
+ }
+}
+
+void update_originator(struct pcc_state *pcc_state)
+{
+ char *originator;
+ if (pcc_state->originator != NULL) {
+ XFREE(MTYPE_PCEP, pcc_state->originator);
+ pcc_state->originator = NULL;
+ }
+ if (pcc_state->pce_opts == NULL)
+ return;
+ originator = XCALLOC(MTYPE_PCEP, 52);
+ assert(!IS_IPADDR_NONE(&pcc_state->pce_opts->addr));
+ if (IS_IPADDR_V6(&pcc_state->pce_opts->addr)) {
+ snprintfrr(originator, 52, "%pI6:%i",
+ &pcc_state->pce_opts->addr.ipaddr_v6,
+ pcc_state->pce_opts->port);
+ } else {
+ snprintfrr(originator, 52, "%pI4:%i",
+ &pcc_state->pce_opts->addr.ipaddr_v4,
+ pcc_state->pce_opts->port);
+ }
+ pcc_state->originator = originator;
+}
+
+void schedule_reconnect(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state)
+{
+ pcc_state->retry_count++;
+ pcep_thread_schedule_reconnect(ctrl_state, pcc_state->id,
+ pcc_state->retry_count,
+ &pcc_state->t_reconnect);
+ if (pcc_state->retry_count == 1) {
+ pcep_thread_schedule_sync_best_pce(
+ ctrl_state, pcc_state->id,
+ pcc_state->pce_opts->config_opts
+ .delegation_timeout_seconds,
+ &pcc_state->t_update_best);
+ }
+}
+
+void schedule_session_timeout(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state)
+{
+ /* No need to schedule timeout if multiple PCEs are connected */
+ if (get_pce_count_connected(ctrl_state->pcc)) {
+ PCEP_DEBUG_PCEP(
+ "schedule_session_timeout not setting timer for multi-pce mode");
+
+ return;
+ }
+
+ pcep_thread_schedule_session_timeout(
+ ctrl_state, pcep_pcc_get_pcc_id(pcc_state),
+ pcc_state->pce_opts->config_opts
+ .session_timeout_inteval_seconds,
+ &pcc_state->t_session_timeout);
+}
+
+void cancel_session_timeout(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state)
+{
+ /* No need to schedule timeout if multiple PCEs are connected */
+ if (pcc_state->t_session_timeout == NULL) {
+ PCEP_DEBUG_PCEP("cancel_session_timeout timer thread NULL");
+ return;
+ }
+
+ PCEP_DEBUG_PCEP("Cancel session_timeout timer");
+ pcep_thread_cancel_timer(&pcc_state->t_session_timeout);
+ pcc_state->t_session_timeout = NULL;
+}
+
+void send_pcep_message(struct pcc_state *pcc_state, struct pcep_message *msg)
+{
+ if (pcc_state->sess != NULL) {
+ PCEP_DEBUG_PCEP("%s Sending PCEP message: %s", pcc_state->tag,
+ format_pcep_message(msg));
+ send_message(pcc_state->sess, msg, true);
+ }
+}
+
+void send_pcep_error(struct pcc_state *pcc_state,
+ enum pcep_error_type error_type,
+ enum pcep_error_value error_value)
+{
+ struct pcep_message *msg;
+ PCEP_DEBUG("%s Sending PCEP error type %s (%d) value %s (%d)",
+ pcc_state->tag, pcep_error_type_name(error_type), error_type,
+ pcep_error_value_name(error_type, error_value), error_value);
+ msg = pcep_lib_format_error(error_type, error_value);
+ send_pcep_message(pcc_state, msg);
+}
+
+void send_report(struct pcc_state *pcc_state, struct path *path)
+{
+ struct pcep_message *report;
+
+ path->req_id = 0;
+ specialize_outgoing_path(pcc_state, path);
+ PCEP_DEBUG_PATH("%s Sending path %s: %s", pcc_state->tag, path->name,
+ format_path(path));
+ report = pcep_lib_format_report(&pcc_state->caps, path);
+ send_pcep_message(pcc_state, report);
+}
+
+/* Updates the path for the PCE, updating the delegation and creation flags */
+void specialize_outgoing_path(struct pcc_state *pcc_state, struct path *path)
+{
+ bool is_delegated = false;
+ bool was_created = false;
+
+ lookup_plspid(pcc_state, path);
+
+ set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr);
+ path->sender = pcc_state->pcc_addr_tr;
+
+ /* TODO: When the pathd API have a way to mark a path as
+ * delegated, use it instead of considering all dynamic path
+ * delegated. We need to disable the originator check for now,
+ * because path could be delegated without having any originator yet */
+ // if ((path->originator == NULL)
+ // || (strcmp(path->originator, pcc_state->originator) == 0)) {
+ // is_delegated = (path->type == SRTE_CANDIDATE_TYPE_DYNAMIC)
+ // && (path->first_hop != NULL);
+ // /* it seems the PCE consider updating an LSP a creation ?!?
+ // at least Cisco does... */
+ // was_created = path->update_origin == SRTE_ORIGIN_PCEP;
+ // }
+ is_delegated = (path->type == SRTE_CANDIDATE_TYPE_DYNAMIC);
+ was_created = path->update_origin == SRTE_ORIGIN_PCEP;
+
+ path->pcc_id = pcc_state->id;
+ path->go_active = is_delegated && pcc_state->is_best;
+ path->is_delegated = is_delegated && pcc_state->is_best;
+ path->was_created = was_created;
+}
+
+/* Updates the path for the PCC */
+void specialize_incoming_path(struct pcc_state *pcc_state, struct path *path)
+{
+ set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr);
+ path->sender = pcc_state->pce_opts->addr;
+ path->pcc_id = pcc_state->id;
+ path->update_origin = SRTE_ORIGIN_PCEP;
+ path->originator = XSTRDUP(MTYPE_PCEP, pcc_state->originator);
+}
+
+/* Ensure the path can be handled by the PCC and if not, sends an error */
+bool validate_incoming_path(struct pcc_state *pcc_state, struct path *path,
+ char *errbuff, size_t buffsize)
+{
+ struct path_hop *hop;
+ enum pcep_error_type err_type = 0;
+ enum pcep_error_value err_value = PCEP_ERRV_UNASSIGNED;
+
+ for (hop = path->first_hop; hop != NULL; hop = hop->next) {
+ /* Hops without SID are not supported */
+ if (!hop->has_sid) {
+ snprintfrr(errbuff, buffsize, "SR segment without SID");
+ err_type = PCEP_ERRT_RECEPTION_OF_INV_OBJECT;
+ err_value = PCEP_ERRV_DISJOINTED_CONF_TLV_MISSING;
+ break;
+ }
+ /* Hops with non-MPLS SID are not supported */
+ if (!hop->is_mpls) {
+ snprintfrr(errbuff, buffsize,
+ "SR segment with non-MPLS SID");
+ err_type = PCEP_ERRT_RECEPTION_OF_INV_OBJECT;
+ err_value = PCEP_ERRV_UNSUPPORTED_NAI;
+ break;
+ }
+ }
+
+ if (err_type != 0) {
+ send_pcep_error(pcc_state, err_type, err_value);
+ return false;
+ }
+
+ return true;
+}
+
+void send_comp_request(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct req_entry *req)
+{
+ assert(req != NULL);
+
+ if (req->t_retry)
+ return;
+
+ assert(req->path != NULL);
+ assert(req->path->req_id > 0);
+ assert(RB_FIND(req_entry_head, &pcc_state->requests, req) == req);
+ assert(lookup_reqid(pcc_state, req->path) == req->path->req_id);
+
+ int timeout;
+ char buff[40];
+ struct pcep_message *msg;
+
+ if (!pcc_state->is_best) {
+ return;
+ }
+ /* TODO: Add a timer to retry the computation request ? */
+
+ specialize_outgoing_path(pcc_state, req->path);
+
+ PCEP_DEBUG(
+ "%s Sending computation request %d for path %s to %s (retry %d)",
+ pcc_state->tag, req->path->req_id, req->path->name,
+ ipaddr2str(&req->path->nbkey.endpoint, buff, sizeof(buff)),
+ req->retry_count);
+ PCEP_DEBUG_PATH("%s Computation request path %s: %s", pcc_state->tag,
+ req->path->name, format_path(req->path));
+
+ msg = pcep_lib_format_request(&pcc_state->caps, req->path);
+ send_pcep_message(pcc_state, msg);
+ req->was_sent = true;
+
+ /* TODO: Enable this back when the pcep config changes are merged back
+ */
+ // timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds;
+ timeout = 30;
+ pcep_thread_schedule_timeout(ctrl_state, pcc_state->id,
+ TO_COMPUTATION_REQUEST, timeout,
+ (void *)req, &req->t_retry);
+}
+
+void cancel_comp_requests(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state)
+{
+ struct req_entry *req, *safe_req;
+
+ RB_FOREACH_SAFE (req, req_entry_head, &pcc_state->requests, safe_req) {
+ cancel_comp_request(ctrl_state, pcc_state, req);
+ RB_REMOVE(req_entry_head, &pcc_state->requests, req);
+ remove_reqid_mapping(pcc_state, req->path);
+ free_req_entry(req);
+ }
+}
+
+void cancel_comp_request(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct req_entry *req)
+{
+ char buff[40];
+ struct pcep_message *msg;
+
+ if (req->was_sent) {
+ /* TODO: Send a computation request cancelation
+ * notification to the PCE */
+ pcep_thread_cancel_timer(&req->t_retry);
+ }
+
+ PCEP_DEBUG(
+ "%s Canceling computation request %d for path %s to %s (retry %d)",
+ pcc_state->tag, req->path->req_id, req->path->name,
+ ipaddr2str(&req->path->nbkey.endpoint, buff, sizeof(buff)),
+ req->retry_count);
+ PCEP_DEBUG_PATH("%s Canceled computation request path %s: %s",
+ pcc_state->tag, req->path->name,
+ format_path(req->path));
+
+ msg = pcep_lib_format_request_cancelled(req->path->req_id);
+ send_pcep_message(pcc_state, msg);
+}
+
+void set_pcc_address(struct pcc_state *pcc_state, struct lsp_nb_key *nbkey,
+ struct ipaddr *addr)
+{
+ select_pcc_addresses(pcc_state);
+ if (IS_IPADDR_V6(&nbkey->endpoint)) {
+ assert(CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6));
+ addr->ipa_type = IPADDR_V6;
+ addr->ipaddr_v6 = pcc_state->pcc_addr_v6;
+ } else if (IS_IPADDR_V4(&nbkey->endpoint)) {
+ assert(CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4));
+ addr->ipa_type = IPADDR_V4;
+ addr->ipaddr_v4 = pcc_state->pcc_addr_v4;
+ } else {
+ addr->ipa_type = IPADDR_NONE;
+ }
+}
+
+
+/* ------------ Data Structure Helper Functions ------------ */
+
+void lookup_plspid(struct pcc_state *pcc_state, struct path *path)
+{
+ struct plspid_map_data key, *plspid_mapping;
+ struct nbkey_map_data *nbkey_mapping;
+
+ if (path->nbkey.color != 0) {
+ key.nbkey = path->nbkey;
+ plspid_mapping = plspid_map_find(&pcc_state->plspid_map, &key);
+ if (plspid_mapping == NULL) {
+ plspid_mapping =
+ XCALLOC(MTYPE_PCEP, sizeof(*plspid_mapping));
+ plspid_mapping->nbkey = key.nbkey;
+ plspid_mapping->plspid = pcc_state->next_plspid;
+ plspid_map_add(&pcc_state->plspid_map, plspid_mapping);
+ nbkey_mapping =
+ XCALLOC(MTYPE_PCEP, sizeof(*nbkey_mapping));
+ nbkey_mapping->nbkey = key.nbkey;
+ nbkey_mapping->plspid = pcc_state->next_plspid;
+ nbkey_map_add(&pcc_state->nbkey_map, nbkey_mapping);
+ pcc_state->next_plspid++;
+ // FIXME: Send some error to the PCE isntead of crashing
+ assert(pcc_state->next_plspid <= 1048576);
+ }
+ path->plsp_id = plspid_mapping->plspid;
+ }
+}
+
+void lookup_nbkey(struct pcc_state *pcc_state, struct path *path)
+{
+ struct nbkey_map_data key, *mapping;
+ // TODO: Should give an error to the PCE instead of crashing
+ assert(path->plsp_id != 0);
+ key.plspid = path->plsp_id;
+ mapping = nbkey_map_find(&pcc_state->nbkey_map, &key);
+ assert(mapping != NULL);
+ path->nbkey = mapping->nbkey;
+}
+
+void free_req_entry(struct req_entry *req)
+{
+ pcep_free_path(req->path);
+ XFREE(MTYPE_PCEP, req);
+}
+
+struct req_entry *push_new_req(struct pcc_state *pcc_state, struct path *path)
+{
+ struct req_entry *req;
+
+ req = XCALLOC(MTYPE_PCEP, sizeof(*req));
+ req->retry_count = 0;
+ req->path = pcep_copy_path(path);
+ repush_req(pcc_state, req);
+
+ return req;
+}
+
+void repush_req(struct pcc_state *pcc_state, struct req_entry *req)
+{
+ uint32_t reqid = pcc_state->next_reqid;
+ void *res;
+
+ req->was_sent = false;
+ req->path->req_id = reqid;
+ res = RB_INSERT(req_entry_head, &pcc_state->requests, req);
+ assert(res == NULL);
+ assert(add_reqid_mapping(pcc_state, req->path) == true);
+
+ pcc_state->next_reqid += 1;
+ /* Wrapping is allowed, but 0 is not a valid id */
+ if (pcc_state->next_reqid == 0)
+ pcc_state->next_reqid = 1;
+}
+
+struct req_entry *pop_req(struct pcc_state *pcc_state, uint32_t reqid)
+{
+ struct path path = {.req_id = reqid};
+ struct req_entry key = {.path = &path};
+ struct req_entry *req;
+
+ req = RB_FIND(req_entry_head, &pcc_state->requests, &key);
+ if (req == NULL)
+ return NULL;
+ RB_REMOVE(req_entry_head, &pcc_state->requests, req);
+ remove_reqid_mapping(pcc_state, req->path);
+
+ return req;
+}
+
+bool add_reqid_mapping(struct pcc_state *pcc_state, struct path *path)
+{
+ struct req_map_data *mapping;
+ mapping = XCALLOC(MTYPE_PCEP, sizeof(*mapping));
+ mapping->nbkey = path->nbkey;
+ mapping->reqid = path->req_id;
+ if (req_map_add(&pcc_state->req_map, mapping) != NULL) {
+ XFREE(MTYPE_PCEP, mapping);
+ return false;
+ }
+ return true;
+}
+
+void remove_reqid_mapping(struct pcc_state *pcc_state, struct path *path)
+{
+ struct req_map_data key, *mapping;
+ key.nbkey = path->nbkey;
+ mapping = req_map_find(&pcc_state->req_map, &key);
+ if (mapping != NULL) {
+ req_map_del(&pcc_state->req_map, mapping);
+ XFREE(MTYPE_PCEP, mapping);
+ }
+}
+
+uint32_t lookup_reqid(struct pcc_state *pcc_state, struct path *path)
+{
+ struct req_map_data key, *mapping;
+ key.nbkey = path->nbkey;
+ mapping = req_map_find(&pcc_state->req_map, &key);
+ if (mapping != NULL)
+ return mapping->reqid;
+ return 0;
+}
+
+bool has_pending_req_for(struct pcc_state *pcc_state, struct path *path)
+{
+ return lookup_reqid(pcc_state, path) != 0;
+}
+
+
+/* ------------ Data Structure Callbacks ------------ */
+
+#define CMP_RETURN(A, B) \
+ if (A != B) \
+ return (A < B) ? -1 : 1
+
+static uint32_t hash_nbkey(const struct lsp_nb_key *nbkey)
+{
+ uint32_t hash;
+ hash = jhash_2words(nbkey->color, nbkey->preference, 0x55aa5a5a);
+ switch (nbkey->endpoint.ipa_type) {
+ case IPADDR_V4:
+ return jhash(&nbkey->endpoint.ipaddr_v4,
+ sizeof(nbkey->endpoint.ipaddr_v4), hash);
+ case IPADDR_V6:
+ return jhash(&nbkey->endpoint.ipaddr_v6,
+ sizeof(nbkey->endpoint.ipaddr_v6), hash);
+ default:
+ return hash;
+ }
+}
+
+static int cmp_nbkey(const struct lsp_nb_key *a, const struct lsp_nb_key *b)
+{
+ CMP_RETURN(a->color, b->color);
+ int cmp = ipaddr_cmp(&a->endpoint, &b->endpoint);
+ if (cmp != 0)
+ return cmp;
+ CMP_RETURN(a->preference, b->preference);
+ return 0;
+}
+
+int plspid_map_cmp(const struct plspid_map_data *a,
+ const struct plspid_map_data *b)
+{
+ return cmp_nbkey(&a->nbkey, &b->nbkey);
+}
+
+uint32_t plspid_map_hash(const struct plspid_map_data *e)
+{
+ return hash_nbkey(&e->nbkey);
+}
+
+int nbkey_map_cmp(const struct nbkey_map_data *a,
+ const struct nbkey_map_data *b)
+{
+ CMP_RETURN(a->plspid, b->plspid);
+ return 0;
+}
+
+uint32_t nbkey_map_hash(const struct nbkey_map_data *e)
+{
+ return e->plspid;
+}
+
+int req_map_cmp(const struct req_map_data *a, const struct req_map_data *b)
+{
+ return cmp_nbkey(&a->nbkey, &b->nbkey);
+}
+
+uint32_t req_map_hash(const struct req_map_data *e)
+{
+ return hash_nbkey(&e->nbkey);
+}
diff --git a/pathd/path_pcep_pcc.h b/pathd/path_pcep_pcc.h
new file mode 100644
index 0000000000..a466d92d50
--- /dev/null
+++ b/pathd/path_pcep_pcc.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; 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_PCC_H_
+#define _PATH_PCEP_PCC_H_
+
+#include "typesafe.h"
+#include "pathd/path_pcep.h"
+
+enum pcc_status {
+ PCEP_PCC_INITIALIZED = 0,
+ PCEP_PCC_DISCONNECTED,
+ PCEP_PCC_CONNECTING,
+ PCEP_PCC_SYNCHRONIZING,
+ PCEP_PCC_OPERATING
+};
+
+PREDECL_HASH(plspid_map)
+PREDECL_HASH(nbkey_map)
+PREDECL_HASH(req_map)
+
+struct plspid_map_data {
+ struct plspid_map_item mi;
+ struct lsp_nb_key nbkey;
+ uint32_t plspid;
+};
+
+struct nbkey_map_data {
+ struct nbkey_map_item mi;
+ struct lsp_nb_key nbkey;
+ uint32_t plspid;
+};
+
+struct req_map_data {
+ struct req_map_item mi;
+ struct lsp_nb_key nbkey;
+ uint32_t reqid;
+};
+
+struct req_entry {
+ RB_ENTRY(req_entry) entry;
+ struct thread *t_retry;
+ int retry_count;
+ bool was_sent;
+ struct path *path;
+};
+RB_HEAD(req_entry_head, req_entry);
+RB_PROTOTYPE(req_entry_head, req_entry, entry, req_entry_compare);
+
+struct pcc_state {
+ int id;
+ char tag[MAX_TAG_SIZE];
+ enum pcc_status status;
+ uint16_t flags;
+#define F_PCC_STATE_HAS_IPV4 0x0002
+#define F_PCC_STATE_HAS_IPV6 0x0004
+ struct pcc_opts *pcc_opts;
+ struct pce_opts *pce_opts;
+ struct in_addr pcc_addr_v4;
+ struct in6_addr pcc_addr_v6;
+ /* PCC transport source address */
+ struct ipaddr pcc_addr_tr;
+ char *originator;
+ pcep_session *sess;
+ uint32_t retry_count;
+ bool synchronized;
+ struct thread *t_reconnect;
+ struct thread *t_update_best;
+ struct thread *t_session_timeout;
+ uint32_t next_reqid;
+ uint32_t next_plspid;
+ struct plspid_map_head plspid_map;
+ struct nbkey_map_head nbkey_map;
+ struct req_map_head req_map;
+ struct req_entry_head requests;
+ struct pcep_caps caps;
+ bool is_best;
+ bool previous_best;
+};
+
+struct pcc_state *pcep_pcc_initialize(struct ctrl_state *ctrl_state,
+ int pcc_id);
+void pcep_pcc_finalize(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state);
+int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state);
+int pcep_pcc_disable(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state);
+int pcep_pcc_update(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state,
+ struct pcc_opts *pcc_opts, struct pce_opts *pce_opts);
+void pcep_pcc_reconnect(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state);
+void pcep_pcc_pcep_event_handler(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ pcep_event *event);
+void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ enum pcep_pathd_event_type type,
+ struct path *path);
+void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ enum pcep_ctrl_timer_type type, void *param);
+void pcep_pcc_sync_path(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct path *path);
+void pcep_pcc_sync_done(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state);
+void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct path *path);
+int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct pcc_state **pcc_state_list);
+int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state,
+ struct pcc_state **pcc_state_list);
+int pcep_pcc_timer_update_best_pce(struct ctrl_state *ctrl_state, int pcc_id);
+int pcep_pcc_calculate_best_pce(struct pcc_state **pcc);
+int pcep_pcc_get_pcc_id_by_ip_port(struct pcc_state **pcc,
+ struct pce_opts *pce_opts);
+int pcep_pcc_get_pcc_id_by_idx(struct pcc_state **pcc, int idx);
+struct pcc_state *pcep_pcc_get_pcc_by_id(struct pcc_state **pcc, int id);
+struct pcc_state *pcep_pcc_get_pcc_by_name(struct pcc_state **pcc,
+ const char *pce_name);
+int pcep_pcc_get_pcc_idx_by_id(struct pcc_state **pcc, int id);
+int pcep_pcc_get_free_pcc_idx(struct pcc_state **pcc);
+int pcep_pcc_get_pcc_id(struct pcc_state *pcc);
+void pcep_pcc_copy_pcc_info(struct pcc_state **pcc,
+ struct pcep_pcc_info *pcc_info);
+
+#endif // _PATH_PCEP_PCC_H_
diff --git a/pathd/path_zebra.c b/pathd/path_zebra.c
new file mode 100644
index 0000000000..276bc9289c
--- /dev/null
+++ b/pathd/path_zebra.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "thread.h"
+#include "log.h"
+#include "lib_errors.h"
+#include "if.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "network.h"
+#include "stream.h"
+#include "linklist.h"
+#include "nexthop.h"
+#include "vrf.h"
+#include "typesafe.h"
+
+#include "pathd/pathd.h"
+#include "pathd/path_zebra.h"
+
+static struct zclient *zclient;
+static struct zclient *zclient_sync;
+
+/* Global Variables */
+bool g_has_router_id_v4 = false;
+bool g_has_router_id_v6 = false;
+struct in_addr g_router_id_v4;
+struct in6_addr g_router_id_v6;
+pthread_mutex_t g_router_id_v4_mtx = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t g_router_id_v6_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Gives the IPv4 router ID received from Zebra.
+ *
+ * @param router_id The in_addr strucure where to store the router id
+ * @return true if the router ID was available, false otherwise
+ */
+bool get_ipv4_router_id(struct in_addr *router_id)
+{
+ bool retval = false;
+ assert(router_id != NULL);
+ pthread_mutex_lock(&g_router_id_v4_mtx);
+ if (g_has_router_id_v4) {
+ memcpy(router_id, &g_router_id_v4, sizeof(*router_id));
+ retval = true;
+ }
+ pthread_mutex_unlock(&g_router_id_v4_mtx);
+ return retval;
+}
+
+/**
+ * Gives the IPv6 router ID received from Zebra.
+ *
+ * @param router_id The in6_addr strucure where to store the router id
+ * @return true if the router ID was available, false otherwise
+ */
+bool get_ipv6_router_id(struct in6_addr *router_id)
+{
+ bool retval = false;
+ assert(router_id != NULL);
+ pthread_mutex_lock(&g_router_id_v6_mtx);
+ if (g_has_router_id_v6) {
+ memcpy(router_id, &g_router_id_v6, sizeof(*router_id));
+ retval = true;
+ }
+ pthread_mutex_unlock(&g_router_id_v6_mtx);
+ return retval;
+}
+
+static void path_zebra_connected(struct zclient *zclient)
+{
+ struct srte_policy *policy;
+
+ zclient_send_reg_requests(zclient, VRF_DEFAULT);
+ zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_ADD, AFI_IP6,
+ VRF_DEFAULT);
+
+ RB_FOREACH (policy, srte_policy_head, &srte_policies) {
+ struct srte_candidate *candidate;
+ struct srte_segment_list *segment_list;
+
+ candidate = policy->best_candidate;
+ if (!candidate)
+ continue;
+
+ segment_list = candidate->lsp->segment_list;
+ if (!segment_list)
+ continue;
+
+ path_zebra_add_sr_policy(policy, segment_list);
+ }
+}
+
+static int path_zebra_sr_policy_notify_status(ZAPI_CALLBACK_ARGS)
+{
+ struct zapi_sr_policy zapi_sr_policy;
+ struct srte_policy *policy;
+ struct srte_candidate *best_candidate_path;
+
+ if (zapi_sr_policy_notify_status_decode(zclient->ibuf, &zapi_sr_policy))
+ return -1;
+
+ policy = srte_policy_find(zapi_sr_policy.color,
+ &zapi_sr_policy.endpoint);
+ if (!policy)
+ return -1;
+
+ best_candidate_path = policy->best_candidate;
+ if (!best_candidate_path)
+ return -1;
+
+ srte_candidate_status_update(best_candidate_path,
+ zapi_sr_policy.status);
+
+ return 0;
+}
+
+/* Router-id update message from zebra. */
+static int path_zebra_router_id_update(ZAPI_CALLBACK_ARGS)
+{
+ struct prefix pref;
+ const char *family;
+ char buf[PREFIX2STR_BUFFER];
+ zebra_router_id_update_read(zclient->ibuf, &pref);
+ if (pref.family == AF_INET) {
+ pthread_mutex_lock(&g_router_id_v4_mtx);
+ memcpy(&g_router_id_v4, &pref.u.prefix4,
+ sizeof(g_router_id_v4));
+ g_has_router_id_v4 = true;
+ inet_ntop(AF_INET, &g_router_id_v4, buf, sizeof(buf));
+ pthread_mutex_unlock(&g_router_id_v4_mtx);
+ family = "IPv4";
+ } else if (pref.family == AF_INET6) {
+ pthread_mutex_lock(&g_router_id_v6_mtx);
+ memcpy(&g_router_id_v6, &pref.u.prefix6,
+ sizeof(g_router_id_v6));
+ g_has_router_id_v6 = true;
+ inet_ntop(AF_INET6, &g_router_id_v6, buf, sizeof(buf));
+ pthread_mutex_unlock(&g_router_id_v6_mtx);
+ family = "IPv6";
+ } else {
+ zlog_warn("Unexpected router ID address family for vrf %u: %u",
+ vrf_id, pref.family);
+ return 0;
+ }
+ zlog_info("%s Router Id updated for VRF %u: %s", family, vrf_id, buf);
+ return 0;
+}
+
+/**
+ * Adds a segment routing policy to Zebra.
+ *
+ * @param policy The policy to add
+ * @param segment_list The segment list for the policy
+ */
+void path_zebra_add_sr_policy(struct srte_policy *policy,
+ struct srte_segment_list *segment_list)
+{
+ struct zapi_sr_policy zp = {};
+ struct srte_segment_entry *segment;
+
+ zp.color = policy->color;
+ zp.endpoint = policy->endpoint;
+ strlcpy(zp.name, policy->name, sizeof(zp.name));
+ zp.segment_list.type = ZEBRA_LSP_SRTE;
+ zp.segment_list.local_label = policy->binding_sid;
+ zp.segment_list.label_num = 0;
+ RB_FOREACH (segment, srte_segment_entry_head, &segment_list->segments)
+ zp.segment_list.labels[zp.segment_list.label_num++] =
+ segment->sid_value;
+ policy->status = SRTE_POLICY_STATUS_GOING_UP;
+
+ (void)zebra_send_sr_policy(zclient, ZEBRA_SR_POLICY_SET, &zp);
+}
+
+/**
+ * Deletes a segment policy from Zebra.
+ *
+ * @param policy The policy to remove
+ */
+void path_zebra_delete_sr_policy(struct srte_policy *policy)
+{
+ struct zapi_sr_policy zp = {};
+
+ zp.color = policy->color;
+ zp.endpoint = policy->endpoint;
+ strlcpy(zp.name, policy->name, sizeof(zp.name));
+ zp.segment_list.type = ZEBRA_LSP_SRTE;
+ zp.segment_list.local_label = policy->binding_sid;
+ zp.segment_list.label_num = 0;
+ policy->status = SRTE_POLICY_STATUS_DOWN;
+
+ (void)zebra_send_sr_policy(zclient, ZEBRA_SR_POLICY_DELETE, &zp);
+}
+
+/**
+ * Allocates a label from Zebra's label manager.
+ *
+ * @param label the label to be allocated
+ * @return 0 if the label has been allocated, -1 otherwise
+ */
+int path_zebra_request_label(mpls_label_t label)
+{
+ int ret;
+ uint32_t start, end;
+
+ ret = lm_get_label_chunk(zclient_sync, 0, label, 1, &start, &end);
+ if (ret < 0) {
+ zlog_warn("%s: error getting label range!", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Releases a previously allocated label from Zebra's label manager.
+ *
+ * @param label The label to release
+ * @return 0 ifthe label has beel released, -1 otherwise
+ */
+void path_zebra_release_label(mpls_label_t label)
+{
+ int ret;
+
+ ret = lm_release_label_chunk(zclient_sync, label, label);
+ if (ret < 0)
+ zlog_warn("%s: error releasing label range!", __func__);
+}
+
+static void path_zebra_label_manager_connect(void)
+{
+ /* Connect to label manager. */
+ while (zclient_socket_connect(zclient_sync) < 0) {
+ zlog_warn("%s: error connecting synchronous zclient!",
+ __func__);
+ sleep(1);
+ }
+ set_nonblocking(zclient_sync->sock);
+
+ /* Send hello to notify zebra this is a synchronous client */
+ while (zclient_send_hello(zclient_sync) < 0) {
+ zlog_warn("%s: Error sending hello for synchronous zclient!",
+ __func__);
+ sleep(1);
+ }
+
+ while (lm_label_manager_connect(zclient_sync, 0) != 0) {
+ zlog_warn("%s: error connecting to label manager!", __func__);
+ sleep(1);
+ }
+}
+
+/**
+ * Initializes Zebra asynchronous connection.
+ *
+ * @param master The master thread
+ */
+void path_zebra_init(struct thread_master *master)
+{
+ struct zclient_options options = zclient_options_default;
+ options.synchronous = true;
+
+ /* Initialize asynchronous zclient. */
+ zclient = zclient_new(master, &zclient_options_default);
+ zclient_init(zclient, ZEBRA_ROUTE_SRTE, 0, &pathd_privs);
+ zclient->zebra_connected = path_zebra_connected;
+ zclient->sr_policy_notify_status = path_zebra_sr_policy_notify_status;
+ zclient->router_id_update = path_zebra_router_id_update;
+
+ /* Initialize special zclient for synchronous message exchanges. */
+ zclient_sync = zclient_new(master, &options);
+ zclient_sync->sock = -1;
+ zclient_sync->redist_default = ZEBRA_ROUTE_SRTE;
+ zclient_sync->instance = 1;
+ zclient_sync->privs = &pathd_privs;
+
+ /* Connect to the LM. */
+ path_zebra_label_manager_connect();
+}
diff --git a/pathd/path_zebra.h b/pathd/path_zebra.h
new file mode 100644
index 0000000000..42a7123dd4
--- /dev/null
+++ b/pathd/path_zebra.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_PATH_MPLS_H_
+#define _FRR_PATH_MPLS_H_
+
+#include <zebra.h>
+#include "pathd/pathd.h"
+
+bool get_ipv4_router_id(struct in_addr *router_id);
+bool get_ipv6_router_id(struct in6_addr *router_id);
+void path_zebra_add_sr_policy(struct srte_policy *policy,
+ struct srte_segment_list *segment_list);
+void path_zebra_delete_sr_policy(struct srte_policy *policy);
+int path_zebra_request_label(mpls_label_t label);
+void path_zebra_release_label(mpls_label_t label);
+void path_zebra_init(struct thread_master *master);
+
+#endif /* _FRR_PATH_MPLS_H_ */
diff --git a/pathd/pathd.c b/pathd/pathd.c
new file mode 100644
index 0000000000..e2c7c95728
--- /dev/null
+++ b/pathd/pathd.c
@@ -0,0 +1,1128 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "log.h"
+#include "lib_errors.h"
+#include "network.h"
+
+#include "pathd/pathd.h"
+#include "pathd/path_memory.h"
+#include "pathd/path_zebra.h"
+#include "pathd/path_debug.h"
+
+#define HOOK_DELAY 3
+
+DEFINE_MTYPE_STATIC(PATHD, PATH_SEGMENT_LIST, "Segment List")
+DEFINE_MTYPE_STATIC(PATHD, PATH_SR_POLICY, "SR Policy")
+DEFINE_MTYPE_STATIC(PATHD, PATH_SR_CANDIDATE, "SR Policy candidate path")
+
+DEFINE_HOOK(pathd_candidate_created, (struct srte_candidate * candidate),
+ (candidate))
+DEFINE_HOOK(pathd_candidate_updated, (struct srte_candidate * candidate),
+ (candidate))
+DEFINE_HOOK(pathd_candidate_removed, (struct srte_candidate * candidate),
+ (candidate))
+
+static void trigger_pathd_candidate_created(struct srte_candidate *candidate);
+static int trigger_pathd_candidate_created_timer(struct thread *thread);
+static void trigger_pathd_candidate_updated(struct srte_candidate *candidate);
+static int trigger_pathd_candidate_updated_timer(struct thread *thread);
+static void trigger_pathd_candidate_removed(struct srte_candidate *candidate);
+static const char *
+srte_candidate_metric_name(enum srte_candidate_metric_type type);
+
+static void srte_set_metric(struct srte_metric *metric, float value,
+ bool required, bool is_bound, bool is_computed);
+static void srte_unset_metric(struct srte_metric *metric);
+
+
+/* Generate rb-tree of Segment List Segment instances. */
+static inline int srte_segment_entry_compare(const struct srte_segment_entry *a,
+ const struct srte_segment_entry *b)
+{
+ return a->index - b->index;
+}
+RB_GENERATE(srte_segment_entry_head, srte_segment_entry, entry,
+ srte_segment_entry_compare)
+
+/* Generate rb-tree of Segment List instances. */
+static inline int srte_segment_list_compare(const struct srte_segment_list *a,
+ const struct srte_segment_list *b)
+{
+ return strcmp(a->name, b->name);
+}
+RB_GENERATE(srte_segment_list_head, srte_segment_list, entry,
+ srte_segment_list_compare)
+
+struct srte_segment_list_head srte_segment_lists =
+ RB_INITIALIZER(&srte_segment_lists);
+
+/* Generate rb-tree of Candidate Path instances. */
+static inline int srte_candidate_compare(const struct srte_candidate *a,
+ const struct srte_candidate *b)
+{
+ return a->preference - b->preference;
+}
+RB_GENERATE(srte_candidate_head, srte_candidate, entry, srte_candidate_compare)
+
+/* Generate rb-tree of SR Policy instances. */
+static inline int srte_policy_compare(const struct srte_policy *a,
+ const struct srte_policy *b)
+{
+ return sr_policy_compare(&a->endpoint, &b->endpoint, a->color,
+ b->color);
+}
+RB_GENERATE(srte_policy_head, srte_policy, entry, srte_policy_compare)
+
+struct srte_policy_head srte_policies = RB_INITIALIZER(&srte_policies);
+
+/**
+ * Adds a segment list to pathd.
+ *
+ * @param name The name of the segment list to add
+ * @return The added segment list
+ */
+struct srte_segment_list *srte_segment_list_add(const char *name)
+{
+ struct srte_segment_list *segment_list;
+
+ segment_list = XCALLOC(MTYPE_PATH_SEGMENT_LIST, sizeof(*segment_list));
+ strlcpy(segment_list->name, name, sizeof(segment_list->name));
+ RB_INIT(srte_segment_entry_head, &segment_list->segments);
+ RB_INSERT(srte_segment_list_head, &srte_segment_lists, segment_list);
+
+ return segment_list;
+}
+
+/**
+ * Deletes a segment list from pathd.
+ *
+ * The given segment list structure will be freed and should not be used anymore
+ * after calling this function.
+ *
+ * @param segment_list the segment list to remove from pathd.
+ */
+void srte_segment_list_del(struct srte_segment_list *segment_list)
+{
+ struct srte_segment_entry *segment, *safe_seg;
+ RB_FOREACH_SAFE (segment, srte_segment_entry_head,
+ &segment_list->segments, safe_seg) {
+ srte_segment_entry_del(segment);
+ }
+ RB_REMOVE(srte_segment_list_head, &srte_segment_lists, segment_list);
+ XFREE(MTYPE_PATH_SEGMENT_LIST, segment_list);
+}
+
+/**
+ * Search for a segment list by name.
+ *
+ * @param name The name of the segment list to look for
+ * @return The segment list if found, NULL otherwise
+ */
+struct srte_segment_list *srte_segment_list_find(const char *name)
+{
+ struct srte_segment_list search;
+
+ strlcpy(search.name, name, sizeof(search.name));
+ return RB_FIND(srte_segment_list_head, &srte_segment_lists, &search);
+}
+
+/**
+ * Adds a segment to a segment list.
+ *
+ * @param segment_list The segment list the segment should be added to
+ * @param index The index of the added segment in the segment list
+ * @return The added segment
+ */
+struct srte_segment_entry *
+srte_segment_entry_add(struct srte_segment_list *segment_list, uint32_t index)
+{
+ struct srte_segment_entry *segment;
+
+ segment = XCALLOC(MTYPE_PATH_SEGMENT_LIST, sizeof(*segment));
+ segment->segment_list = segment_list;
+ segment->index = index;
+ RB_INSERT(srte_segment_entry_head, &segment_list->segments, segment);
+
+ return segment;
+}
+
+/**
+ * Deletes a segment from a segment list.
+ *
+ * @param segment The segment to be removed
+ */
+void srte_segment_entry_del(struct srte_segment_entry *segment)
+{
+ RB_REMOVE(srte_segment_entry_head, &segment->segment_list->segments,
+ segment);
+ XFREE(MTYPE_PATH_SEGMENT_LIST, segment);
+}
+
+/**
+ * Set the node or adjacency identifier of a segment.
+ *
+ * @param segment The segment for which the NAI should be set
+ * @param type The type of the NAI
+ * @param type The address of the node or the local address of the adjacency
+ * @param type The local interface index of the unumbered adjacency
+ * @param type The remote address of the adjacency
+ * @param type The remote interface index of the unumbered adjacency
+ */
+void srte_segment_entry_set_nai(struct srte_segment_entry *segment,
+ enum srte_segment_nai_type type,
+ struct ipaddr *local_ip, uint32_t local_iface,
+ struct ipaddr *remote_ip, uint32_t remote_iface)
+{
+ segment->nai_type = type;
+ memcpy(&segment->nai_local_addr, local_ip, sizeof(struct ipaddr));
+
+ switch (type) {
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY:
+ memcpy(&segment->nai_remote_addr, remote_ip,
+ sizeof(struct ipaddr));
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY:
+ memcpy(&segment->nai_remote_addr, remote_ip,
+ sizeof(struct ipaddr));
+ segment->nai_local_iface = local_iface;
+ segment->nai_remote_iface = remote_iface;
+ break;
+ default:
+ segment->nai_local_addr.ipa_type = IPADDR_NONE;
+ segment->nai_local_iface = 0;
+ segment->nai_remote_addr.ipa_type = IPADDR_NONE;
+ segment->nai_remote_iface = 0;
+ }
+}
+
+/**
+ * Add a policy to pathd.
+ *
+ * WARNING: The color 0 is a special case as it is the no-color.
+ *
+ * @param color The color of the policy.
+ * @param endpoint The IP address of the policy endpoint
+ * @return The created policy
+ */
+struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint)
+{
+ struct srte_policy *policy;
+
+ policy = XCALLOC(MTYPE_PATH_SR_POLICY, sizeof(*policy));
+ policy->color = color;
+ policy->endpoint = *endpoint;
+ policy->binding_sid = MPLS_LABEL_NONE;
+ RB_INIT(srte_candidate_head, &policy->candidate_paths);
+ RB_INSERT(srte_policy_head, &srte_policies, policy);
+
+ return policy;
+}
+
+/**
+ * Delete a policy from pathd.
+ *
+ * The given policy structure will be freed and should never be used again
+ * after calling this function.
+ *
+ * @param policy The policy to be removed
+ */
+void srte_policy_del(struct srte_policy *policy)
+{
+ struct srte_candidate *candidate;
+
+ path_zebra_delete_sr_policy(policy);
+
+ path_zebra_release_label(policy->binding_sid);
+
+ while (!RB_EMPTY(srte_candidate_head, &policy->candidate_paths)) {
+ candidate =
+ RB_ROOT(srte_candidate_head, &policy->candidate_paths);
+ trigger_pathd_candidate_removed(candidate);
+ srte_candidate_del(candidate);
+ }
+
+ RB_REMOVE(srte_policy_head, &srte_policies, policy);
+ XFREE(MTYPE_PATH_SR_POLICY, policy);
+}
+
+/**
+ * Search for a policy by color and endpoint.
+ *
+ * WARNING: The color 0 is a special case as it is the no-color.
+ *
+ * @param color The color of the policy to look for
+ * @param endpoint The endpoint of the policy to look for
+ * @return The policy if found, NULL otherwise
+ */
+struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint)
+{
+ struct srte_policy search;
+
+ search.color = color;
+ search.endpoint = *endpoint;
+ return RB_FIND(srte_policy_head, &srte_policies, &search);
+}
+
+/**
+ * Update a policy binding SID.
+ *
+ * @param policy The policy for which the SID should be updated
+ * @param binding_sid The new binding SID for the given policy
+ */
+void srte_policy_update_binding_sid(struct srte_policy *policy,
+ uint32_t binding_sid)
+{
+ if (policy->binding_sid != MPLS_LABEL_NONE)
+ path_zebra_release_label(policy->binding_sid);
+
+ policy->binding_sid = binding_sid;
+
+ /* Reinstall the Binding-SID if necessary. */
+ if (policy->best_candidate)
+ path_zebra_add_sr_policy(
+ policy, policy->best_candidate->lsp->segment_list);
+}
+
+/**
+ * Gives the policy best candidate path.
+ *
+ * @param policy The policy we want the best candidate path from
+ * @return The best candidate path
+ */
+static struct srte_candidate *
+srte_policy_best_candidate(const struct srte_policy *policy)
+{
+ struct srte_candidate *candidate;
+
+ RB_FOREACH_REVERSE (candidate, srte_candidate_head,
+ &policy->candidate_paths) {
+ /* search for highest preference with existing segment list */
+ if (!CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED)
+ && candidate->lsp->segment_list)
+ return candidate;
+ }
+
+ return NULL;
+}
+
+/**
+ * Apply changes defined by setting the policies, candidate paths
+ * and segment lists modification flags NEW, MODIFIED and DELETED.
+ *
+ * This allows the northbound code to delay all the side effects of adding
+ * modifying and deleting them to the end.
+ *
+ * Example of marking an object as modified:
+ * `SET_FLAG(obj->flags, F_XXX_MODIFIED)`
+ */
+void srte_apply_changes(void)
+{
+ struct srte_policy *policy, *safe_pol;
+ struct srte_segment_list *segment_list, *safe_sl;
+
+ RB_FOREACH_SAFE (policy, srte_policy_head, &srte_policies, safe_pol) {
+ if (CHECK_FLAG(policy->flags, F_POLICY_DELETED)) {
+ srte_policy_del(policy);
+ continue;
+ }
+ srte_policy_apply_changes(policy);
+ UNSET_FLAG(policy->flags, F_POLICY_NEW);
+ UNSET_FLAG(policy->flags, F_POLICY_MODIFIED);
+ }
+
+ RB_FOREACH_SAFE (segment_list, srte_segment_list_head,
+ &srte_segment_lists, safe_sl) {
+ if (CHECK_FLAG(segment_list->flags, F_SEGMENT_LIST_DELETED)) {
+ srte_segment_list_del(segment_list);
+ continue;
+ }
+ UNSET_FLAG(segment_list->flags, F_SEGMENT_LIST_NEW);
+ UNSET_FLAG(segment_list->flags, F_SEGMENT_LIST_MODIFIED);
+ }
+}
+
+/**
+ * Apply changes defined by setting the given policy and its candidate paths
+ * modification flags NEW, MODIFIED and DELETED.
+ *
+ * In moste cases `void srte_apply_changes(void)` should be used instead,
+ * this function will not handle the changes of segment lists used by the
+ * policy.
+ *
+ * @param policy The policy changes has to be applied to.
+ */
+void srte_policy_apply_changes(struct srte_policy *policy)
+{
+ struct srte_candidate *candidate, *safe;
+ struct srte_candidate *old_best_candidate;
+ struct srte_candidate *new_best_candidate;
+ char endpoint[46];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+
+ /* Get old and new best candidate path. */
+ old_best_candidate = policy->best_candidate;
+ new_best_candidate = srte_policy_best_candidate(policy);
+
+ if (new_best_candidate != old_best_candidate) {
+ /* TODO: add debug guard. */
+ zlog_debug(
+ "SR-TE(%s, %u): best candidate changed from %s to %s",
+ endpoint, policy->color,
+ old_best_candidate ? old_best_candidate->name : "none",
+ new_best_candidate ? new_best_candidate->name : "none");
+
+ if (old_best_candidate) {
+ policy->best_candidate = NULL;
+ UNSET_FLAG(old_best_candidate->flags, F_CANDIDATE_BEST);
+ SET_FLAG(old_best_candidate->flags,
+ F_CANDIDATE_MODIFIED);
+
+ /*
+ * Rely on replace semantics if there's a new best
+ * candidate.
+ */
+ if (!new_best_candidate)
+ path_zebra_delete_sr_policy(policy);
+ }
+ if (new_best_candidate) {
+ policy->best_candidate = new_best_candidate;
+ SET_FLAG(new_best_candidate->flags, F_CANDIDATE_BEST);
+ SET_FLAG(new_best_candidate->flags,
+ F_CANDIDATE_MODIFIED);
+
+ path_zebra_add_sr_policy(
+ policy, new_best_candidate->lsp->segment_list);
+ }
+ } else if (new_best_candidate) {
+ /* The best candidate path did not change, but some of its
+ * attributes or its segment list may have changed.
+ */
+
+ bool candidate_changed = CHECK_FLAG(new_best_candidate->flags,
+ F_CANDIDATE_MODIFIED);
+ bool segment_list_changed =
+ new_best_candidate->lsp->segment_list
+ && CHECK_FLAG(
+ new_best_candidate->lsp->segment_list->flags,
+ F_SEGMENT_LIST_MODIFIED);
+
+ if (candidate_changed || segment_list_changed) {
+ /* TODO: add debug guard. */
+ zlog_debug("SR-TE(%s, %u): best candidate %s changed",
+ endpoint, policy->color,
+ new_best_candidate->name);
+
+ path_zebra_add_sr_policy(
+ policy, new_best_candidate->lsp->segment_list);
+ }
+ }
+
+ RB_FOREACH_SAFE (candidate, srte_candidate_head,
+ &policy->candidate_paths, safe) {
+ if (CHECK_FLAG(candidate->flags, F_CANDIDATE_DELETED)) {
+ trigger_pathd_candidate_removed(candidate);
+ srte_candidate_del(candidate);
+ continue;
+ } else if (CHECK_FLAG(candidate->flags, F_CANDIDATE_NEW)) {
+ trigger_pathd_candidate_created(candidate);
+ } else if (CHECK_FLAG(candidate->flags, F_CANDIDATE_MODIFIED)) {
+ trigger_pathd_candidate_updated(candidate);
+ } else if (candidate->lsp->segment_list
+ && CHECK_FLAG(candidate->lsp->segment_list->flags,
+ F_SEGMENT_LIST_MODIFIED)) {
+ trigger_pathd_candidate_updated(candidate);
+ }
+
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_NEW);
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ }
+}
+
+/**
+ * Adds a candidate path to a policy.
+ *
+ * @param policy The policy the candidate path should be added to
+ * @param preference The preference of the candidate path to be added
+ * @return The added candidate path
+ */
+struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
+ uint32_t preference)
+{
+ struct srte_candidate *candidate;
+ struct srte_lsp *lsp;
+
+ candidate = XCALLOC(MTYPE_PATH_SR_CANDIDATE, sizeof(*candidate));
+ lsp = XCALLOC(MTYPE_PATH_SR_CANDIDATE, sizeof(*lsp));
+
+ candidate->preference = preference;
+ candidate->policy = policy;
+ candidate->type = SRTE_CANDIDATE_TYPE_UNDEFINED;
+ candidate->discriminator = frr_weak_random();
+
+ lsp->candidate = candidate;
+ candidate->lsp = lsp;
+
+ RB_INSERT(srte_candidate_head, &policy->candidate_paths, candidate);
+
+ return candidate;
+}
+
+/**
+ * Deletes a candidate.
+ *
+ * The corresponding LSP will be removed alongside the candidate path.
+ * The given candidate will be freed and shouldn't be used anymore after the
+ * calling this function.
+ *
+ * @param candidate The candidate path to delete
+ */
+void srte_candidate_del(struct srte_candidate *candidate)
+{
+ struct srte_policy *srte_policy = candidate->policy;
+
+ RB_REMOVE(srte_candidate_head, &srte_policy->candidate_paths,
+ candidate);
+
+ XFREE(MTYPE_PATH_SR_CANDIDATE, candidate->lsp);
+ XFREE(MTYPE_PATH_SR_CANDIDATE, candidate);
+}
+
+/**
+ * Sets the bandwidth constraint of given candidate path.
+ *
+ * The corresponding LSP will be changed too.
+ *
+ * @param candidate The candidate path of which the bandwidth should be changed
+ * @param bandwidth The Bandwidth constraint to set to the candidate path
+ * @param required If the constraint is required (true) or only desired (false)
+ */
+void srte_candidate_set_bandwidth(struct srte_candidate *candidate,
+ float bandwidth, bool required)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug(
+ "SR-TE(%s, %u): candidate %s %sconfig bandwidth set to %f B/s",
+ endpoint, policy->color, candidate->name,
+ required ? "required " : "", bandwidth);
+ SET_FLAG(candidate->flags, F_CANDIDATE_HAS_BANDWIDTH);
+ COND_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_BANDWIDTH, required);
+ candidate->bandwidth = bandwidth;
+
+ srte_lsp_set_bandwidth(candidate->lsp, bandwidth, required);
+}
+
+/**
+ * Sets the bandwidth constraint of the given LSP.
+ *
+ * The changes will not be shown as part of the running configuration.
+ *
+ * @param lsp The lsp of which the bandwidth should be changed
+ * @param bandwidth The Bandwidth constraint to set to the candidate path
+ * @param required If the constraint is required (true) or only desired (false)
+ */
+void srte_lsp_set_bandwidth(struct srte_lsp *lsp, float bandwidth,
+ bool required)
+{
+ struct srte_candidate *candidate = lsp->candidate;
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): candidate %s %slsp bandwidth set to %f B/s",
+ endpoint, policy->color, candidate->name,
+ required ? "required" : "", bandwidth);
+ SET_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH);
+ COND_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH, required);
+ lsp->bandwidth = bandwidth;
+}
+
+/**
+ * Remove a candidate path bandwidth constraint.
+ *
+ * The corresponding LSP will be changed too.
+ *
+ * @param candidate The candidate path of which the bandwidth should be removed
+ */
+void srte_candidate_unset_bandwidth(struct srte_candidate *candidate)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): candidate %s config bandwidth unset",
+ endpoint, policy->color, candidate->name);
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_HAS_BANDWIDTH);
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_BANDWIDTH);
+ candidate->bandwidth = 0;
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ srte_lsp_unset_bandwidth(candidate->lsp);
+}
+
+/**
+ * Remove an LSP bandwidth constraint.
+ *
+ * The changes will not be shown as part of the running configuration.
+ *
+ * @param lsp The lsp of which the bandwidth should be changed
+ */
+void srte_lsp_unset_bandwidth(struct srte_lsp *lsp)
+{
+ struct srte_candidate *candidate = lsp->candidate;
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): candidate %s lsp bandwidth unset", endpoint,
+ policy->color, candidate->name);
+ UNSET_FLAG(lsp->flags, F_CANDIDATE_HAS_BANDWIDTH);
+ UNSET_FLAG(lsp->flags, F_CANDIDATE_REQUIRED_BANDWIDTH);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ lsp->bandwidth = 0;
+}
+
+/**
+ * Sets a candidate path metric constraint.
+ *
+ * The corresponding LSP will be changed too.
+ *
+ * @param candidate The candidate path of which the metric should be changed
+ * @param type The metric type
+ * @param value The metric value
+ * @param required If the constraint is required (true) or only desired (false)
+ * @param is_bound If the metric is an indicative value or a strict upper bound
+ * @param is_computed If the metric was computed or configured
+ */
+void srte_candidate_set_metric(struct srte_candidate *candidate,
+ enum srte_candidate_metric_type type,
+ float value, bool required, bool is_bound,
+ bool is_computed)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug(
+ "SR-TE(%s, %u): candidate %s %sconfig metric %s (%u) set to %f "
+ "(is-bound: %s; is_computed: %s)",
+ endpoint, policy->color, candidate->name,
+ required ? "required " : "", srte_candidate_metric_name(type),
+ type, value, is_bound ? "true" : "false",
+ is_computed ? "true" : "false");
+ assert((type > 0) && (type <= MAX_METRIC_TYPE));
+ srte_set_metric(&candidate->metrics[type - 1], value, required,
+ is_bound, is_computed);
+ srte_lsp_set_metric(candidate->lsp, type, value, required, is_bound,
+ is_computed);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+}
+
+/**
+ * Sets an LSP metric constraint.
+ *
+ * The changes will not be shown as part of the running configuration.
+ *
+ * @param lsp The LSP of which the metric should be changed
+ * @param type The metric type
+ * @param value The metric value
+ * @param required If the constraint is required (true) or only desired (false)
+ * @param is_bound If the metric is an indicative value or a strict upper bound
+ * @param is_computed If the metric was computed or configured
+ */
+void srte_lsp_set_metric(struct srte_lsp *lsp,
+ enum srte_candidate_metric_type type, float value,
+ bool required, bool is_bound, bool is_computed)
+{
+ struct srte_candidate *candidate = lsp->candidate;
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug(
+ "SR-TE(%s, %u): candidate %s %slsp metric %s (%u) set to %f "
+ "(is-bound: %s; is_computed: %s)",
+ endpoint, policy->color, candidate->name,
+ required ? "required " : "", srte_candidate_metric_name(type),
+ type, value, is_bound ? "true" : "false",
+ is_computed ? "true" : "false");
+ assert((type > 0) && (type <= MAX_METRIC_TYPE));
+ srte_set_metric(&lsp->metrics[type - 1], value, required, is_bound,
+ is_computed);
+}
+
+void srte_set_metric(struct srte_metric *metric, float value, bool required,
+ bool is_bound, bool is_computed)
+{
+ SET_FLAG(metric->flags, F_METRIC_IS_DEFINED);
+ COND_FLAG(metric->flags, F_METRIC_IS_REQUIRED, required);
+ COND_FLAG(metric->flags, F_METRIC_IS_BOUND, is_bound);
+ COND_FLAG(metric->flags, F_METRIC_IS_COMPUTED, is_computed);
+ metric->value = value;
+}
+
+/**
+ * Removes a candidate path metric constraint.
+ *
+ * The corresponding LSP will be changed too.
+ *
+ * @param candidate The candidate path from which the metric should be removed
+ * @param type The metric type
+ */
+void srte_candidate_unset_metric(struct srte_candidate *candidate,
+ enum srte_candidate_metric_type type)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): candidate %s config metric %s (%u) unset",
+ endpoint, policy->color, candidate->name,
+ srte_candidate_metric_name(type), type);
+ assert((type > 0) && (type <= MAX_METRIC_TYPE));
+ srte_unset_metric(&candidate->metrics[type - 1]);
+ srte_lsp_unset_metric(candidate->lsp, type);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+}
+
+/**
+ * Removes a candidate path metric constraint.
+ *
+ * The changes will not be shown as part of the running configuration.
+ *
+ * @param lsp The LSP from which the metric should be removed
+ * @param type The metric type
+ */
+void srte_lsp_unset_metric(struct srte_lsp *lsp,
+ enum srte_candidate_metric_type type)
+{
+ struct srte_candidate *candidate = lsp->candidate;
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): candidate %s lsp metric %s (%u) unset",
+ endpoint, policy->color, candidate->name,
+ srte_candidate_metric_name(type), type);
+ assert((type > 0) && (type <= MAX_METRIC_TYPE));
+ srte_unset_metric(&lsp->metrics[type - 1]);
+}
+
+void srte_unset_metric(struct srte_metric *metric)
+{
+ UNSET_FLAG(metric->flags, F_METRIC_IS_DEFINED);
+ UNSET_FLAG(metric->flags, F_METRIC_IS_BOUND);
+ UNSET_FLAG(metric->flags, F_METRIC_IS_COMPUTED);
+ metric->value = 0;
+}
+
+/**
+ * Sets a candidate path objective function.
+ *
+ * @param candidate The candidate path of which the OF should be changed
+ * @param required If the constraint is required (true) or only desired (false)
+ * @param type The objective function type
+ */
+void srte_candidate_set_objfun(struct srte_candidate *candidate, bool required,
+ enum objfun_type type)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+
+ candidate->objfun = type;
+ SET_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN);
+ COND_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN, required);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ zlog_debug("SR-TE(%s, %u): candidate %s %sobjective function set to %s",
+ endpoint, policy->color, candidate->name,
+ required ? "required " : "", objfun_type_name(type));
+}
+
+/**
+ * Removed the objective function constraint from a candidate path.
+ *
+ * @param candidate The candidate path from which the OF should be removed
+ */
+void srte_candidate_unset_objfun(struct srte_candidate *candidate)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_HAS_OBJFUN);
+ UNSET_FLAG(candidate->flags, F_CANDIDATE_REQUIRED_OBJFUN);
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ candidate->objfun = OBJFUN_UNDEFINED;
+ zlog_debug(
+ "SR-TE(%s, %u): candidate %s objective functions preferences unset",
+ endpoint, policy->color, candidate->name);
+}
+
+static uint32_t filter_type_to_flag(enum affinity_filter_type type)
+{
+ switch (type) {
+ case AFFINITY_FILTER_EXCLUDE_ANY:
+ return F_CANDIDATE_HAS_EXCLUDE_ANY;
+ case AFFINITY_FILTER_INCLUDE_ANY:
+ return F_CANDIDATE_HAS_INCLUDE_ANY;
+ case AFFINITY_FILTER_INCLUDE_ALL:
+ return F_CANDIDATE_HAS_INCLUDE_ALL;
+ default:
+ return 0;
+ }
+}
+
+static const char *filter_type_name(enum affinity_filter_type type)
+{
+ switch (type) {
+ case AFFINITY_FILTER_EXCLUDE_ANY:
+ return "exclude-any";
+ case AFFINITY_FILTER_INCLUDE_ANY:
+ return "include-any";
+ case AFFINITY_FILTER_INCLUDE_ALL:
+ return "include-all";
+ default:
+ return "unknown";
+ }
+}
+
+/**
+ * Sets a candidate path affinity filter constraint.
+ *
+ * @param candidate The candidate path of which the constraint should be changed
+ * @param type The affinity constraint type to set
+ * @param filter The bitmask filter of the constraint
+ */
+void srte_candidate_set_affinity_filter(struct srte_candidate *candidate,
+ enum affinity_filter_type type,
+ uint32_t filter)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+
+ assert(type > AFFINITY_FILTER_UNDEFINED);
+ assert(type <= MAX_AFFINITY_FILTER_TYPE);
+ SET_FLAG(candidate->flags, filter_type_to_flag(type));
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ candidate->affinity_filters[type - 1] = filter;
+ zlog_debug(
+ "SR-TE(%s, %u): candidate %s affinity filter %s set to 0x%08x",
+ endpoint, policy->color, candidate->name,
+ filter_type_name(type), filter);
+}
+
+/**
+ * Removes a candidate path affinity filter constraint.
+ *
+ * @param candidate The candidate path from which the constraint should be
+ * removed
+ * @param type The affinity constraint type to remove
+ */
+void srte_candidate_unset_affinity_filter(struct srte_candidate *candidate,
+ enum affinity_filter_type type)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+
+ assert(type > AFFINITY_FILTER_UNDEFINED);
+ assert(type <= MAX_AFFINITY_FILTER_TYPE);
+ UNSET_FLAG(candidate->flags, filter_type_to_flag(type));
+ SET_FLAG(candidate->flags, F_CANDIDATE_MODIFIED);
+ candidate->affinity_filters[type - 1] = 0;
+ zlog_debug("SR-TE(%s, %u): candidate %s affinity filter %s unset",
+ endpoint, policy->color, candidate->name,
+ filter_type_name(type));
+}
+
+/**
+ * Searches for a candidate path of the given policy.
+ *
+ * @param policy The policy to search for candidate path
+ * @param preference The preference of the candidate path you are looking for
+ * @return The candidate path if found, NULL otherwise
+ */
+struct srte_candidate *srte_candidate_find(struct srte_policy *policy,
+ uint32_t preference)
+{
+ struct srte_candidate search;
+
+ search.preference = preference;
+ return RB_FIND(srte_candidate_head, &policy->candidate_paths, &search);
+}
+
+/**
+ * Searches for a an entry of a given segment list.
+ *
+ * @param segment_list The segment list to search for the entry
+ * @param index The index of the entry you are looking for
+ * @return The segment list entry if found, NULL otherwise.
+ */
+struct srte_segment_entry *
+srte_segment_entry_find(struct srte_segment_list *segment_list, uint32_t index)
+{
+ struct srte_segment_entry search;
+
+ search.index = index;
+ return RB_FIND(srte_segment_entry_head, &segment_list->segments,
+ &search);
+}
+
+/**
+ * Updates a candidate status.
+ *
+ * @param candidate The candidate of which the status should be updated
+ * @param status The new candidate path status
+ */
+void srte_candidate_status_update(struct srte_candidate *candidate, int status)
+{
+ struct srte_policy *policy = candidate->policy;
+ char endpoint[46];
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ zlog_debug("SR-TE(%s, %u): zebra updated status to %d", endpoint,
+ policy->color, status);
+ switch (status) {
+ case ZEBRA_SR_POLICY_DOWN:
+ switch (policy->status) {
+ /* If the policy is GOING_UP, and zebra faild
+ to install it, we wait for zebra to retry */
+ /* TODO: Add some timeout after which we would
+ get is back to DOWN and remove the
+ policy */
+ case SRTE_POLICY_STATUS_GOING_UP:
+ case SRTE_POLICY_STATUS_DOWN:
+ return;
+ default:
+ zlog_debug("SR-TE(%s, %u): policy is DOWN", endpoint,
+ policy->color);
+ policy->status = SRTE_POLICY_STATUS_DOWN;
+ break;
+ }
+ break;
+ case ZEBRA_SR_POLICY_UP:
+ switch (policy->status) {
+ case SRTE_POLICY_STATUS_UP:
+ return;
+ default:
+ zlog_debug("SR-TE(%s, %u): policy is UP", endpoint,
+ policy->color);
+ policy->status = SRTE_POLICY_STATUS_UP;
+ break;
+ }
+ break;
+ }
+
+ trigger_pathd_candidate_updated(candidate);
+}
+
+/**
+ * Flags the segment lists from give originator for removal.
+ *
+ * The function srte_apply_changes must be called afterward for
+ * the segment list to be removed.
+ *
+ * @param originator The originator tag of the segment list to be marked
+ * @param force If the unset should be forced regardless of the originator
+ */
+void srte_candidate_unset_segment_list(const char *originator, bool force)
+{
+ if (originator == NULL) {
+ zlog_warn(
+ "Cannot unset segment list because originator is NULL");
+ return;
+ }
+
+ zlog_debug("Unset segment lists for originator %s", originator);
+
+ /* Iterate the policies, then iterate each policy's candidate path
+ * to check the candidate path's segment list originator */
+ struct srte_policy *policy;
+ RB_FOREACH (policy, srte_policy_head, &srte_policies) {
+ zlog_debug("Unset segment lists checking policy %s",
+ policy->name);
+ struct srte_candidate *candidate;
+ RB_FOREACH (candidate, srte_candidate_head,
+ &policy->candidate_paths) {
+ zlog_debug("Unset segment lists checking candidate %s",
+ candidate->name);
+ if (candidate->lsp == NULL) {
+ continue;
+ }
+
+ /* The candidate->lsp->segment_list is operational data,
+ * configured by the PCE. We dont want to modify the
+ * candidate->segment_list,
+ * which is configuration data. */
+ struct srte_segment_list *segment_list =
+ candidate->lsp->segment_list;
+ if (segment_list == NULL) {
+ continue;
+ }
+
+ if (segment_list->protocol_origin
+ == SRTE_ORIGIN_LOCAL) {
+ zlog_warn(
+ "Cannot unset segment list %s because it "
+ "was created locally",
+ segment_list->name);
+ continue;
+ }
+
+ /* In the case of last pce,we force the unset
+ * because we don't have pce by prefix (TODO) is all
+ * 'global' */
+ if (strncmp(segment_list->originator, originator,
+ sizeof(segment_list->originator))
+ == 0
+ || force) {
+ zlog_debug("Unset segment list %s",
+ segment_list->name);
+ SET_FLAG(segment_list->flags,
+ F_SEGMENT_LIST_DELETED);
+ SET_FLAG(candidate->flags,
+ F_CANDIDATE_MODIFIED);
+ candidate->lsp->segment_list = NULL;
+ }
+ }
+ }
+}
+
+/**
+ * Gives a string representation of given protocol origin enum.
+ *
+ * @param origin The enum you want a string representation of
+ * @return The string representation of given enum
+ */
+const char *srte_origin2str(enum srte_protocol_origin origin)
+{
+ switch (origin) {
+ case SRTE_ORIGIN_PCEP:
+ return "PCEP";
+ case SRTE_ORIGIN_BGP:
+ return "BGP";
+ case SRTE_ORIGIN_LOCAL:
+ return "Local";
+ default:
+ return "Unknown";
+ }
+}
+
+void trigger_pathd_candidate_created(struct srte_candidate *candidate)
+{
+ /* The hook is called asynchronously to let the PCEP module
+ time to send a response to the PCE before receiving any updates from
+ pathd. In addition, a minimum amount of time need to pass before
+ the hook is called to prevent the hook to be called multiple times
+ from changing the candidate by hand with the console */
+ if (candidate->hook_timer != NULL)
+ return;
+ thread_add_timer(master, trigger_pathd_candidate_created_timer,
+ (void *)candidate, HOOK_DELAY, &candidate->hook_timer);
+}
+
+int trigger_pathd_candidate_created_timer(struct thread *thread)
+{
+ struct srte_candidate *candidate = THREAD_ARG(thread);
+ candidate->hook_timer = NULL;
+ return hook_call(pathd_candidate_created, candidate);
+}
+
+void trigger_pathd_candidate_updated(struct srte_candidate *candidate)
+{
+ /* The hook is called asynchronously to let the PCEP module
+ time to send a response to the PCE before receiving any updates from
+ pathd. In addition, a minimum amount of time need to pass before
+ the hook is called to prevent the hook to be called multiple times
+ from changing the candidate by hand with the console */
+ if (candidate->hook_timer != NULL)
+ return;
+ thread_add_timer(master, trigger_pathd_candidate_updated_timer,
+ (void *)candidate, HOOK_DELAY, &candidate->hook_timer);
+}
+
+int trigger_pathd_candidate_updated_timer(struct thread *thread)
+{
+ struct srte_candidate *candidate = THREAD_ARG(thread);
+ candidate->hook_timer = NULL;
+ return hook_call(pathd_candidate_updated, candidate);
+}
+
+void trigger_pathd_candidate_removed(struct srte_candidate *candidate)
+{
+ /* The hook needs to be call synchronously, otherwise the candidate
+ path will be already deleted when the handler is called */
+ if (candidate->hook_timer != NULL) {
+ thread_cancel(&candidate->hook_timer);
+ candidate->hook_timer = NULL;
+ }
+ hook_call(pathd_candidate_removed, candidate);
+}
+
+const char *srte_candidate_metric_name(enum srte_candidate_metric_type type)
+{
+ switch (type) {
+ case SRTE_CANDIDATE_METRIC_TYPE_IGP:
+ return "IGP";
+ case SRTE_CANDIDATE_METRIC_TYPE_TE:
+ return "TE";
+ case SRTE_CANDIDATE_METRIC_TYPE_HC:
+ return "HC";
+ case SRTE_CANDIDATE_METRIC_TYPE_ABC:
+ return "ABC";
+ case SRTE_CANDIDATE_METRIC_TYPE_LMLL:
+ return "LMLL";
+ case SRTE_CANDIDATE_METRIC_TYPE_CIGP:
+ return "CIGP";
+ case SRTE_CANDIDATE_METRIC_TYPE_CTE:
+ return "CTE";
+ case SRTE_CANDIDATE_METRIC_TYPE_PIGP:
+ return "PIGP";
+ case SRTE_CANDIDATE_METRIC_TYPE_PTE:
+ return "PTE";
+ case SRTE_CANDIDATE_METRIC_TYPE_PHC:
+ return "PHC";
+ case SRTE_CANDIDATE_METRIC_TYPE_MSD:
+ return "MSD";
+ case SRTE_CANDIDATE_METRIC_TYPE_PD:
+ return "PD";
+ case SRTE_CANDIDATE_METRIC_TYPE_PDV:
+ return "PDV";
+ case SRTE_CANDIDATE_METRIC_TYPE_PL:
+ return "PL";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPD:
+ return "PPD";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPDV:
+ return "PPDV";
+ case SRTE_CANDIDATE_METRIC_TYPE_PPL:
+ return "PPL";
+ case SRTE_CANDIDATE_METRIC_TYPE_NAP:
+ return "NAP";
+ case SRTE_CANDIDATE_METRIC_TYPE_NLP:
+ return "NLP";
+ case SRTE_CANDIDATE_METRIC_TYPE_DC:
+ return "DC";
+ case SRTE_CANDIDATE_METRIC_TYPE_BNC:
+ return "BNC";
+ default:
+ return "UNKNOWN";
+ }
+}
diff --git a/pathd/pathd.conf.sample b/pathd/pathd.conf.sample
new file mode 100644
index 0000000000..9fe7d2d6e3
--- /dev/null
+++ b/pathd/pathd.conf.sample
@@ -0,0 +1,41 @@
+! Default pathd configuration sample
+!
+password frr
+log stdout
+
+segment-routing
+ traffic-eng
+ segment-list test1
+ index 10 mpls label 123
+ index 20 mpls label 456
+ !
+ segment-list test2
+ index 10 mpls label 321
+ index 20 mpls label 654
+ !
+ policy color 1 endpoint 1.1.1.1
+ name one
+ binding-sid 100
+ candidate-path preference 100 name test1 explicit segment-list test1
+ candidate-path preference 200 name test2 explicit segment-list test2
+ !
+ policy color 2 endpoint 2.2.2.2
+ name two
+ binding-sid 101
+ candidate-path preference 100 name def explicit segment-list test2
+ candidate-path preference 200 name dyn dynamic
+ bandwidth 12345
+ metric bound abc 16 required
+ metric te 10
+ !
+ !
+ pcep
+ pcc-peer PCE1
+ address ip 127.0.0.1
+ sr-draft07
+ !
+ pcc
+ peer PCE1
+ !
+ !
+!
diff --git a/pathd/pathd.h b/pathd/pathd.h
new file mode 100644
index 0000000000..4879239db8
--- /dev/null
+++ b/pathd/pathd.h
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_PATHD_H_
+#define _FRR_PATHD_H_
+
+#include "lib/mpls.h"
+#include "lib/ipaddr.h"
+#include "lib/srte.h"
+#include "lib/hook.h"
+
+enum srte_protocol_origin {
+ SRTE_ORIGIN_UNDEFINED = 0,
+ SRTE_ORIGIN_PCEP = 1,
+ SRTE_ORIGIN_BGP = 2,
+ SRTE_ORIGIN_LOCAL = 3,
+};
+
+enum srte_policy_status {
+ SRTE_POLICY_STATUS_UNKNOWN = 0,
+ SRTE_POLICY_STATUS_DOWN = 1,
+ SRTE_POLICY_STATUS_UP = 2,
+ SRTE_POLICY_STATUS_GOING_DOWN = 3,
+ SRTE_POLICY_STATUS_GOING_UP = 4
+};
+
+enum srte_candidate_type {
+ SRTE_CANDIDATE_TYPE_UNDEFINED = 0,
+ SRTE_CANDIDATE_TYPE_EXPLICIT = 1,
+ SRTE_CANDIDATE_TYPE_DYNAMIC = 2,
+};
+
+enum srte_candidate_metric_type {
+ /* IGP metric */
+ SRTE_CANDIDATE_METRIC_TYPE_IGP = 1,
+ /* TE metric */
+ SRTE_CANDIDATE_METRIC_TYPE_TE = 2,
+ /* Hop Counts */
+ SRTE_CANDIDATE_METRIC_TYPE_HC = 3,
+ /* Aggregate bandwidth consumption */
+ SRTE_CANDIDATE_METRIC_TYPE_ABC = 4,
+ /* Load of the most loaded link */
+ SRTE_CANDIDATE_METRIC_TYPE_LMLL = 5,
+ /* Cumulative IGP cost */
+ SRTE_CANDIDATE_METRIC_TYPE_CIGP = 6,
+ /* Cumulative TE cost */
+ SRTE_CANDIDATE_METRIC_TYPE_CTE = 7,
+ /* P2MP IGP metric */
+ SRTE_CANDIDATE_METRIC_TYPE_PIGP = 8,
+ /* P2MP TE metric */
+ SRTE_CANDIDATE_METRIC_TYPE_PTE = 9,
+ /* P2MP hop count metric */
+ SRTE_CANDIDATE_METRIC_TYPE_PHC = 10,
+ /* Segment-ID (SID) Depth */
+ SRTE_CANDIDATE_METRIC_TYPE_MSD = 11,
+ /* Path Delay metric */
+ SRTE_CANDIDATE_METRIC_TYPE_PD = 12,
+ /* Path Delay Variation metric */
+ SRTE_CANDIDATE_METRIC_TYPE_PDV = 13,
+ /* Path Loss metric */
+ SRTE_CANDIDATE_METRIC_TYPE_PL = 14,
+ /* P2MP Path Delay metric */
+ SRTE_CANDIDATE_METRIC_TYPE_PPD = 15,
+ /* P2MP Path Delay variation metric */
+ SRTE_CANDIDATE_METRIC_TYPE_PPDV = 16,
+ /* P2MP Path Loss metric */
+ SRTE_CANDIDATE_METRIC_TYPE_PPL = 17,
+ /* Number of adaptations on a path */
+ SRTE_CANDIDATE_METRIC_TYPE_NAP = 18,
+ /* Number of layers on a path */
+ SRTE_CANDIDATE_METRIC_TYPE_NLP = 19,
+ /* Domain Count metric */
+ SRTE_CANDIDATE_METRIC_TYPE_DC = 20,
+ /* Border Node Count metric */
+ SRTE_CANDIDATE_METRIC_TYPE_BNC = 21,
+};
+#define MAX_METRIC_TYPE 21
+
+enum srte_segment_nai_type {
+ SRTE_SEGMENT_NAI_TYPE_NONE = 0,
+ SRTE_SEGMENT_NAI_TYPE_IPV4_NODE = 1,
+ SRTE_SEGMENT_NAI_TYPE_IPV6_NODE = 2,
+ SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY = 3,
+ SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY = 4,
+ SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY = 5
+};
+
+enum objfun_type {
+ OBJFUN_UNDEFINED = 0,
+ /* Minimum Cost Path [RFC5541] */
+ OBJFUN_MCP = 1,
+ /* Minimum Load Path [RFC5541] */
+ OBJFUN_MLP = 2,
+ /* Maximum residual Bandwidth Path [RFC5541] */
+ OBJFUN_MBP = 3,
+ /* Minimize aggregate Bandwidth Consumption [RFC5541] */
+ OBJFUN_MBC = 4,
+ /* Minimize the Load of the most loaded Link [RFC5541] */
+ OBJFUN_MLL = 5,
+ /* Minimize the Cumulative Cost of a set of paths [RFC5541] */
+ OBJFUN_MCC = 6,
+ /* Shortest Path Tree [RFC8306] */
+ OBJFUN_SPT = 7,
+ /* Minimum Cost Tree [RFC8306] */
+ OBJFUN_MCT = 8,
+ /* Minimum Packet Loss Path [RFC8233] */
+ OBJFUN_MPLP = 9,
+ /* Maximum Under-Utilized Path [RFC8233] */
+ OBJFUN_MUP = 10,
+ /* Maximum Reserved Under-Utilized Path [RFC8233] */
+ OBJFUN_MRUP = 11,
+ /* Minimize the number of Transit Domains [RFC8685] */
+ OBJFUN_MTD = 12,
+ /* Minimize the number of Border Nodes [RFC8685] */
+ OBJFUN_MBN = 13,
+ /* Minimize the number of Common Transit Domains [RFC8685] */
+ OBJFUN_MCTD = 14,
+ /* Minimize the number of Shared Links [RFC8800] */
+ OBJFUN_MSL = 15,
+ /* Minimize the number of Shared SRLGs [RFC8800] */
+ OBJFUN_MSS = 16,
+ /* Minimize the number of Shared Nodes [RFC8800] */
+ OBJFUN_MSN = 17,
+};
+#define MAX_OBJFUN_TYPE 17
+
+enum affinity_filter_type {
+ AFFINITY_FILTER_UNDEFINED = 0,
+ AFFINITY_FILTER_EXCLUDE_ANY = 1,
+ AFFINITY_FILTER_INCLUDE_ANY = 2,
+ AFFINITY_FILTER_INCLUDE_ALL = 3,
+};
+#define MAX_AFFINITY_FILTER_TYPE 3
+
+struct srte_segment_list;
+
+struct srte_segment_entry {
+ RB_ENTRY(srte_segment_entry) entry;
+
+ /* The segment list the entry belong to */
+ struct srte_segment_list *segment_list;
+
+ /* Index of the Label. */
+ uint32_t index;
+
+ /* Label Value. */
+ mpls_label_t sid_value;
+
+ /* NAI Type */
+ enum srte_segment_nai_type nai_type;
+ /* NAI local address when nai type is not NONE */
+ struct ipaddr nai_local_addr;
+ /* NAI local interface when nai type is not IPv4 unnumbered adjacency */
+ uint32_t nai_local_iface;
+ /* NAI local interface when nai type is IPv4 or IPv6 adjacency */
+ struct ipaddr nai_remote_addr;
+ /* NAI remote interface when nai type is not IPv4 unnumbered adjacency
+ */
+ uint32_t nai_remote_iface;
+};
+RB_HEAD(srte_segment_entry_head, srte_segment_entry);
+RB_PROTOTYPE(srte_segment_entry_head, srte_segment_entry, entry,
+ srte_segment_entry_compare)
+
+struct srte_segment_list {
+ RB_ENTRY(srte_segment_list) entry;
+
+ /* Name of the Segment List. */
+ char name[64];
+
+ /* The Protocol-Origin. */
+ enum srte_protocol_origin protocol_origin;
+
+ /* The Originator */
+ char originator[64];
+
+ /* Nexthops. */
+ struct srte_segment_entry_head segments;
+
+ /* Status flags. */
+ uint16_t flags;
+#define F_SEGMENT_LIST_NEW 0x0002
+#define F_SEGMENT_LIST_MODIFIED 0x0004
+#define F_SEGMENT_LIST_DELETED 0x0008
+};
+RB_HEAD(srte_segment_list_head, srte_segment_list);
+RB_PROTOTYPE(srte_segment_list_head, srte_segment_list, entry,
+ srte_segment_list_compare)
+
+struct srte_metric {
+ uint16_t flags;
+#define F_METRIC_IS_DEFINED 0x0001
+#define F_METRIC_IS_REQUIRED 0x0002
+#define F_METRIC_IS_BOUND 0x0004
+#define F_METRIC_IS_COMPUTED 0x0008
+ float value;
+};
+
+/* Runtime information about the candidate path */
+struct srte_lsp {
+ /* Backpointer to the Candidate Path. */
+ struct srte_candidate *candidate;
+
+ /* The associated Segment List. */
+ struct srte_segment_list *segment_list;
+
+ /* The Protocol-Origin. */
+ enum srte_protocol_origin protocol_origin;
+
+ /* The Originator */
+ char originator[64];
+
+ /* The Discriminator */
+ uint32_t discriminator;
+
+ /* Flags. */
+ uint32_t flags;
+
+ /* Metrics LSP Values */
+ struct srte_metric metrics[MAX_METRIC_TYPE];
+
+ /* Bandwidth Configured Value */
+ float bandwidth;
+
+ /* The objective function in used */
+ enum objfun_type objfun;
+};
+
+/* Configured candidate path */
+struct srte_candidate {
+ RB_ENTRY(srte_candidate) entry;
+
+ /* Backpointer to SR Policy */
+ struct srte_policy *policy;
+
+ /* The LSP associated with this candidate path. */
+ struct srte_lsp *lsp;
+
+ /* Administrative preference. */
+ uint32_t preference;
+
+ /* Symbolic Name. */
+ char name[64];
+
+ /* The associated Segment List. */
+ struct srte_segment_list *segment_list;
+
+ /* The Protocol-Origin. */
+ enum srte_protocol_origin protocol_origin;
+
+ /* The Originator */
+ char originator[64];
+
+ /* The Discriminator */
+ uint32_t discriminator;
+
+ /* The Type (explicit or dynamic) */
+ enum srte_candidate_type type;
+
+ /* Flags. */
+ uint32_t flags;
+#define F_CANDIDATE_BEST 0x0001
+#define F_CANDIDATE_NEW 0x0002
+#define F_CANDIDATE_MODIFIED 0x0004
+#define F_CANDIDATE_DELETED 0x0008
+#define F_CANDIDATE_HAS_BANDWIDTH 0x0100
+#define F_CANDIDATE_REQUIRED_BANDWIDTH 0x0200
+#define F_CANDIDATE_HAS_OBJFUN 0x0400
+#define F_CANDIDATE_REQUIRED_OBJFUN 0x0800
+#define F_CANDIDATE_HAS_EXCLUDE_ANY 0x1000
+#define F_CANDIDATE_HAS_INCLUDE_ANY 0x2000
+#define F_CANDIDATE_HAS_INCLUDE_ALL 0x4000
+
+ /* Metrics Configured Values */
+ struct srte_metric metrics[MAX_METRIC_TYPE];
+
+ /* Bandwidth Configured Value */
+ float bandwidth;
+
+ /* Configured objective functions */
+ enum objfun_type objfun;
+
+ /* Path constraints attribute filters */
+ uint32_t affinity_filters[MAX_AFFINITY_FILTER_TYPE];
+
+ /* Hooks delaying timer */
+ struct thread *hook_timer;
+};
+
+RB_HEAD(srte_candidate_head, srte_candidate);
+RB_PROTOTYPE(srte_candidate_head, srte_candidate, entry, srte_candidate_compare)
+
+struct srte_policy {
+ RB_ENTRY(srte_policy) entry;
+
+ /* Color */
+ uint32_t color;
+
+ /* Endpoint */
+ struct ipaddr endpoint;
+
+ /* Name */
+ char name[64];
+
+ /* Binding SID */
+ mpls_label_t binding_sid;
+
+ /* Operational Status of the policy */
+ enum srte_policy_status status;
+
+ /* Best candidate path. */
+ struct srte_candidate *best_candidate;
+
+ /* Candidate Paths */
+ struct srte_candidate_head candidate_paths;
+ /* Status flags. */
+ uint16_t flags;
+#define F_POLICY_NEW 0x0002
+#define F_POLICY_MODIFIED 0x0004
+#define F_POLICY_DELETED 0x0008
+};
+RB_HEAD(srte_policy_head, srte_policy);
+RB_PROTOTYPE(srte_policy_head, srte_policy, entry, srte_policy_compare)
+
+DECLARE_HOOK(pathd_candidate_created, (struct srte_candidate * candidate),
+ (candidate))
+DECLARE_HOOK(pathd_candidate_updated, (struct srte_candidate * candidate),
+ (candidate))
+DECLARE_HOOK(pathd_candidate_removed, (struct srte_candidate * candidate),
+ (candidate))
+
+extern struct srte_segment_list_head srte_segment_lists;
+extern struct srte_policy_head srte_policies;
+extern struct zebra_privs_t pathd_privs;
+
+/* master thread, defined in path_main.c */
+extern struct thread_master *master;
+
+/* pathd.c */
+struct srte_segment_list *srte_segment_list_add(const char *name);
+void srte_segment_list_del(struct srte_segment_list *segment_list);
+struct srte_segment_list *srte_segment_list_find(const char *name);
+struct srte_segment_entry *
+srte_segment_entry_add(struct srte_segment_list *segment_list, uint32_t index);
+void srte_segment_entry_del(struct srte_segment_entry *segment);
+void srte_segment_entry_set_nai(struct srte_segment_entry *segment,
+ enum srte_segment_nai_type type,
+ struct ipaddr *local_ip, uint32_t local_iface,
+ struct ipaddr *remote_ip,
+ uint32_t remote_iface);
+struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint);
+void srte_policy_del(struct srte_policy *policy);
+struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint);
+void srte_policy_update_binding_sid(struct srte_policy *policy,
+ uint32_t binding_sid);
+void srte_apply_changes(void);
+void srte_policy_apply_changes(struct srte_policy *policy);
+struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
+ uint32_t preference);
+void srte_candidate_del(struct srte_candidate *candidate);
+void srte_candidate_set_bandwidth(struct srte_candidate *candidate,
+ float bandwidth, bool required);
+void srte_candidate_unset_bandwidth(struct srte_candidate *candidate);
+void srte_candidate_set_metric(struct srte_candidate *candidate,
+ enum srte_candidate_metric_type type,
+ float value, bool required, bool is_cound,
+ bool is_computed);
+void srte_candidate_unset_metric(struct srte_candidate *candidate,
+ enum srte_candidate_metric_type type);
+void srte_candidate_set_objfun(struct srte_candidate *candidate, bool required,
+ enum objfun_type type);
+void srte_candidate_unset_objfun(struct srte_candidate *candidate);
+void srte_candidate_set_affinity_filter(struct srte_candidate *candidate,
+ enum affinity_filter_type type,
+ uint32_t filter);
+void srte_candidate_unset_affinity_filter(struct srte_candidate *candidate,
+ enum affinity_filter_type type);
+void srte_lsp_set_bandwidth(struct srte_lsp *lsp, float bandwidth,
+ bool required);
+void srte_lsp_unset_bandwidth(struct srte_lsp *lsp);
+void srte_lsp_set_metric(struct srte_lsp *lsp,
+ enum srte_candidate_metric_type type, float value,
+ bool required, bool is_cound, bool is_computed);
+void srte_lsp_unset_metric(struct srte_lsp *lsp,
+ enum srte_candidate_metric_type type);
+struct srte_candidate *srte_candidate_find(struct srte_policy *policy,
+ uint32_t preference);
+struct srte_segment_entry *
+srte_segment_entry_find(struct srte_segment_list *segment_list, uint32_t index);
+void srte_candidate_status_update(struct srte_candidate *candidate, int status);
+void srte_candidate_unset_segment_list(const char *originator, bool force);
+const char *srte_origin2str(enum srte_protocol_origin origin);
+
+/* path_cli.c */
+void path_cli_init(void);
+
+#endif /* _FRR_PATHD_H_ */
diff --git a/pathd/subdir.am b/pathd/subdir.am
new file mode 100644
index 0000000000..520a8c696a
--- /dev/null
+++ b/pathd/subdir.am
@@ -0,0 +1,74 @@
+#
+# pathd
+#
+
+if PATHD
+noinst_LIBRARIES += pathd/libpath.a
+sbin_PROGRAMS += pathd/pathd
+dist_examples_DATA += pathd/pathd.conf.sample
+vtysh_scan += $(top_srcdir)/pathd/path_cli.c
+vtysh_daemons += pathd
+# TODO add man page
+#man8 += $(MANBUILD)/pathd.8
+
+if HAVE_PATHD_PCEP
+vtysh_scan += $(top_srcdir)/pathd/path_pcep_cli.c
+module_LTLIBRARIES += pathd/pathd_pcep.la
+endif
+
+endif
+
+pathd_libpath_a_SOURCES = \
+ pathd/path_cli.c \
+ pathd/path_debug.c \
+ pathd/path_errors.c \
+ pathd/path_main.c \
+ pathd/path_memory.c \
+ pathd/path_nb.c \
+ pathd/path_nb_config.c \
+ pathd/path_nb_state.c \
+ pathd/path_zebra.c \
+ pathd/pathd.c \
+ # end
+
+clippy_scan += \
+ pathd/path_cli.c \
+ pathd/path_pcep_cli.c \
+ # end
+
+noinst_HEADERS += \
+ pathd/path_debug.h \
+ 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 \
+ pathd/path_pcep_memory.h \
+ pathd/path_pcep_config.h \
+ pathd/path_pcep_pcc.h \
+ pathd/path_zebra.h \
+ pathd/pathd.h \
+ # end
+
+pathd_pathd_SOURCES = pathd/path_main.c
+nodist_pathd_pathd_SOURCES = \
+ yang/frr-pathd.yang.c \
+ # end
+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 \
+ pathd/path_pcep_memory.c \
+ pathd/path_pcep_config.c \
+ pathd/path_pcep_pcc.c \
+ # end
+pathd_pathd_pcep_la_CFLAGS = $(WERROR)
+pathd_pathd_pcep_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+pathd_pathd_pcep_la_LIBADD = @PATHD_PCEP_LIBS@