summaryrefslogtreecommitdiffstats
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.c1379
-rw-r--r--pathd/path_debug.c110
-rw-r--r--pathd/path_debug.h31
-rw-r--r--pathd/path_errors.c127
-rw-r--r--pathd/path_errors.h33
-rw-r--r--pathd/path_main.c145
-rw-r--r--pathd/path_nb.c350
-rw-r--r--pathd/path_nb.h126
-rw-r--r--pathd/path_nb_config.c759
-rw-r--r--pathd/path_nb_state.c176
-rw-r--r--pathd/path_pcep.c374
-rw-r--r--pathd/path_pcep.h325
-rw-r--r--pathd/path_pcep_cli.c2163
-rw-r--r--pathd/path_pcep_cli.h14
-rw-r--r--pathd/path_pcep_config.c539
-rw-r--r--pathd/path_pcep_config.h33
-rw-r--r--pathd/path_pcep_controller.c1088
-rw-r--r--pathd/path_pcep_controller.h171
-rw-r--r--pathd/path_pcep_debug.c1804
-rw-r--r--pathd/path_pcep_debug.h42
-rw-r--r--pathd/path_pcep_lib.c1336
-rw-r--r--pathd/path_pcep_lib.h36
-rw-r--r--pathd/path_pcep_pcc.c2001
-rw-r--r--pathd/path_pcep_pcc.h136
-rw-r--r--pathd/path_ted.c716
-rw-r--r--pathd/path_ted.h170
-rw-r--r--pathd/path_zebra.c349
-rw-r--r--pathd/path_zebra.h22
-rw-r--r--pathd/pathd.c1476
-rw-r--r--pathd/pathd.h481
-rw-r--r--pathd/subdir.am80
33 files changed, 16604 insertions, 0 deletions
diff --git a/pathd/.gitignore b/pathd/.gitignore
new file mode 100644
index 0000000..95f4a99
--- /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 0000000..b681a9a
--- /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 0000000..45f4ac7
--- /dev/null
+++ b/pathd/path_cli.c
@@ -0,0 +1,1379 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include <float.h>
+#include <math.h>
+#include <zebra.h>
+
+#include "memory.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_cli_clippy.c"
+#include "pathd/path_ted.h"
+
+#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 segment_list_has_src_dst(
+ struct vty *vty, char *xpath, long index, const char *index_str,
+ struct in_addr adj_src_ipv4, struct in_addr adj_dst_ipv4,
+ struct in6_addr adj_src_ipv6, struct in6_addr adj_dst_ipv6,
+ const char *adj_src_ipv4_str, const char *adj_dst_ipv4_str,
+ const char *adj_src_ipv6_str, const char *adj_dst_ipv6_str);
+static int segment_list_has_prefix(
+ struct vty *vty, char *xpath, long index, const char *index_str,
+ const struct prefix_ipv4 *prefix_ipv4, const char *prefix_ipv4_str,
+ const struct prefix_ipv6 *prefix_ipv6, const char *prefix_ipv6_str,
+ const char *has_algo, long algo, const char *algo_str,
+ const char *has_iface_id, long iface_id, const char *iface_id_str);
+
+DEFINE_MTYPE_STATIC(PATHD, PATH_CLI, "Client");
+
+DEFINE_HOOK(pathd_srte_config_write, (struct vty *vty), (vty));
+
+/* 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)# ",
+};
+
+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)# ",
+};
+
+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)# ",
+};
+
+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[ENDPOINT_STR_LENGTH];
+ 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[ENDPOINT_STR_LENGTH];
+ 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, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " segment-list %s\n",
+ yang_dnode_get_string(dnode, "./name"));
+}
+
+void cli_show_srte_segment_list_end(struct vty *vty,
+ const struct lyd_node *dnode)
+{
+ vty_out(vty, " exit\n");
+}
+
+static int segment_list_has_src_dst(
+ struct vty *vty, char *xpath, long index, const char *index_str,
+ struct in_addr adj_src_ipv4, struct in_addr adj_dst_ipv4,
+ struct in6_addr adj_src_ipv6, struct in6_addr adj_dst_ipv6,
+ const char *adj_src_ipv4_str, const char *adj_dst_ipv4_str,
+ const char *adj_src_ipv6_str, const char *adj_dst_ipv6_str)
+{
+ const char *node_src_id;
+ uint32_t ted_sid = MPLS_LABEL_NONE;
+
+ struct ipaddr ip_src = {};
+ struct ipaddr ip_dst = {};
+ if (adj_src_ipv4_str != NULL) {
+ ip_src.ipa_type = IPADDR_V4;
+ ip_src.ip._v4_addr = adj_src_ipv4;
+ ip_dst.ipa_type = IPADDR_V4;
+ ip_dst.ip._v4_addr = adj_dst_ipv4;
+ } else if (adj_src_ipv6_str != NULL) {
+ ip_src.ipa_type = IPADDR_V6;
+ ip_src.ip._v6_addr = adj_src_ipv6;
+ ip_dst.ipa_type = IPADDR_V6;
+ ip_dst.ip._v6_addr = adj_dst_ipv6;
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+ ted_sid = path_ted_query_type_f(&ip_src, &ip_dst);
+ if (ted_sid == MPLS_LABEL_NONE) {
+ zlog_warn(
+ "%s: [rcv ted] CLI NOT FOUND Continue query_type_f SRC (%pIA) DST (%pIA)!",
+ __func__, &ip_src, &ip_dst);
+ }
+ /* type */
+ snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/type",
+ index_str);
+ if (adj_src_ipv4_str != NULL) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ "ipv4_adjacency");
+ node_src_id = adj_src_ipv4_str;
+ } else if (adj_src_ipv6_str != NULL) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ "ipv6_adjacency");
+ node_src_id = adj_src_ipv6_str;
+ } else {
+ /*
+ * This is just to make the compiler happy about
+ * node_src_id not being initialized. This
+ * should never happen unless we change the cli
+ * function.
+ */
+ assert(!"We must have a adj_src_ipv4_str or a adj_src_ipv6_str");
+ }
+
+ /* addresses */
+ snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/local-address",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, node_src_id);
+ snprintf(xpath, XPATH_MAXLEN,
+ "./segment[index='%s']/nai/remote-address", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ adj_dst_ipv4_str ? adj_dst_ipv4_str
+ : adj_dst_ipv6_str);
+ return CMD_SUCCESS;
+}
+int segment_list_has_prefix(
+ struct vty *vty, char *xpath, long index, const char *index_str,
+ const struct prefix_ipv4 *prefix_ipv4, const char *prefix_ipv4_str,
+ const struct prefix_ipv6 *prefix_ipv6, const char *prefix_ipv6_str,
+ const char *has_algo, long algo, const char *algo_str,
+ const char *has_iface_id, long iface_id, const char *iface_id_str)
+{
+ char buf_prefix[INET6_ADDRSTRLEN];
+
+ uint32_t ted_sid = MPLS_LABEL_NONE;
+ struct prefix prefix_cli = {};
+ struct ipaddr pre_ipaddr = {};
+ /* prefix with algorithm or local interface id */
+ /* Type */
+ snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/type",
+ index_str);
+ if (has_iface_id != NULL) {
+ if (prefix_ipv4_str != NULL) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ "ipv4_local_iface");
+ } else if (prefix_ipv6_str != NULL) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ "ipv6_local_iface");
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+ } else {
+ if (prefix_ipv4_str != NULL) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ "ipv4_algo");
+ } else if (prefix_ipv6_str != NULL) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ "ipv6_algo");
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+ }
+ /* Prefix */
+ if (prefix_ipv4_str != NULL) {
+ if (!str2prefix(prefix_ipv4_str, &prefix_cli)) {
+ vty_out(vty, "%% Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ inet_ntop(AF_INET, &prefix_cli.u.prefix4, buf_prefix,
+ sizeof(buf_prefix));
+ pre_ipaddr.ipa_type = IPADDR_V4;
+ pre_ipaddr.ip._v4_addr = prefix_cli.u.prefix4;
+ } else if (prefix_ipv6_str != NULL) {
+ if (!str2prefix(prefix_ipv6_str, &prefix_cli)) {
+ vty_out(vty, "%% Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ inet_ntop(AF_INET6, &prefix_cli.u.prefix6, buf_prefix,
+ sizeof(buf_prefix));
+ pre_ipaddr.ipa_type = IPADDR_V6;
+ pre_ipaddr.ip._v6_addr = prefix_cli.u.prefix6;
+ }
+ snprintf(xpath, XPATH_MAXLEN, "./segment[index='%s']/nai/local-address",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, buf_prefix);
+ snprintf(xpath, XPATH_MAXLEN,
+ "./segment[index='%s']/nai/local-prefix-len", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ prefix_ipv4_str
+ ? strchr(prefix_ipv4_str, '/') + 1
+ : strchr(prefix_ipv6_str, '/') + 1);
+ /* Alg / Iface */
+ if (has_algo != NULL) {
+ snprintf(xpath, XPATH_MAXLEN,
+ "./segment[index='%s']/nai/algorithm", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, algo_str);
+ } else {
+ if (has_iface_id != NULL) {
+ snprintf(xpath, XPATH_MAXLEN,
+ "./segment[index='%s']/nai/local-interface",
+ index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
+ iface_id_str);
+ }
+ }
+ if (has_algo != NULL) {
+ ted_sid = path_ted_query_type_c(&prefix_cli, algo);
+ if (ted_sid == MPLS_LABEL_NONE) {
+ zlog_err(
+ "%s: [rcv ted] CLI NOT FOUND Continue query_type_c PREFIX (%pIA/%d) ALGO (%ld) sid:(%d)!",
+ __func__, &pre_ipaddr, prefix_cli.prefixlen,
+ algo, ted_sid);
+ }
+ }
+ if (has_iface_id != NULL) {
+ ted_sid = path_ted_query_type_e(&prefix_cli, iface_id);
+ if (ted_sid == MPLS_LABEL_NONE) {
+ zlog_err(
+ "%s: [rcv ted] CLI NOT FOUND Continue query_type_e PREFIX (%pIA/%d) IFACE (%ld) sid:(%d)!",
+ __func__, &pre_ipaddr, prefix_cli.prefixlen,
+ iface_id, ted_sid);
+ }
+ }
+ return CMD_SUCCESS;
+}
+/*
+ * XPath: /frr-pathd:pathd/srte/segment-list/segment
+ */
+/* clang-format off */
+DEFPY(srte_segment_list_segment, srte_segment_list_segment_cmd,
+ "index (0-4294967295)$index <[mpls$has_mpls_label label (16-1048575)$label] "
+ "|"
+ "[nai$has_nai <"
+ "prefix <A.B.C.D/M$prefix_ipv4|X:X::X:X/M$prefix_ipv6>"
+ "<algorithm$has_algo (0-1)$algo| iface$has_iface_id (0-4294967295)$iface_id>"
+ "| adjacency$has_adj "
+ "<A.B.C.D$adj_src_ipv4 A.B.C.D$adj_dst_ipv4|X:X::X:X$adj_src_ipv6 X:X::X:X$adj_dst_ipv6>"
+ ">]"
+ ">",
+ "Index\n"
+ "Index Value\n"
+ "MPLS or IP Label\n"
+ "Label\n"
+ "Label Value\n"
+ "Segment NAI\n"
+ "NAI prefix identifier\n"
+ "NAI IPv4 prefix identifier\n"
+ "NAI IPv6 prefix identifier\n"
+ "IGP Algorithm\n"
+ "Algorithm Value SPF or Strict-SPF\n"
+ "Interface Id\n"
+ "Interface Value\n"
+ "ADJ identifier\n"
+ "ADJ IPv4 src identifier\n"
+ "ADJ IPv4 dst identifier\n"
+ "ADJ IPv6 src identifier\n"
+ "ADJ IPv6 dst identifier\n")
+/* clang-format on */
+{
+ char xpath[XPATH_MAXLEN];
+ int status = CMD_SUCCESS;
+
+
+ snprintf(xpath, sizeof(xpath), "./segment[index='%s']", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ if (has_mpls_label != NULL) {
+ snprintf(xpath, sizeof(xpath),
+ "./segment[index='%s']/sid-value", index_str);
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, label_str);
+ return nb_cli_apply_changes(vty, NULL);
+ }
+
+ if (has_adj != NULL) {
+ status = segment_list_has_src_dst(vty, xpath, index, index_str,
+ adj_src_ipv4, adj_dst_ipv4,
+ adj_src_ipv6, adj_dst_ipv6,
+ adj_src_ipv4_str, adj_dst_ipv4_str,
+ adj_src_ipv6_str, adj_dst_ipv6_str);
+ if (status != CMD_SUCCESS)
+ return status;
+ } else {
+ status = segment_list_has_prefix(
+ vty, xpath, index, index_str, prefix_ipv4,
+ prefix_ipv4_str, prefix_ipv6, prefix_ipv6_str, has_algo,
+ algo, algo_str, has_iface_id, iface_id, iface_id_str);
+ if (status != CMD_SUCCESS)
+ return status;
+ }
+
+ 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,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " index %s", yang_dnode_get_string(dnode, "./index"));
+ if (yang_dnode_exists(dnode, "./sid-value")) {
+ vty_out(vty, " mpls label %s",
+ yang_dnode_get_string(dnode, "./sid-value"));
+ }
+ if (yang_dnode_exists(dnode, "./nai")) {
+ struct ipaddr addr;
+ struct ipaddr addr_rmt;
+
+ switch (yang_dnode_get_enum(dnode, "./nai/type")) {
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM:
+ yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
+ vty_out(vty, " nai prefix %pI4", &addr.ipaddr_v4);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM:
+ yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
+ vty_out(vty, " nai prefix %pI6", &addr.ipaddr_v6);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY:
+ yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
+ yang_dnode_get_ip(&addr_rmt, dnode,
+ "./nai/remote-address");
+ vty_out(vty, " nai adjacency %pI4", &addr.ipaddr_v4);
+ vty_out(vty, " %pI4", &addr_rmt.ipaddr_v4);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY:
+ yang_dnode_get_ip(&addr, dnode, "./nai/local-address");
+ yang_dnode_get_ip(&addr_rmt, dnode,
+ "./nai/remote-address");
+ vty_out(vty, " nai adjacency %pI6", &addr.ipaddr_v6);
+ vty_out(vty, " %pI6", &addr_rmt.ipaddr_v6);
+ break;
+ default:
+ break;
+ }
+ if (yang_dnode_exists(dnode, "./nai/local-prefix-len")) {
+ vty_out(vty, "/%s",
+ yang_dnode_get_string(
+ dnode, "./nai/local-prefix-len"));
+ }
+ if (yang_dnode_exists(dnode, "./nai/local-interface")) {
+ vty_out(vty, " iface %s",
+ yang_dnode_get_string(dnode,
+ "./nai/local-interface"));
+ }
+ if (yang_dnode_exists(dnode, "./nai/algorithm")) {
+ vty_out(vty, " algorithm %s",
+ yang_dnode_get_string(dnode,
+ "./nai/algorithm"));
+ }
+ }
+ 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, const 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"));
+}
+
+void cli_show_srte_policy_end(struct vty *vty, const struct lyd_node *dnode)
+{
+ vty_out(vty, " exit\n");
+}
+
+/*
+ * 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, const 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,
+ const 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";
+ case OBJFUN_UNDEFINED:
+ return NULL;
+ }
+
+ assert(!"Reached end of function we should never hit");
+}
+
+DEFPY_NOSH(show_debugging_pathd, show_debugging_pathd_cmd,
+ "show debugging [pathd]",
+ SHOW_STR
+ "State of each debugging option\n"
+ "pathd module debugging\n")
+{
+
+ vty_out(vty, "Path debugging status:\n");
+
+ cmd_show_lib_debugs(vty);
+ /* nothing to do here */
+ path_ted_show_debugging(vty);
+ path_policy_show_debugging(vty);
+ return CMD_SUCCESS;
+}
+
+DEFPY(debug_path_policy, debug_path_policy_cmd, "[no] debug pathd policy",
+ NO_STR DEBUG_STR
+ "path debugging\n"
+ "policy debugging\n")
+{
+ uint32_t mode = DEBUG_NODE2MODE(vty->node);
+ bool no_debug = no;
+
+ DEBUG_MODE_SET(&path_policy_debug, mode, !no);
+ DEBUG_FLAGS_SET(&path_policy_debug, PATH_POLICY_DEBUG_BASIC, !no_debug);
+ 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,
+ const 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, " bandwidth");
+ config_write_float(vty, bandwidth);
+ if (required)
+ vty_out(vty, " required");
+ 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" : "");
+ }
+ }
+}
+
+void cli_show_srte_policy_candidate_path_end(struct vty *vty,
+ const struct lyd_node *dnode)
+{
+ const char *type = yang_dnode_get_string(dnode, "./type");
+
+ if (strmatch(type, "dynamic"))
+ vty_out(vty, " exit\n");
+}
+
+static int config_write_dnode(const struct lyd_node *dnode, void *arg)
+{
+ struct vty *vty = arg;
+
+ nb_cli_show_dnode_cmds(vty, dnode, false);
+
+ return YANG_ITER_CONTINUE;
+}
+
+int config_write_segment_routing(struct vty *vty)
+{
+ vty_out(vty, "segment-routing\n");
+ vty_out(vty, " traffic-eng\n");
+
+ path_ted_config_write(vty);
+
+ yang_dnode_iterate(config_write_dnode, vty, running_config->dnode,
+ "/frr-pathd:pathd/srte/segment-list");
+ yang_dnode_iterate(config_write_dnode, vty, running_config->dnode,
+ "/frr-pathd:pathd/srte/policy");
+
+ hook_call(pathd_srte_config_write, vty);
+
+ vty_out(vty, " exit\n");
+ vty_out(vty, "exit\n");
+
+ return 1;
+}
+
+static int path_policy_cli_debug_config_write(struct vty *vty)
+{
+ if (DEBUG_MODE_CHECK(&path_policy_debug, DEBUG_MODE_CONF)) {
+ if (DEBUG_FLAGS_CHECK(&path_policy_debug,
+ PATH_POLICY_DEBUG_BASIC))
+ vty_out(vty, "debug pathd policy\n");
+ return 1;
+ }
+ return 0;
+}
+
+static int path_policy_cli_debug_set_all(uint32_t flags, bool set)
+{
+ DEBUG_FLAGS_SET(&path_policy_debug, flags, set);
+
+ /* If all modes have been turned off, don't preserve options. */
+ if (!DEBUG_MODE_CHECK(&path_policy_debug, DEBUG_MODE_ALL))
+ DEBUG_CLEAR(&path_policy_debug);
+
+ return 0;
+}
+
+void path_cli_init(void)
+{
+ hook_register(nb_client_debug_config_write,
+ path_policy_cli_debug_config_write);
+ hook_register(nb_client_debug_set_all, path_policy_cli_debug_set_all);
+
+ 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(ENABLE_NODE, &debug_path_policy_cmd);
+ install_element(CONFIG_NODE, &debug_path_policy_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 0000000..cae212d
--- /dev/null
+++ b/pathd/path_debug.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#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 0000000..12a21cb
--- /dev/null
+++ b/pathd/path_debug.h
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#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 0000000..53187fd
--- /dev/null
+++ b/pathd/path_errors.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * pathd-specific error messages.
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#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_PROTOCOL_ERROR,
+ .title = "PCEP protocol error",
+ .description = "The PCE did not respect the PCEP protocol",
+ .suggestion = "Open an Issue with all relevant log files"
+ },
+ {
+ .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 = "Received 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 0000000..69dd023
--- /dev/null
+++ b/pathd/path_errors.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#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_PROTOCOL_ERROR,
+ 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 0000000..c333162
--- /dev/null
+++ b/pathd/path_main.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "getopt.h"
+#include "frrevent.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"
+#include "path_ted.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 event_loop *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");
+ zlog_notice("Unregisterfrom opaque,etc ");
+ pathd_shutdown();
+
+ exit(0);
+}
+
+/* SIGUSR1 handler. */
+static void sigusr1(void)
+{
+ zlog_rotate();
+}
+
+struct frr_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);
+ }
+ }
+
+ master = frr_init();
+
+ access_list_init();
+
+ path_error_init();
+ path_zebra_init(master);
+ path_cli_init();
+ path_ted_init(master);
+
+ frr_config_fork();
+ frr_run(master);
+
+ /* Not reached. */
+ return 0;
+}
diff --git a/pathd/path_nb.c b/pathd/path_nb.c
new file mode 100644
index 0000000..9e91957
--- /dev/null
+++ b/pathd/path_nb.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#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,
+ .cli_show_end = cli_show_srte_segment_list_end,
+ .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,
+ .destroy = pathd_srte_segment_list_segment_sid_value_destroy,
+ },
+ .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/local-prefix-len",
+ .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/segment-list/segment/nai/algorithm",
+ .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,
+ .cli_show_end = cli_show_srte_policy_end,
+ .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,
+ .cli_show_end = cli_show_srte_policy_candidate_path_end,
+ .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, "%s", 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 0000000..21876d7
--- /dev/null
+++ b/pathd/path_nb.h
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#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_segment_list_segment_sid_value_destroy(
+ struct nb_cb_destroy_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, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_segment_list_end(struct vty *vty,
+ const struct lyd_node *dnode);
+void cli_show_srte_segment_list_segment(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_policy(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_policy_end(struct vty *vty, const struct lyd_node *dnode);
+void cli_show_srte_policy_name(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_policy_binding_sid(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_policy_candidate_path(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_srte_policy_candidate_path_end(struct vty *vty,
+ const struct lyd_node *dnode);
+
+/* 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 0000000..ad24f23
--- /dev/null
+++ b/pathd/path_nb_config.c
@@ -0,0 +1,759 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#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_sid_value_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->sid_value = MPLS_LABEL_NONE;
+ 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;
+ uint8_t algo = 0, local_prefix_len = 0;
+ const char *algo_buf, *local_prefix_len_buf;
+
+ 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;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM:
+ algo_buf = yang_dnode_get_string(args->dnode, "./algorithm");
+ algo = atoi(algo_buf);
+ local_prefix_len_buf = yang_dnode_get_string(
+ args->dnode, "./local-prefix-len");
+ local_prefix_len = atoi(local_prefix_len_buf);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE:
+ local_iface =
+ yang_dnode_get_uint32(args->dnode, "./local-interface");
+ local_prefix_len_buf = yang_dnode_get_string(
+ args->dnode, "./local-prefix-len");
+ local_prefix_len = atoi(local_prefix_len_buf);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_NONE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY_LINK_LOCAL_ADDRESSES:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM:
+ break;
+ }
+
+ zlog_debug(" Segment list name (%d) index (%s) ", segment->index,
+ segment->segment_list->name);
+ if (srte_segment_entry_set_nai(segment, type, &local_addr, local_iface,
+ &remote_addr, remote_iface, algo,
+ local_prefix_len))
+ SET_FLAG(segment->segment_list->flags,
+ F_SEGMENT_LIST_SID_CONFLICT);
+}
+
+/*
+ * 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, SRTE_ORIGIN_LOCAL, NULL);
+
+ 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;
+
+ 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:
+ policy = nb_running_get_entry(args->dnode, NULL, true);
+ 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, SRTE_ORIGIN_LOCAL, NULL);
+ 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;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ candidate = nb_running_get_entry(args->dnode, NULL, true);
+ segment_list_name = yang_dnode_get_string(args->dnode, NULL);
+
+ 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 0000000..35b9e37
--- /dev/null
+++ b/pathd/path_nb_state.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#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 0000000..ec9d8ad
--- /dev/null
+++ b/pathd/path_pcep.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#include <zebra.h>
+#include "pceplib/pcep_utils_counters.h"
+
+#include "memory.h"
+#include "log.h"
+#include "command.h"
+#include "libfrr.h"
+#include "printfrr.h"
+#include "lib/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.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"
+#include "pathd/path_pcep_debug.h"
+
+DEFINE_MTYPE(PATHD, PCEP, "PCEP module");
+
+/*
+ * 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_initiate_candidate(struct path *path);
+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);
+
+/* Other static functions */
+static void notify_status(struct path *path, bool not_changed);
+
+/* Module Functions */
+static int pcep_module_finish(void);
+static int pcep_module_late_init(struct event_loop *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);
+}
+
+/* ------------ Other Static Functions ------------ */
+
+void notify_status(struct path *path, bool not_changed)
+{
+ struct path *resp = NULL;
+
+ if ((resp = path_pcep_config_get_path(&path->nbkey))) {
+ resp->srp_id = path->srp_id;
+ flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ "(%s) Send report for candidate path %s", __func__,
+ path->name);
+ pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp,
+ not_changed);
+ }
+}
+
+/* ------------ 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_INITIATE_CANDIDATE:
+ assert(payload != NULL);
+ ret = pcep_main_event_initiate_candidate(
+ (struct path *)payload);
+ 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;
+ case PCEP_MAIN_EVENT_UNDEFINED:
+ 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_initiate_candidate(struct path *path)
+{
+ int ret = 0;
+
+ ret = path_pcep_config_initiate_path(path);
+ if (path->do_remove) {
+ struct pcep_error *error;
+ error = XCALLOC(MTYPE_PCEP, sizeof(*error));
+ error->path = path;
+ error->error_type = PCEP_ERRT_INVALID_OPERATION;
+ switch (ret) {
+ case ERROR_19_1:
+ error->error_value =
+ PCEP_ERRV_LSP_UPDATE_FOR_NON_DELEGATED_LSP;
+ break;
+ case ERROR_19_3:
+ error->error_value =
+ PCEP_ERRV_LSP_UPDATE_UNKNOWN_PLSP_ID;
+ break;
+ case ERROR_19_9:
+ error->error_value = PCEP_ERRV_LSP_NOT_PCE_INITIATED;
+ break;
+ default:
+ zlog_warn("(%s)PCE tried to REMOVE unknown error!",
+ __func__);
+ XFREE(MTYPE_PCEP, error);
+ pcep_free_path(path);
+ return ret;
+ break;
+ }
+ pcep_ctrl_send_error(pcep_g->fpt, path->pcc_id, error);
+ } else if (ret != PATH_NB_ERR && path->srp_id != 0)
+ notify_status(path, ret == PATH_NB_NO_CHANGE);
+ return ret;
+}
+
+int pcep_main_event_update_candidate(struct path *path)
+{
+ int ret = 0;
+
+ ret = path_pcep_config_update_path(path);
+ if (ret != PATH_NB_ERR && path->srp_id != 0)
+ notify_status(path, ret == PATH_NB_NO_CHANGE);
+ 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 ------------ */
+
+/* this creates threads, therefore must run after fork(). but it must also
+ * run before config load, so the CLI commands don't try to touch things that
+ * aren't set up yet...
+ */
+static int pcep_module_config_pre(struct event_loop *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;
+
+ return 0;
+}
+
+static int pcep_module_late_init(struct event_loop *tm)
+{
+ 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_config_pre, pcep_module_config_pre);
+ 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 0000000..5c6a023
--- /dev/null
+++ b/pathd/path_pcep.h
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#ifndef _PATH_PCEP_H_
+#define _PATH_PCEP_H_
+
+#include <stdbool.h>
+#include <debug.h>
+#include <netinet/tcp.h>
+#include "memory.h"
+#include "pceplib/pcep_utils_logging.h"
+#include "pceplib/pcep_pcc_api.h"
+#include "mpls.h"
+#include "pathd/pathd.h"
+
+DECLARE_MTYPE(PCEP);
+
+#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[PCEP_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 event_loop *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;
+
+struct pcep_error {
+ struct path *path;
+ int error_type;
+ int error_value;
+ /* Rfc 8281 PcInitiated error on bad values */
+#define ERROR_19_1 1
+#define ERROR_19_3 2
+#define ERROR_19_9 3
+};
+
+/* 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 0000000..c46649b
--- /dev/null
+++ b/pathd/path_pcep_cli.c
@@ -0,0 +1,2163 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 Volta Networks, Inc
+ * Brady Johnson
+ */
+
+#include <zebra.h>
+#include "pceplib/pcep_utils_counters.h"
+#include "pceplib/pcep_session_logic.h"
+
+#include "log.h"
+#include "command.h"
+#include "libfrr.h"
+#include "printfrr.h"
+#include "lib/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.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"
+
+#include "pathd/path_pcep_cli_clippy.c"
+
+#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 void reset_pcc_peer(const char *peer_name);
+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(const char *name);
+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,
+ .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,
+ .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,
+ .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,
+ .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;
+ }
+ }
+ strlcpy(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth,
+ tcp_md5_auth_str,
+ sizeof(pce_opts_cli->pce_opts.config_opts.tcp_md5_auth));
+
+ 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;
+ localtime_r(&group->start_time, &tm_info);
+ strftime(tm_buffer, sizeof(tm_buffer), "%Y-%m-%d %H:%M:%S", &tm_info);
+
+ vty_out(vty, "PCEP counters since %s (%uh %um %us):\n", tm_buffer,
+ (uint32_t)(diff_time / 3600), (uint32_t)((diff_time / 60) % 60),
+ (uint32_t)(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, bool reset)
+{
+ struct pcep_config_group_opts *pce_config = NULL;
+ struct pce_opts *pce_opts = &current_pce_opts_g->pce_opts;
+ bool pce_in_use = false;
+
+ if (vty->node == PCEP_PCE_NODE) {
+ pce_config = &current_pce_opts_g->pce_config_group_opts;
+ current_pce_opts_g->merged = false;
+ pce_in_use = pcep_cli_pcc_has_pce(pce_opts->pce_name);
+ } 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 = reset ? DEFAULT_SR_DRAFT07 : true;
+
+ if (pce_in_use) {
+ vty_out(vty, "%% PCE in use, resetting pcc peer session...\n");
+ reset_pcc_peer(pce_opts->pce_name);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_peer_pce_initiated(struct vty *vty, bool reset)
+{
+ struct pcep_config_group_opts *pce_config = NULL;
+ struct pce_opts *pce_opts = &current_pce_opts_g->pce_opts;
+ bool pce_in_use = false;
+
+ if (vty->node == PCEP_PCE_NODE) {
+ pce_config = &current_pce_opts_g->pce_config_group_opts;
+ current_pce_opts_g->merged = false;
+ pce_in_use = pcep_cli_pcc_has_pce(pce_opts->pce_name);
+ } 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 = reset ? DEFAULT_PCE_INITIATED : true;
+
+ if (pce_in_use) {
+ vty_out(vty, "%% PCE in use, resetting pcc peer session...\n");
+ reset_pcc_peer(pce_opts->pce_name);
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int path_pcep_cli_peer_tcp_md5_auth(struct vty *vty,
+ const char *tcp_md5_auth,
+ bool reset)
+{
+ struct pcep_config_group_opts *pce_config = NULL;
+ struct pce_opts *pce_opts = &current_pce_opts_g->pce_opts;
+ bool pce_in_use = false;
+
+ if (vty->node == PCEP_PCE_NODE) {
+ pce_config = &current_pce_opts_g->pce_config_group_opts;
+ current_pce_opts_g->merged = false;
+ pce_in_use = pcep_cli_pcc_has_pce(pce_opts->pce_name);
+ } else if (vty->node == PCEP_PCE_CONFIG_NODE) {
+ pce_config = current_pcep_config_group_opts_g;
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+
+ if (reset)
+ pce_config->tcp_md5_auth[0] = '\0';
+ else
+ strlcpy(pce_config->tcp_md5_auth, tcp_md5_auth,
+ sizeof(pce_config->tcp_md5_auth));
+
+ if (pce_in_use) {
+ vty_out(vty, "%% PCE in use, resetting pcc peer session...\n");
+ reset_pcc_peer(pce_opts->pce_name);
+ }
+
+ 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,
+ bool reset)
+{
+ struct pcep_config_group_opts *pce_config = NULL;
+ struct pce_opts *pce_opts = &current_pce_opts_g->pce_opts;
+ bool pce_in_use = false;
+
+ if (vty->node == PCEP_PCE_NODE) {
+ pce_config = &current_pce_opts_g->pce_config_group_opts;
+ current_pce_opts_g->merged = false;
+ pce_in_use = pcep_cli_pcc_has_pce(pce_opts->pce_name);
+ } else if (vty->node == PCEP_PCE_CONFIG_NODE) {
+ pce_config = current_pcep_config_group_opts_g;
+ } else {
+ return CMD_ERR_NO_MATCH;
+ }
+
+ if (reset) {
+ pce_config->source_ip.ipa_type = IPADDR_NONE;
+ pce_config->source_port = 0;
+ return CMD_SUCCESS;
+ }
+
+ /* 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);
+
+ if (pce_in_use) {
+ vty_out(vty, "%% PCE in use, resetting pcc peer session...\n");
+ reset_pcc_peer(pce_opts->pce_name);
+ }
+
+ 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;
+ struct pce_opts *pce_opts = &current_pce_opts_g->pce_opts;
+ bool pce_in_use = false;
+
+ if (vty->node == PCEP_PCE_NODE) {
+ pce_config = &current_pce_opts_g->pce_config_group_opts;
+ current_pce_opts_g->merged = false;
+ pce_in_use = pcep_cli_pcc_has_pce(pce_opts->pce_name);
+ } 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);
+
+ if (pce_in_use) {
+ vty_out(vty, "%% PCE in use, resetting pcc peer session...\n");
+ reset_pcc_peer(pce_opts->pce_name);
+ }
+
+ 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, bool reset)
+{
+ if (reset)
+ pcc_msd_configured_g = false;
+ else {
+ pcc_msd_configured_g = true;
+ PCEP_VTYSH_INT_ARG_CHECK(msd_str, msd, pcc_msd_g, 0, 33);
+ }
+
+ return CMD_SUCCESS;
+}
+
+void reset_pcc_peer(const char *peer_name)
+{
+ struct pce_opts_cli *pce_opts_cli = pcep_cli_find_pce(peer_name);
+
+ /* Remove the pcc peer */
+ pcep_cli_remove_pce_connection(&pce_opts_cli->pce_opts);
+ 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);
+
+ /* Re-add the pcc peer */
+ pcep_cli_merge_pcep_pce_config_options(pce_opts_cli);
+ pcep_cli_add_pce_connection(&pce_opts_cli->pce_opts);
+
+ /* Update the pcc_opts */
+ 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(pcc_opts_copy->addr));
+ pcc_opts_copy->msd = pcc_msd_g;
+ pcc_opts_copy->port = pce_opts_cli->pce_opts.config_opts.source_port;
+ pcep_ctrl_update_pcc_options(pcep_g->fpt, pcc_opts_copy);
+
+ /* Update the 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_update_pce_options(pcep_g->fpt, pce_opts_copy);
+}
+
+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(zero_v6_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(pcc_opts_copy->addr));
+ 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 %u seconds, since %d-%02d-%02d %02d:%02d:%02d UTC\n",
+ (uint32_t)(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");
+ pcep_cli_pcep_pce_config_write(vty);
+ pcep_cli_pce_config_write(vty);
+ pcep_cli_pcc_config_write(vty);
+ vty_out(vty, " exit\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) {
+ goto exit;
+ }
+
+ 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;
+ }
+exit:
+ vty_out(vty, " exit\n");
+
+ 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';
+
+ vty_out(vty, " exit\n");
+ }
+
+ 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;
+
+ vty_out(vty, " exit\n");
+ }
+
+ 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(
+ pcep_cli_no_pcep,
+ pcep_cli_no_pcep_cmd,
+ "no pcep",
+ NO_STR
+ "PCEP configuration\n")
+{
+ /* Delete PCCs */
+ path_pcep_cli_pcc_delete(vty);
+
+ for (int i = 0; i < MAX_PCE; i++) {
+ /* Delete PCEs */
+ if (pcep_g->pce_opts_cli[i] != NULL) {
+ XFREE(MTYPE_PCEP, pcep_g->pce_opts_cli[i]);
+ pcep_g->pce_opts_cli[i] = NULL;
+ pcep_g->num_pce_opts_cli--;
+ }
+
+ /* Delete PCE-CONFIGs */
+ if (pcep_g->config_group_opts[i] != NULL) {
+ XFREE(MTYPE_PCEP, pcep_g->config_group_opts[i]);
+ pcep_g->config_group_opts[i] = NULL;
+ pcep_g->num_config_group_opts--;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY_NOSH(
+ pcep_cli_pcep_pce_config,
+ pcep_cli_pcep_pce_config_cmd,
+ "pce-config WORD$name",
+ "Shared configuration\n"
+ "Shared configuration name\n")
+{
+ return path_pcep_cli_pcep_pce_config(vty, name);
+}
+
+DEFPY(pcep_cli_pcep_no_pce_config,
+ pcep_cli_pcep_no_pce_config_cmd,
+ "no pce-config WORD$name",
+ NO_STR
+ "Shared configuration\n"
+ "Shared configuration name\n")
+{
+ 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,
+ "pce WORD$name",
+ "PCE configuration, address sub-config is mandatory\n"
+ "PCE name\n")
+{
+ return path_pcep_cli_pce(vty, name);
+}
+
+DEFPY(pcep_cli_no_pce,
+ pcep_cli_no_pce_cmd,
+ "no pce WORD$name",
+ NO_STR
+ "PCE configuration, address sub-config is mandatory\n"
+ "PCE name\n")
+{
+ 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,
+ "[no] sr-draft07",
+ NO_STR
+ "Configure PCC to send PCEP Open with SR draft07\n")
+{
+ return path_pcep_cli_peer_sr_draft07(vty, no);
+}
+
+DEFPY(pcep_cli_peer_pce_initiated,
+ pcep_cli_peer_pce_initiated_cmd,
+ "[no] pce-initiated",
+ NO_STR
+ "Configure PCC to accept PCE initiated LSPs\n")
+{
+ return path_pcep_cli_peer_pce_initiated(vty, no);
+}
+
+DEFPY(pcep_cli_peer_tcp_md5_auth,
+ pcep_cli_peer_tcp_md5_auth_cmd,
+ "[no] tcp-md5-auth WORD",
+ NO_STR
+ "Configure PCC TCP-MD5 RFC2385 Authentication\n"
+ "TCP-MD5 Authentication string\n")
+{
+ return path_pcep_cli_peer_tcp_md5_auth(vty, tcp_md5_auth, no);
+}
+
+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,
+ "[no] source-address [ip A.B.C.D | ipv6 X:X::X:X] [port (1024-65535)]",
+ NO_STR
+ "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, no);
+}
+
+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,
+ "pcc",
+ "PCC configuration\n")
+{
+ return path_pcep_cli_pcc(vty);
+}
+
+DEFPY(pcep_cli_no_pcc,
+ pcep_cli_no_pcc_cmd,
+ "no pcc",
+ NO_STR
+ "PCC configuration\n")
+{
+ return path_pcep_cli_pcc_delete(vty);
+}
+
+DEFPY(pcep_cli_pcc_pcc_msd,
+ pcep_cli_pcc_pcc_msd_cmd,
+ "[no] msd (1-32)",
+ NO_STR
+ "PCC maximum SID depth \n"
+ "PCC maximum SID depth value\n")
+{
+ return path_pcep_cli_pcc_pcc_msd(vty, msd_str, msd, no);
+}
+
+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(pathd_srte_config_write, pcep_cli_pcep_config_write);
+ 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);
+ install_element(SR_TRAFFIC_ENG_NODE, &pcep_cli_no_pcep_cmd);
+
+ /* PCEP configuration group related configuration commands */
+ install_element(PCEP_NODE, &pcep_cli_pcep_pce_config_cmd);
+ install_element(PCEP_NODE, &pcep_cli_pcep_no_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_NODE, &pcep_cli_no_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_NODE, &pcep_cli_no_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 0000000..a6fd858
--- /dev/null
+++ b/pathd/path_pcep_cli.h
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 Volta Networks, Inc
+ * Brady Johnson
+ */
+
+#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 0000000..da7ee89
--- /dev/null
+++ b/pathd/path_pcep_config.c
@@ -0,0 +1,539 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include <northbound.h>
+#include <yang.h>
+#include <printfrr.h>
+#include "pceplib/pcep_msg_objects.h"
+#include "pathd/pathd.h"
+#include "pathd/path_pcep.h"
+#include "pathd/path_pcep_config.h"
+#include "pathd/path_pcep_debug.h"
+#include "frrevent.h"
+
+#define MAX_XPATH 256
+#define MAX_FLOAT_LEN 22
+#define INETADDR4_MAXLEN 16
+#define INETADDR6_MAXLEN 40
+#define INITIATED_CANDIDATE_PREFERENCE 255
+#define INITIATED_POLICY_COLOR 1
+
+
+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);
+
+void path_pcep_refine_path(struct path *path)
+{
+ struct srte_candidate *candidate = lookup_candidate(&path->nbkey);
+ struct srte_lsp *lsp;
+
+ if (candidate == NULL)
+ return;
+
+ 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;
+}
+
+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 = policy->srp_id,
+ .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:
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM:
+ 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;
+ case SRTE_SEGMENT_NAI_TYPE_NONE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY_LINK_LOCAL_ADDRESSES:
+ break;
+ }
+ last_hop = hop;
+ }
+ return hop;
+}
+
+int path_pcep_config_initiate_path(struct path *path)
+{
+ struct srte_policy *policy;
+ struct srte_candidate *candidate;
+
+ if (path->do_remove) {
+ zlog_warn("PCE %s tried to REMOVE pce-initiate a path ",
+ path->originator);
+ candidate = lookup_candidate(&path->nbkey);
+ if (candidate) {
+ if (!path->is_delegated) {
+ zlog_warn(
+ "(%s)PCE tried to REMOVE but it's not Delegated!",
+ __func__);
+ return ERROR_19_1;
+ }
+ if (candidate->type != SRTE_CANDIDATE_TYPE_DYNAMIC) {
+ zlog_warn(
+ "(%s)PCE tried to REMOVE but it's not PCE origin!",
+ __func__);
+ return ERROR_19_9;
+ }
+ zlog_warn(
+ "(%s)PCE tried to REMOVE found candidate!, let's remove",
+ __func__);
+ candidate->policy->srp_id = path->srp_id;
+ SET_FLAG(candidate->policy->flags, F_POLICY_DELETED);
+ SET_FLAG(candidate->flags, F_CANDIDATE_DELETED);
+ } else {
+ zlog_warn("(%s)PCE tried to REMOVE not existing LSP!",
+ __func__);
+ return ERROR_19_3;
+ }
+ srte_apply_changes();
+ } else {
+ assert(!IS_IPADDR_NONE(&path->nbkey.endpoint));
+
+ if (path->nbkey.preference == 0)
+ path->nbkey.preference = INITIATED_CANDIDATE_PREFERENCE;
+
+ if (path->nbkey.color == 0)
+ path->nbkey.color = INITIATED_POLICY_COLOR;
+
+ candidate = lookup_candidate(&path->nbkey);
+ if (!candidate) {
+ policy = srte_policy_add(
+ path->nbkey.color, &path->nbkey.endpoint,
+ SRTE_ORIGIN_PCEP, path->originator);
+ strlcpy(policy->name, path->name, sizeof(policy->name));
+ policy->binding_sid = path->binding_sid;
+ SET_FLAG(policy->flags, F_POLICY_NEW);
+ candidate = srte_candidate_add(
+ policy, path->nbkey.preference,
+ SRTE_ORIGIN_PCEP, path->originator);
+ candidate->policy->srp_id = path->srp_id;
+ strlcpy(candidate->name, path->name,
+ sizeof(candidate->name));
+ SET_FLAG(candidate->flags, F_CANDIDATE_NEW);
+ } else {
+ policy = candidate->policy;
+ if ((path->originator != candidate->originator)
+ || (path->originator != policy->originator)) {
+ /* There is already an initiated path from
+ * another PCE, show a warning and regect the
+ * initiated path */
+ zlog_warn(
+ "PCE %s tried to initiate a path already initiated by PCE %s",
+ path->originator,
+ candidate->originator);
+ return 1;
+ }
+ if ((policy->protocol_origin != SRTE_ORIGIN_PCEP)
+ || (candidate->protocol_origin
+ != SRTE_ORIGIN_PCEP)) {
+ /* There is already an initiated path from
+ * another PCE, show a warning and regect the
+ * initiated path */
+ zlog_warn(
+ "PCE %s tried to initiate a path created localy",
+ path->originator);
+ return 1;
+ }
+ }
+ return path_pcep_config_update_path(path);
+ }
+ return 0;
+}
+
+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);
+
+ int number_of_sid_clashed = 0;
+ 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;
+
+ candidate->policy->srp_id = path->srp_id;
+ // first clean up old segment list if present
+ if (candidate->lsp->segment_list) {
+ SET_FLAG(candidate->lsp->segment_list->flags,
+ F_SEGMENT_LIST_DELETED);
+ srte_segment_list_del(candidate->lsp->segment_list);
+ candidate->lsp->segment_list = NULL;
+ }
+
+ if (path->first_hop == NULL)
+ return PATH_NB_ERR;
+
+ 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)
+ continue;
+ if (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, 0, 0)
+ == PATH_SID_ERROR)
+ /* TED queries don't match PCE */
+ /* Don't apply srte,zebra changes */
+ number_of_sid_clashed++;
+ }
+
+ 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,
+ (enum srte_candidate_metric_type)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;
+ }
+
+ if (number_of_sid_clashed)
+ SET_FLAG(segment_list->flags, F_SEGMENT_LIST_SID_CONFLICT);
+ else
+ 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)
+{
+ if (candidate->protocol_origin == SRTE_ORIGIN_PCEP
+ || candidate->protocol_origin == SRTE_ORIGIN_BGP)
+ return asprintfrr(MTYPE_PCEP, "%s", candidate->policy->name);
+ else
+ 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;
+ case SRTE_POLICY_STATUS_DOWN:
+ case SRTE_POLICY_STATUS_UNKNOWN:
+ return PCEP_LSP_OPERATIONAL_DOWN;
+ }
+
+ assert(!"Reached end of function where we are not expecting to");
+}
+
+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;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY_LINK_LOCAL_ADDRESSES:
+ return PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE:
+ return PCEP_SR_SUBOBJ_NAI_IPV4_NODE;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE:
+ return PCEP_SR_SUBOBJ_NAI_IPV6_NODE;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM:
+ return PCEP_SR_SUBOBJ_NAI_IPV4_NODE;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM:
+ return PCEP_SR_SUBOBJ_NAI_IPV6_NODE;
+ 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;
+ case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY:
+ case PCEP_SR_SUBOBJ_NAI_UNKNOWN:
+ return SRTE_SEGMENT_NAI_TYPE_NONE;
+ }
+
+ assert(!"Reached end of function where we were not expecting to");
+}
diff --git a/pathd/path_pcep_config.h b/pathd/path_pcep_config.h
new file mode 100644
index 0000000..e933728
--- /dev/null
+++ b/pathd/path_pcep_config.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#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.
+ * MUST BE CALLED FROM THE MAIN THREAD */
+void path_pcep_refine_path(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_initiate_path(struct path *path);
+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 0000000..a00a114
--- /dev/null
+++ b/pathd/path_pcep_controller.c
@@ -0,0 +1,1088 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "command.h"
+#include "libfrr.h"
+#include "printfrr.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
+
+/* 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,
+ EV_SEND_REPORT,
+ EV_SEND_ERROR,
+ EV_PATH_REFINED
+};
+
+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;
+};
+
+struct pcep_refine_path_event_data {
+ struct ctrl_state *ctrl_state;
+ int pcc_id;
+ pcep_refine_callback_t continue_lsp_update_handler;
+ struct path *path;
+ void *payload;
+};
+
+/* Synchronous call arguments */
+
+struct get_counters_args {
+ struct ctrl_state *ctrl_state;
+ int pcc_id;
+ struct counters_group *counters;
+};
+
+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);
+static void pcep_refine_path_event_cb(struct event *thread);
+
+/* Internal Functions Called From Controller Thread */
+static void pcep_thread_finish_event_handler(struct event *thread);
+
+/* 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 event **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 event **thread, pcep_ctrl_thread_callback timer_cb);
+static void pcep_thread_timer_handler(struct event *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 event **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 void pcep_thread_event_handler(struct event *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);
+static void
+pcep_thread_path_refined_event(struct ctrl_state *ctrl_state,
+ struct pcep_refine_path_event_data *data);
+
+/* 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 void pcep_main_event_handler(struct event *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 event_loop *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 counters_group *counters = NULL;
+ struct pcc_state *pcc_state;
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ if (pcc_state) {
+ counters = pcep_lib_copy_counters(pcc_state->sess);
+ }
+ return 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 pcc_state *pcc_state;
+ pcep_session *session = NULL;
+
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ if (pcc_state) {
+ session = pcep_lib_copy_pcep_session(pcc_state->sess);
+ }
+ return 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 *pcc_info = XCALLOC(MTYPE_PCEP, sizeof(*pcc_info));
+ if( pcc_info && ctrl_state){
+ strlcpy(pcc_info->pce_name, pce_name, sizeof(pcc_info->pce_name));
+ pcep_pcc_copy_pcc_info(ctrl_state->pcc, pcc_info);
+ }
+
+ return pcc_info;
+}
+
+int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
+ struct path *path, bool is_stable)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, pcc_id, EV_SEND_REPORT, is_stable,
+ path);
+}
+
+
+int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id,
+ struct pcep_error *error)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, pcc_id, EV_SEND_ERROR, 0, error);
+}
+
+
+/* ------------ Internal Functions Called from Main Thread ------------ */
+
+int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res)
+{
+ event_add_event(fpt->master, pcep_thread_finish_event_handler,
+ (void *)fpt, 0, NULL);
+ pthread_join(fpt->thread, res);
+
+ return 0;
+}
+
+void pcep_refine_path_event_cb(struct event *thread)
+{
+ struct pcep_refine_path_event_data *data = EVENT_ARG(thread);
+ assert(data != NULL);
+ struct ctrl_state *ctrl_state = data->ctrl_state;
+ struct path *path = data->path;
+ assert(path != NULL);
+ int pcc_id = data->pcc_id;
+
+
+ path_pcep_refine_path(path);
+ send_to_thread(ctrl_state, pcc_id, EV_PATH_REFINED, 0, data);
+}
+
+
+/* ------------ 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_initiate_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path)
+{
+ send_to_main(ctrl_state, pcc_id, PCEP_MAIN_EVENT_INITIATE_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 event **thread)
+{
+
+ schedule_thread_timer(ctrl_state, pcc_id, TM_CALCULATE_BEST_PCE,
+ TO_UNDEFINED, delay, NULL, thread);
+}
+
+void pcep_thread_cancel_timer(struct event **thread)
+{
+ if (thread == NULL || *thread == NULL) {
+ return;
+ }
+
+ struct pcep_ctrl_timer_data *data = EVENT_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()) {
+ event_cancel(thread);
+ } else {
+ event_cancel_async((*thread)->master, thread, NULL);
+ }
+}
+
+void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id,
+ int retry_count, struct event **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 event **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 event **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 event **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;
+}
+
+int pcep_thread_refine_path(struct ctrl_state *ctrl_state, int pcc_id,
+ pcep_refine_callback_t cb, struct path *path,
+ void *payload)
+{
+ struct pcep_refine_path_event_data *data;
+
+ data = XCALLOC(MTYPE_PCEP, sizeof(*data));
+ data->ctrl_state = ctrl_state;
+ data->path = path;
+ data->pcc_id = pcc_id;
+ data->continue_lsp_update_handler = cb;
+ data->payload = payload;
+
+ event_add_event(ctrl_state->main, pcep_refine_path_event_cb,
+ (void *)data, 0, NULL);
+ return 0;
+}
+
+void pcep_thread_path_refined_event(struct ctrl_state *ctrl_state,
+ struct pcep_refine_path_event_data *data)
+{
+ assert(data != NULL);
+ int pcc_id = data->pcc_id;
+ pcep_refine_callback_t continue_lsp_update_handler = data->continue_lsp_update_handler;
+ assert(continue_lsp_update_handler != NULL);
+ struct path *path = data->path;
+ void *payload = data->payload;
+ struct pcc_state *pcc_state = NULL;
+ XFREE(MTYPE_PCEP, data);
+
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ continue_lsp_update_handler(ctrl_state, pcc_state, path, payload);
+}
+
+
+/* ------------ Internal Functions Called From Controller Thread ------------ */
+
+void pcep_thread_finish_event_handler(struct event *thread)
+{
+ int i;
+ struct frr_pthread *fpt = EVENT_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);
+}
+
+/* ------------ 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 event **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;
+
+ event_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 event **thread)
+{
+ return schedule_thread_timer_with_cb(ctrl_state, pcc_id, timer_type,
+ timeout_type, delay, payload,
+ thread, pcep_thread_timer_handler);
+}
+
+void pcep_thread_timer_handler(struct event *thread)
+{
+ /* data unpacking */
+ struct pcep_ctrl_timer_data *data = EVENT_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);
+
+ 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;
+ 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;
+ 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 */
+ 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;
+ case TM_PCEPLIB_TIMER:
+ case TM_UNDEFINED:
+ case TM_MAX:
+ flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ "Unknown controller timer triggered: %u", timer_type);
+ break;
+ }
+}
+
+void pcep_thread_pcep_event(struct event *thread)
+{
+ struct pcep_ctrl_event_data *data = EVENT_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);
+}
+
+/* ------------ 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 event **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) {
+ event_add_read(ctrl_state->self, socket_cb, (void *)data, fd,
+ thread);
+ } else {
+ event_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 event **)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 event **)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;
+
+ event_add_event(ctrl_state->self, event_cb, (void *)data, 0, NULL);
+
+ return 0;
+}
+
+void pcep_thread_event_handler(struct event *thread)
+{
+ /* data unpacking */
+ struct pcep_ctrl_event_data *data = EVENT_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);
+
+ /* Possible sub-type values */
+ enum pcep_pathd_event_type path_event_type = PCEP_PATH_UNDEFINED;
+
+ /* Possible payload values, maybe an union would be better... */
+ struct path *path = NULL;
+ struct pcc_opts *pcc_opts = NULL;
+ struct pce_opts *pce_opts = NULL;
+ struct pcc_state *pcc_state = NULL;
+ struct pcep_refine_path_event_data *refine_data = NULL;
+
+ struct path *path_copy = NULL;
+ struct pcep_error *error = NULL;
+
+ switch (type) {
+ case EV_UPDATE_PCC_OPTS:
+ assert(payload != NULL);
+ pcc_opts = (struct pcc_opts *)payload;
+ 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;
+ pcep_thread_event_update_pce_options(ctrl_state, pcc_id,
+ pce_opts);
+ break;
+ case EV_REMOVE_PCC:
+ pce_opts = (struct pce_opts *)payload;
+ if (pcep_thread_event_remove_pcc(ctrl_state, pce_opts) == 0)
+ 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;
+ 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:
+ 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);
+ 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;
+ case EV_SEND_REPORT:
+ assert(payload != NULL);
+ path = (struct path *)payload;
+ if (pcc_id == 0) {
+ for (int i = 0; i < MAX_PCC; i++) {
+ if (ctrl_state->pcc[i]) {
+ path_copy = pcep_copy_path(path);
+ pcep_pcc_send_report(
+ ctrl_state, ctrl_state->pcc[i],
+ path_copy, (bool)sub_type);
+ }
+ }
+ } else {
+ pcc_state =
+ pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_pcc_send_report(ctrl_state, pcc_state, path,
+ (bool)sub_type);
+ }
+ break;
+ case EV_PATH_REFINED:
+ assert(payload != NULL);
+ refine_data = (struct pcep_refine_path_event_data *)payload;
+ pcep_thread_path_refined_event(ctrl_state, refine_data);
+ break;
+ case EV_SEND_ERROR:
+ assert(payload != NULL);
+ error = (struct pcep_error *)payload;
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_pcc_send_error(ctrl_state, pcc_state, error,
+ (bool)sub_type);
+ break;
+ case EV_PCEPLIB_EVENT:
+ flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ "Unexpected event received in controller thread: %u",
+ type);
+ break;
+ }
+}
+
+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;
+
+ event_add_event(ctrl_state->main, pcep_main_event_handler, (void *)data,
+ 0, NULL);
+ return 0;
+}
+
+void pcep_main_event_handler(struct event *thread)
+{
+ /* data unpacking */
+ struct pcep_main_event_data *data = EVENT_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);
+
+ 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";
+ case TM_CALCULATE_BEST_PCE:
+ return "BEST_PCE";
+ case TM_SESSION_TIMEOUT_PCC:
+ return "TIMEOUT_PCC";
+ case TM_MAX:
+ return "UNKNOWN";
+ }
+
+ assert(!"Reached end of function where we did not expect to");
+}
+
+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";
+ case TO_MAX:
+ return "UNKNOWN";
+ }
+
+ assert(!"Reached end of function where we did not expect to");
+}
diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h
new file mode 100644
index 0000000..236b267
--- /dev/null
+++ b/pathd/path_pcep_controller.h
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#ifndef _PATH_PCEP_CONTROLLER_H_
+#define _PATH_PCEP_CONTROLLER_H_
+
+#include "pathd/path_pcep.h"
+
+struct ctrl_state;
+struct pcc_state;
+
+enum pcep_main_event_type {
+ PCEP_MAIN_EVENT_UNDEFINED = 0,
+ PCEP_MAIN_EVENT_START_SYNC,
+ PCEP_MAIN_EVENT_INITIATE_CANDIDATE,
+ 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);
+typedef void (*pcep_refine_callback_t)(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct path *path, void *payload);
+
+enum pcep_pathd_event_type {
+ PCEP_PATH_UNDEFINED = 0,
+ PCEP_PATH_CREATED,
+ PCEP_PATH_UPDATED,
+ PCEP_PATH_REMOVED
+};
+
+struct ctrl_state {
+ struct event_loop *main;
+ struct event_loop *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 void (*pcep_ctrl_thread_callback)(struct event *);
+
+/* 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 event_loop *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);
+
+/* Asynchronously send a report. The caller is giving away the path structure,
+ * it shouldn't be allocated on the stack. If `pcc_id` is `0` the report is
+ * sent by all PCCs. The parameter is_stable is used to hint whether the status
+ * will soon change, this is used to ensure all report updates are sent even
+ * when missing status update events */
+int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
+ struct path *path, bool is_stable);
+
+int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id,
+ struct pcep_error *error);
+
+/* 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_initiate_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path);
+void pcep_thread_cancel_timer(struct event **thread);
+void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id,
+ int retry_count, struct event **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 event **thread);
+void pcep_thread_schedule_session_timeout(struct ctrl_state *ctrl_state,
+ int pcc_id, int delay,
+ struct event **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 event **thread);
+void pcep_thread_schedule_pceplib_timer(struct ctrl_state *ctrl_state,
+ int delay, void *payload,
+ struct event **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);
+void pcep_thread_pcep_event(struct event *thread);
+int pcep_thread_pcc_count(struct ctrl_state *ctrl_state);
+/* Called by the PCC to refine a path in the main thread */
+int pcep_thread_refine_path(struct ctrl_state *ctrl_state, int pcc_id,
+ pcep_refine_callback_t cb, struct path *path,
+ void *payload);
+
+#endif // _PATH_PCEP_CONTROLLER_H_
diff --git a/pathd/path_pcep_debug.c b/pathd/path_pcep_debug.c
new file mode 100644
index 0000000..7bff9c7
--- /dev/null
+++ b/pathd/path_pcep_debug.c
@@ -0,0 +1,1804 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#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";
+ }
+
+ assert(!"Reached end of function where we do not expect to");
+}
+
+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";
+ case PCC_CONNECTION_FAILURE:
+ return "PCC_CONNECTION_FAILURE";
+ case PCC_SENT_INVALID_OPEN:
+ return "PCC_SENT_INVALID_OPEN";
+ }
+
+ assert(!"Reached end of function where we do not expect to");
+}
+
+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_START_TLS:
+ return "START_TLS";
+ case PCEP_TYPE_MAX:
+ return "UNKNOWN";
+ }
+
+ assert(!"Reached end of function where we are not expecting to");
+}
+
+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";
+ case PCEP_OBJ_CLASS_MAX:
+ return "UNKNOWN";
+ }
+
+ assert(!"Reached end of function where we are not expecting to");
+}
+
+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";
+ }
+
+ assert(!"Reached end of function where we do not expect to");
+}
+
+
+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_OBJECTIVE_FUNCTION_LIST:
+ return "OBJECTIVE_FUNCTION_LIST";
+ case PCEP_OBJ_TLV_TYPE_VENDOR_INFO:
+ return "VENDOR_INFO";
+ 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";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID:
+ return "SRPOLICY_POL_ID";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME:
+ return "SRPOLICY_POL_NAME";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID:
+ return "SRPOLICY_CPATH_ID";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE:
+ return "SRPOLICY_CPATH_PREFERENCE";
+ case PCEP_OBJ_TLV_TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case PCEP_OBJ_TLV_TYPE_ARBITRARY:
+ return "ARBITRARY";
+ case PCEP_OBJ_TYPE_CISCO_BSID:
+ return "CISCO_BSID";
+ }
+
+ assert(!"Reached end of function where we do not expect to");
+}
+
+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";
+ case RO_SUBOBJ_UNKNOWN:
+ return "UNKNOWN";
+ }
+
+ assert(!"Reached end of function where we do not expect to");
+}
+
+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";
+ case PCEP_SR_SUBOBJ_NAI_UNKNOWN:
+ return "UNKNOWN";
+ }
+
+ assert(!"Reached end of function where we do not expect to");
+}
+
+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();
+}
+
+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;
+ case IPADDR_NONE:
+ 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: %pI6(%u)/%pI6(%u)\n", ps, "",
+ &hop->nai.local_addr.ipaddr_v6,
+ hop->nai.local_iface,
+ &hop->nai.remote_addr.ipaddr_v6,
+ hop->nai.remote_iface);
+ break;
+ case PCEP_SR_SUBOBJ_NAI_ABSENT:
+ case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY:
+ case PCEP_SR_SUBOBJ_NAI_UNKNOWN:
+ PATHD_FORMAT("%*sNAI: UNSUPPORTED\n", ps, "");
+ break;
+ }
+ }
+}
+
+void _format_pcep_event(int ps, pcep_event *event)
+{
+ char buf[32];
+
+ 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_r(&event->event_time, buf));
+ 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;
+ case RO_SUBOBJ_TYPE_IPV6:
+ case RO_SUBOBJ_TYPE_LABEL:
+ case RO_SUBOBJ_TYPE_UNNUM:
+ case RO_SUBOBJ_TYPE_ASN:
+ case RO_SUBOBJ_UNKNOWN:
+ 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;
+ case PCEP_SR_SUBOBJ_NAI_ABSENT:
+ case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY:
+ case PCEP_SR_SUBOBJ_NAI_UNKNOWN:
+ 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;
+ case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR:
+ case PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST:
+ case PCEP_OBJ_TLV_TYPE_VENDOR_INFO:
+ case PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS:
+ case PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS:
+ case PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE:
+ case PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC:
+ case PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION:
+ case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID:
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE:
+ case PCEP_OBJ_TLV_TYPE_UNKNOWN:
+ case PCEP_OBJ_TYPE_CISCO_BSID:
+ case PCEP_OBJ_TLV_TYPE_ARBITRARY:
+ 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 0000000..cc63ca0
--- /dev/null
+++ b/pathd/path_pcep_debug.h
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#ifndef _PATH_PCEP_DEBUG_H_
+#define _PATH_PCEP_DEBUG_H_
+
+#include "pathd/path_debug.h"
+#include "pceplib/pcep_pcc_api.h"
+#include "pceplib/pcep_msg_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);
+
+#endif // _PATH_PCEP_DEBUG_H_
diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c
new file mode 100644
index 0000000..d43fdb0
--- /dev/null
+++ b/pathd/path_pcep_lib.c
@@ -0,0 +1,1336 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+
+#include <debug.h>
+#include "pceplib/pcep_utils_counters.h"
+#include "pceplib/pcep_timers.h"
+#include "pathd/path_errors.h"
+#include "pathd/path_pcep.h"
+#include "pathd/path_pcep_lib.h"
+#include "pathd/path_pcep_debug.h"
+
+DEFINE_MTYPE_STATIC(PATHD, PCEPLIB_INFRA, "PCEPlib Infrastructure");
+DEFINE_MTYPE_STATIC(PATHD, PCEPLIB_MESSAGES, "PCEPlib PCEP Messages");
+
+#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
+#define MAX_PATH_NAME_SIZE 255
+
+/* pceplib logging callback */
+static int pceplib_logging_cb(int level, const char *fmt, va_list args)
+ PRINTFRR(2, 0);
+
+/* 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 void pcep_lib_socket_read_ready(struct event *thread);
+static void pcep_lib_socket_write_ready(struct event *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_lsp_symbolic_name(
+ struct path *path, struct pcep_object_tlv_symbolic_path_name *tlv);
+static void pcep_lib_parse_metric(struct path *path,
+ struct pcep_object_metric *obj);
+static void
+pcep_lib_parse_endpoints_ipv4(struct path *path,
+ struct pcep_object_endpoints_ipv4 *obj);
+static void
+pcep_lib_parse_endpoints_ipv6(struct path *path,
+ struct pcep_object_endpoints_ipv6 *obj);
+static void pcep_lib_parse_vendor_info(struct path *path,
+ struct pcep_object_vendor_info *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 = pcep_options->pce_initiated;
+ 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[0] != '\0') {
+ config->is_tcp_auth_md5 = true;
+ strlcpy(config->tcp_authentication_str,
+ pcep_options->tcp_md5_auth,
+ sizeof(config->tcp_authentication_str));
+ } 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
+ */
+
+void pcep_lib_socket_write_ready(struct event *thread)
+{
+ struct pcep_ctrl_socket_data *data = EVENT_ARG(thread);
+ assert(data != NULL);
+
+ pceplib_external_socket_write(data->fd, data->payload);
+ XFREE(MTYPE_PCEP, data);
+}
+
+void pcep_lib_socket_read_ready(struct event *thread)
+{
+ struct pcep_ctrl_socket_data *data = EVENT_ARG(thread);
+ assert(data != NULL);
+
+ pceplib_external_socket_read(data->fd, data->payload);
+ XFREE(MTYPE_PCEP, data);
+}
+
+/* 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,
+ struct path *path)
+{
+ double_linked_list *objs, *srp_tlvs;
+ struct pcep_object_srp *srp;
+ struct pcep_object_tlv_header *tlv;
+
+ if ((path == NULL) || (path->srp_id == 0))
+ return pcep_msg_create_error(error_type, error_value);
+
+ objs = dll_initialize();
+ srp_tlvs = dll_initialize();
+ tlv = (struct pcep_object_tlv_header *)pcep_tlv_create_path_setup_type(
+ SR_TE_PST);
+ dll_append(srp_tlvs, tlv);
+ srp = pcep_obj_create_srp(path->do_remove, path->srp_id, srp_tlvs);
+ dll_append(objs, srp);
+ return pcep_msg_create_error_with_objects(error_type, error_value,
+ objs);
+}
+
+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;
+ struct pcep_object_endpoints_ipv4 *epv4 = NULL;
+ struct pcep_object_endpoints_ipv6 *epv6 = NULL;
+ struct pcep_object_vendor_info *vendor_info = 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;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV4):
+ epv4 = (struct pcep_object_endpoints_ipv4 *)obj;
+ pcep_lib_parse_endpoints_ipv4(path, epv4);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV6):
+ epv6 = (struct pcep_object_endpoints_ipv6 *)obj;
+ pcep_lib_parse_endpoints_ipv6(path, epv6);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_VENDOR_INFO,
+ PCEP_OBJ_TYPE_VENDOR_INFO):
+ vendor_info = (struct pcep_object_vendor_info *)obj;
+ pcep_lib_parse_vendor_info(path, vendor_info);
+ 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),
+ PCEP_OBJ_TYPE_CISCO_BSID);
+ 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;
+ case PCEP_SR_SUBOBJ_NAI_ABSENT:
+ case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY:
+ case PCEP_SR_SUBOBJ_NAI_UNKNOWN:
+ 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;
+ case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR:
+ case PCEP_OBJ_TLV_TYPE_VENDOR_INFO:
+ case PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS:
+ case PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS:
+ case PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE:
+ case PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC:
+ case PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION:
+ case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID:
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE:
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE:
+ case PCEP_OBJ_TLV_TYPE_UNKNOWN:
+ case PCEP_OBJ_TLV_TYPE_ARBITRARY:
+ case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
+ case PCEP_OBJ_TYPE_CISCO_BSID:
+ 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;
+
+ if (tlvs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty RP's TLV plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
+ return;
+ }
+ /* 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;
+ case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR:
+ case PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST:
+ case PCEP_OBJ_TLV_TYPE_VENDOR_INFO:
+ case PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS:
+ case PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS:
+ case PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE:
+ case PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC:
+ case PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION:
+ case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID:
+ case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE:
+ case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_UNKNOWN:
+ case PCEP_OBJ_TLV_TYPE_ARBITRARY:
+ case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
+ case PCEP_OBJ_TYPE_CISCO_BSID:
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
+ 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;
+
+ if (tlvs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty SRP's TLV plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
+ return;
+ }
+ 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;
+ case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR:
+ case PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST:
+ case PCEP_OBJ_TLV_TYPE_VENDOR_INFO:
+ case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
+ case PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS:
+ case PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS:
+ case PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE:
+ case PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC:
+ case PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION:
+ case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID:
+ case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE:
+ case PCEP_OBJ_TLV_TYPE_UNKNOWN:
+ case PCEP_OBJ_TYPE_CISCO_BSID:
+ case PCEP_OBJ_TLV_TYPE_ARBITRARY:
+ 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;
+ struct pcep_object_tlv_symbolic_path_name *name;
+ struct pcep_object_tlv_arbitrary *arb_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) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty LSP's TLV plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
+ return;
+ }
+
+ 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_SYMBOLIC_PATH_NAME:
+ name = (struct pcep_object_tlv_symbolic_path_name *)tlv;
+ pcep_lib_parse_lsp_symbolic_name(path, name);
+ break;
+ case PCEP_OBJ_TYPE_CISCO_BSID:
+ arb_tlv = (struct pcep_object_tlv_arbitrary *)tlv;
+ memcpy(&path->binding_sid, arb_tlv->data + 2,
+ sizeof(path->binding_sid));
+ path->binding_sid = ntohl(path->binding_sid);
+ path->binding_sid = (path->binding_sid >> 12);
+ break;
+ case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR:
+ case PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST:
+ case PCEP_OBJ_TLV_TYPE_VENDOR_INFO:
+ case PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS:
+ case PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS:
+ case PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE:
+ case PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC:
+ case PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION:
+ case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID:
+ case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE:
+ case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID:
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE:
+ case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
+ case PCEP_OBJ_TLV_TYPE_UNKNOWN:
+ case PCEP_OBJ_TLV_TYPE_ARBITRARY:
+ 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_lsp_symbolic_name(
+ struct path *path, struct pcep_object_tlv_symbolic_path_name *tlv)
+{
+ uint16_t size = tlv->symbolic_path_name_length;
+ assert(path->name == NULL);
+ size = size > MAX_PATH_NAME_SIZE ? MAX_PATH_NAME_SIZE : size;
+ path->name = XCALLOC(MTYPE_PCEP, size);
+ strlcpy((char *)path->name, tlv->symbolic_path_name, size + 1);
+}
+
+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_endpoints_ipv4(struct path *path,
+ struct pcep_object_endpoints_ipv4 *obj)
+{
+ SET_IPADDR_V4(&path->pcc_addr);
+ path->pcc_addr.ipaddr_v4 = obj->src_ipv4;
+ SET_IPADDR_V4(&path->nbkey.endpoint);
+ path->nbkey.endpoint.ipaddr_v4 = obj->dst_ipv4;
+}
+
+void pcep_lib_parse_endpoints_ipv6(struct path *path,
+ struct pcep_object_endpoints_ipv6 *obj)
+{
+ SET_IPADDR_V6(&path->pcc_addr);
+ path->pcc_addr.ipaddr_v6 = obj->src_ipv6;
+ SET_IPADDR_V6(&path->nbkey.endpoint);
+ path->nbkey.endpoint.ipaddr_v6 = obj->dst_ipv6;
+}
+
+void pcep_lib_parse_vendor_info(struct path *path,
+ struct pcep_object_vendor_info *obj)
+{
+ if (obj->enterprise_number == ENTERPRISE_NUMBER_CISCO
+ && obj->enterprise_specific_info == ENTERPRISE_COLOR_CISCO)
+ path->nbkey.color = obj->enterprise_specific_info1;
+ else
+ path->nbkey.color = 0;
+}
+
+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;
+
+ if (objs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty ERO's sub_obj plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
+ return;
+ }
+ 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;
+ case RO_SUBOBJ_TYPE_IPV4:
+ case RO_SUBOBJ_TYPE_IPV6:
+ case RO_SUBOBJ_TYPE_LABEL:
+ case RO_SUBOBJ_TYPE_UNNUM:
+ case RO_SUBOBJ_TYPE_ASN:
+ case RO_SUBOBJ_UNKNOWN:
+ 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;
+ case PCEP_SR_SUBOBJ_NAI_ABSENT:
+ case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY:
+ case PCEP_SR_SUBOBJ_NAI_UNKNOWN:
+ 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 0000000..6bba344
--- /dev/null
+++ b/pathd/path_pcep_lib.h
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#ifndef _PATH_PCEP_LIB_H_
+#define _PATH_PCEP_LIB_H_
+
+#include <stdbool.h>
+#include "pceplib/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 *path);
+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_pcc.c b/pathd/path_pcep_pcc.c
new file mode 100644
index 0000000..f18eff2
--- /dev/null
+++ b/pathd/path_pcep_pcc.c
@@ -0,0 +1,2001 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+/* 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 "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.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
+
+pthread_mutex_t g_pcc_info_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+/* 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_initiate(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 continue_pcep_lsp_update(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ struct path *path, void *payload);
+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,
+ struct path *trigger_path);
+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 struct req_entry *pop_req_no_reqid(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) {
+ event_cancel(&pcc_state->t_reconnect);
+ pcc_state->t_reconnect = NULL;
+ }
+
+ if (pcc_state->t_update_best != NULL) {
+ event_cancel(&pcc_state->t_update_best);
+ pcc_state->t_update_best = NULL;
+ }
+
+ if (pcc_state->t_session_timeout != NULL) {
+ event_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)
+{
+ assert(pcc_state->status == PCEP_PCC_DISCONNECTED);
+ assert(pcc_state->sess == NULL);
+
+ if (pcc_state->t_reconnect != NULL) {
+ event_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 %pIA:%d due to missing PCC IPv4 address",
+ &pcc_state->pce_opts->addr,
+ 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 %pIA:%d due to missing PCC IPv6 address",
+ &pcc_state->pce_opts->addr,
+ 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 %pIA:%d due to missing PCC address",
+ &pcc_state->pce_opts->addr,
+ 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 %pIA:%d from %pIA:%d",
+ &pcc_state->pce_opts->addr,
+ pcc_state->pce_opts->port,
+ &pcc_state->pcc_addr_tr,
+ 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) {
+ event_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;
+ case PCEP_PCC_INITIALIZED:
+ return 1;
+ }
+
+ assert(!"Reached end of function where we are not expecting to");
+}
+
+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,
+ bool is_stable)
+{
+ if ((pcc_state->status != PCEP_PCC_OPERATING)
+ || (!pcc_state->caps.is_stateful)) {
+ pcep_free_path(path);
+ return;
+ }
+
+ PCEP_DEBUG("(%s)%s Send report for candidate path %s", __func__,
+ pcc_state->tag, path->name);
+
+ /* 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 = path->status;
+ path->status = PCEP_LSP_OPERATIONAL_DOWN;
+ send_report(pcc_state, path);
+
+ /* If no update is expected and the real status wasn't down, we need to
+ * send a second report with the real status */
+ if (is_stable && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) {
+ PCEP_DEBUG("(%s)%s Send report for candidate path (!DOWN) %s",
+ __func__, pcc_state->tag, path->name);
+ path->status = real_status;
+ send_report(pcc_state, path);
+ }
+
+ pcep_free_path(path);
+}
+
+
+void pcep_pcc_send_error(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct pcep_error *error,
+ bool sub_type)
+{
+
+ PCEP_DEBUG("(%s) Send error after PcInitiated ", __func__);
+
+
+ send_pcep_error(pcc_state, error->error_type, error->error_value,
+ error->path);
+ pcep_free_path(error->path);
+ XFREE(MTYPE_PCEP, error);
+}
+/* ------------ Timeout handler ------------ */
+
+void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state,
+ enum pcep_ctrl_timeout_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;
+ case TO_UNDEFINED:
+ case TO_MAX:
+ 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;
+ /* Removed as response to a PcInitiated 'R'emove*/
+ /* RFC 8281 #5.4 LSP Deletion*/
+ path->do_remove = path->was_removed;
+ if (pcc_state->caps.is_stateful)
+ send_report(pcc_state, path);
+ return;
+ case PCEP_PATH_UNDEFINED:
+ 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;
+ case PCC_CONNECTION_FAILURE:
+ 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) {
+ pthread_mutex_lock(&g_pcc_info_mtx);
+ // 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;
+ }
+ }
+ }
+ }
+ pthread_mutex_unlock(&g_pcc_info_mtx);
+ }
+
+ 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;
+ if(pcc_state->pcc_opts){
+ 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;
+ pthread_mutex_lock(&g_pcc_info_mtx);
+ pcc_info->is_best_multi_pce = pcc_state->is_best;
+ pcc_info->previous_best = pcc_state->previous_best;
+ pthread_mutex_unlock(&g_pcc_info_mtx);
+ pcc_info->precedence =
+ pcc_state->pce_opts ? pcc_state->pce_opts->precedence : 0;
+ if(pcc_state->pcc_addr_tr.ipa_type != IPADDR_NONE){
+ 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;
+ case PCEP_TYPE_OPEN:
+ case PCEP_TYPE_KEEPALIVE:
+ case PCEP_TYPE_PCREQ:
+ case PCEP_TYPE_PCNOTF:
+ case PCEP_TYPE_ERROR:
+ case PCEP_TYPE_CLOSE:
+ case PCEP_TYPE_REPORT:
+ case PCEP_TYPE_START_TLS:
+ case PCEP_TYPE_MAX:
+ 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)
+{
+ struct path *path;
+ path = pcep_lib_parse_path(msg);
+ lookup_nbkey(pcc_state, path);
+ pcep_thread_refine_path(ctrl_state, pcc_state->id,
+ &continue_pcep_lsp_update, path, NULL);
+}
+
+void continue_pcep_lsp_update(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct path *path,
+ void *payload)
+{
+ char err[MAX_ERROR_MSG_SIZE] = {0};
+
+ 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)
+{
+ char err[MAX_ERROR_MSG_SIZE] = "";
+ struct path *path;
+
+ path = pcep_lib_parse_path(msg);
+
+ if (!pcc_state->pce_opts->config_opts.pce_initiated) {
+ /* PCE Initiated is not enabled */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "Not allowed PCE initiated path received: %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state, PCEP_ERRT_LSP_INSTANTIATE_ERROR,
+ PCEP_ERRV_UNACCEPTABLE_INSTANTIATE_ERROR, path);
+ return;
+ }
+
+ if (path->do_remove) {
+ // lookup in nbkey sequential as no endpoint
+ struct nbkey_map_data *key;
+ char endpoint[46];
+
+ frr_each (nbkey_map, &pcc_state->nbkey_map, key) {
+ ipaddr2str(&key->nbkey.endpoint, endpoint,
+ sizeof(endpoint));
+ flog_warn(
+ EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "FOR_EACH nbkey [color (%d) endpoint (%s)] path [plsp_id (%d)] ",
+ key->nbkey.color, endpoint, path->plsp_id);
+ if (path->plsp_id == key->plspid) {
+ flog_warn(
+ EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "FOR_EACH MATCH nbkey [color (%d) endpoint (%s)] path [plsp_id (%d)] ",
+ key->nbkey.color, endpoint,
+ path->plsp_id);
+ path->nbkey = key->nbkey;
+ break;
+ }
+ }
+ } else {
+ if (path->first_hop == NULL /*ero sets first_hop*/) {
+ /* If the PCC receives a PCInitiate message without an
+ * ERO and the R flag in the SRP object != zero, then it
+ * MUST send a PCErr message with Error-type=6
+ * (Mandatory Object missing) and Error-value=9 (ERO
+ * object missing). */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "ERO object missing or incomplete : %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state,
+ PCEP_ERRT_LSP_INSTANTIATE_ERROR,
+ PCEP_ERRV_INTERNAL_ERROR, path);
+ return;
+ }
+
+ if (path->plsp_id != 0) {
+ /* If the PCC receives a PCInitiate message with a
+ * non-zero PLSP-ID and the R flag in the SRP object set
+ * to zero, then it MUST send a PCErr message with
+ * Error-type=19 (Invalid Operation) and Error-value=8
+ * (Non-zero PLSP-ID in the LSP Initiate Request) */
+ flog_warn(
+ EC_PATH_PCEP_PROTOCOL_ERROR,
+ "PCE initiated path with non-zero PLSP ID: %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_INIT_NON_ZERO_PLSP_ID,
+ path);
+ return;
+ }
+
+ if (path->name == NULL) {
+ /* If the PCC receives a PCInitiate message without a
+ * SYMBOLIC-PATH-NAME TLV, then it MUST send a PCErr
+ * message with Error-type=10 (Reception of an invalid
+ * object) and Error-value=8 (SYMBOLIC-PATH-NAME TLV
+ * missing) */
+ flog_warn(
+ EC_PATH_PCEP_PROTOCOL_ERROR,
+ "PCE initiated path without symbolic name: %s",
+ format_pcep_message(msg));
+ send_pcep_error(
+ pcc_state, PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_SYMBOLIC_PATH_NAME_TLV_MISSING, path);
+ return;
+ }
+ }
+
+ /* TODO: If there is a conflict with the symbolic path name of an
+ * existing LSP, the PCC MUST send a PCErr message with Error-type=23
+ * (Bad Parameter value) and Error-value=1 (SYMBOLIC-PATH-NAME in
+ * use) */
+
+ specialize_incoming_path(pcc_state, path);
+ /* TODO: Validate the PCC address received from the PCE is valid */
+ PCEP_DEBUG("%s Received LSP initiate", pcc_state->tag);
+ PCEP_DEBUG_PATH("%s", format_path(path));
+
+ if (validate_incoming_path(pcc_state, path, err, sizeof(err))) {
+ pcep_thread_initiate_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);
+ send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_NOT_PCE_INITIATED, path);
+ pcep_free_path(path);
+ }
+}
+
+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);
+ if (path->no_path) {
+ req = pop_req_no_reqid(pcc_state, path->req_id);
+ } else {
+ 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, NULL);
+ 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);
+ free_req_entry(req);
+ pcep_free_path(path);
+ return;
+ } 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, req->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->ipaddr_v4 = pcc_state->pcc_addr_v4;
+ taddr->ipa_type = IPADDR_V4;
+ }
+ } else {
+ if (CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)) {
+ taddr->ipaddr_v6 = pcc_state->pcc_addr_v6;
+ taddr->ipa_type = IPADDR_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 path *trigger_path)
+{
+ 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, trigger_path);
+ 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)
+{
+ if (IS_IPADDR_NONE(&path->pcc_addr))
+ 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, NULL);
+ 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;
+ struct pcep_message *msg;
+
+ if (!pcc_state->is_best) {
+ return;
+ }
+
+ specialize_outgoing_path(pcc_state, req->path);
+
+ PCEP_DEBUG(
+ "%s Sending computation request %d for path %s to %pIA (retry %d)",
+ pcc_state->tag, req->path->req_id, req->path->name,
+ &req->path->nbkey.endpoint, 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;
+
+ timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds;
+ 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)
+{
+ 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 %pIA (retry %d)",
+ pcc_state->tag, req->path->req_id, req->path->name,
+ &req->path->nbkey.endpoint, 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;
+}
+
+struct req_entry *pop_req_no_reqid(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);
+
+ 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)
+{
+ struct req_entry key = {.path = path};
+ struct req_entry *req;
+
+
+ PCEP_DEBUG_PATH("(%s) %s", format_path(path), __func__);
+ /* Looking for request without result */
+ if (path->no_path || !path->first_hop) {
+ PCEP_DEBUG_PATH("%s Path : no_path|!first_hop", __func__);
+ /* ...and already was handle */
+ req = RB_FIND(req_entry_head, &pcc_state->requests, &key);
+ if (!req) {
+ /* we must purge remaining reqid */
+ PCEP_DEBUG_PATH("%s Purge pending reqid: no_path(%s)",
+ __func__,
+ path->no_path ? "TRUE" : "FALSE");
+ if (lookup_reqid(pcc_state, path) != 0) {
+ PCEP_DEBUG_PATH("%s Purge pending reqid: DONE ",
+ __func__);
+ remove_reqid_mapping(pcc_state, path);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+
+ 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);
+ case IPADDR_NONE:
+ return hash;
+ }
+
+ assert(!"Reached end of function where we were not expecting to");
+}
+
+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 0000000..f3d0f29
--- /dev/null
+++ b/pathd/path_pcep_pcc.h
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#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 event *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 event *t_reconnect;
+ struct event *t_update_best;
+ struct event *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_timeout_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);
+/* Send a report explicitly. When doing so the PCC may send multiple reports
+ * due to expectations from vendors for the first report to be with a DOWN
+ * status. The parameter is_stable is used for that purpose as a hint wheter
+ * to expect an update for the report */
+void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct path *path,
+ bool is_stable);
+void pcep_pcc_send_error(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct pcep_error *path,
+ bool is_stable);
+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_ted.c b/pathd/path_ted.c
new file mode 100644
index 0000000..df23f93
--- /dev/null
+++ b/pathd/path_ted.c
@@ -0,0 +1,716 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 Volta Networks, Inc
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <zebra.h>
+
+#include <stdlib.h>
+
+#include "memory.h"
+#include "log.h"
+#include "command.h"
+#include "prefix.h"
+#include <lib/json.h>
+
+#include "pathd.h"
+#include "pathd/path_errors.h"
+#include "pathd/path_ted.h"
+
+#include "pathd/path_ted_clippy.c"
+
+static struct ls_ted *path_ted_create_ted(void);
+static void path_ted_register_vty(void);
+static void path_ted_unregister_vty(void);
+static uint32_t path_ted_start_importing_igp(const char *daemon_str);
+static uint32_t path_ted_stop_importing_igp(void);
+static enum zclient_send_status path_ted_link_state_sync(void);
+static void path_ted_timer_handler_sync(struct event *thread);
+static void path_ted_timer_handler_refresh(struct event *thread);
+static int path_ted_cli_debug_config_write(struct vty *vty);
+static int path_ted_cli_debug_set_all(uint32_t flags, bool set);
+
+extern struct zclient *zclient;
+
+struct ted_state ted_state_g = {};
+
+/*
+ * path_path_ted public API function implementations
+ */
+
+void path_ted_init(struct event_loop *master)
+{
+ ted_state_g.main = master;
+ ted_state_g.link_state_delay_interval = TIMER_RETRY_DELAY;
+ ted_state_g.segment_list_refresh_interval = TIMER_RETRY_DELAY;
+ path_ted_register_vty();
+ path_ted_segment_list_refresh();
+}
+
+uint32_t path_ted_teardown(void)
+{
+ PATH_TED_DEBUG("%s : TED [%p]", __func__, ted_state_g.ted);
+ path_ted_unregister_vty();
+ path_ted_stop_importing_igp();
+ ls_ted_del_all(&ted_state_g.ted);
+ path_ted_timer_sync_cancel();
+ path_ted_timer_refresh_cancel();
+ return 0;
+}
+
+/**
+ * Set all needed to receive igp data.
+ *
+ * @return true if ok
+ *
+ */
+uint32_t path_ted_start_importing_igp(const char *daemon_str)
+{
+ uint32_t status = 0;
+
+ if (strcmp(daemon_str, "ospfv2") == 0)
+ ted_state_g.import = IMPORT_OSPFv2;
+ else if (strcmp(daemon_str, "ospfv3") == 0) {
+ ted_state_g.import = IMPORT_UNKNOWN;
+ return 1;
+ } else if (strcmp(daemon_str, "isis") == 0)
+ ted_state_g.import = IMPORT_ISIS;
+ else {
+ ted_state_g.import = IMPORT_UNKNOWN;
+ return 1;
+ }
+
+ if (ls_register(zclient, false /*client*/) != 0) {
+ PATH_TED_ERROR("%s: PATHD-TED: Unable to register Link State",
+ __func__);
+ ted_state_g.import = IMPORT_UNKNOWN;
+ status = 1;
+ } else {
+ if (path_ted_link_state_sync() != -1) {
+ PATH_TED_DEBUG("%s: PATHD-TED: Importing %s data ON",
+ __func__,
+ PATH_TED_IGP_PRINT(ted_state_g.import));
+ } else {
+ PATH_TED_WARN("%s: PATHD-TED: Importing %s data OFF",
+ __func__,
+ PATH_TED_IGP_PRINT(ted_state_g.import));
+ ted_state_g.import = IMPORT_UNKNOWN;
+ }
+ }
+ return status;
+}
+
+/**
+ * Unset all needed to receive igp data.
+ *
+ * @return true if ok
+ *
+ */
+uint32_t path_ted_stop_importing_igp(void)
+{
+ uint32_t status = 0;
+
+ if (ted_state_g.import != IMPORT_UNKNOWN) {
+ if (ls_unregister(zclient, false /*client*/) != 0) {
+ PATH_TED_ERROR(
+ "%s: PATHD-TED: Unable to unregister Link State",
+ __func__);
+ status = 1;
+ } else {
+ ted_state_g.import = IMPORT_UNKNOWN;
+ PATH_TED_DEBUG("%s: PATHD-TED: Importing igp data OFF",
+ __func__);
+ }
+ path_ted_timer_sync_cancel();
+ }
+ return status;
+}
+/**
+ * Check for ted status
+ *
+ * @return true if ok
+ *
+ */
+bool path_ted_is_initialized(void)
+{
+ if (ted_state_g.ted == NULL) {
+ PATH_TED_WARN("PATHD TED ls_ted not initialized");
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Creates an empty ted
+ *
+ * @param void
+ *
+ * @return Ptr to ted or NULL
+ */
+struct ls_ted *path_ted_create_ted(void)
+{
+ struct ls_ted *ted = ls_ted_new(TED_KEY, TED_NAME, TED_ASN);
+
+ if (ted == NULL) {
+ PATH_TED_ERROR("%s Unable to initialize TED Key [%d] ASN [%d] Name [%s]",
+ __func__, TED_KEY, TED_ASN, TED_NAME);
+ } else {
+ PATH_TED_INFO("%s Initialize TED Key [%d] ASN [%d] Name [%s]",
+ __func__, TED_KEY, TED_ASN, TED_NAME);
+ }
+
+ return ted;
+}
+
+uint32_t path_ted_rcvd_message(struct ls_message *msg)
+{
+ if (!path_ted_is_initialized())
+ return 1;
+
+ if (msg == NULL) {
+ PATH_TED_ERROR("%s: [rcv ted] TED received NULL message ",
+ __func__);
+ return 1;
+ }
+
+ if (path_ted_get_current_igp(msg->data.node->adv.origin))
+ return 1;
+
+ switch (msg->type) {
+ case LS_MSG_TYPE_NODE:
+ ls_msg2vertex(ted_state_g.ted, msg, true /*hard delete*/);
+ break;
+
+ case LS_MSG_TYPE_ATTRIBUTES:
+ ls_msg2edge(ted_state_g.ted, msg, true /*Ä¥ard delete*/);
+ break;
+
+ case LS_MSG_TYPE_PREFIX:
+ ls_msg2subnet(ted_state_g.ted, msg, true /*hard delete*/);
+ break;
+
+ default:
+ PATH_TED_DEBUG(
+ "%s: [rcv ted] TED received unknown message type [%d]",
+ __func__, msg->type);
+ break;
+ }
+ return 0;
+}
+
+uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote)
+{
+ uint32_t sid = MPLS_LABEL_NONE;
+ struct ls_edge *edge;
+ struct ls_edge_key key;
+
+ if (!path_ted_is_initialized())
+ return MPLS_LABEL_NONE;
+
+ if (!local || !remote)
+ return MPLS_LABEL_NONE;
+
+ switch (local->ipa_type) {
+ case IPADDR_V4:
+ /* We have local and remote ip */
+ /* so check all attributes in ted */
+ key.family = AF_INET;
+ IPV4_ADDR_COPY(&key.k.addr, &local->ip._v4_addr);
+ edge = ls_find_edge_by_key(ted_state_g.ted, key);
+ if (edge) {
+ if (edge->attributes->standard.remote.s_addr
+ == remote->ip._v4_addr.s_addr
+ && CHECK_FLAG(edge->attributes->flags,
+ LS_ATTR_ADJ_SID)) {
+ sid = edge->attributes->adj_sid[0]
+ .sid; /* from primary */
+ break;
+ }
+ }
+ break;
+ case IPADDR_V6:
+ key.family = AF_INET6;
+ IPV6_ADDR_COPY(&key.k.addr6, &local->ip._v6_addr);
+ edge = ls_find_edge_by_key(ted_state_g.ted, key);
+ if (edge) {
+ if ((0 == memcmp(&edge->attributes->standard.remote6,
+ &remote->ip._v6_addr,
+ sizeof(remote->ip._v6_addr)) &&
+ CHECK_FLAG(edge->attributes->flags,
+ LS_ATTR_ADJ_SID6))) {
+ sid = edge->attributes->adj_sid[ADJ_PRI_IPV6]
+ .sid; /* from primary */
+ break;
+ }
+ }
+ break;
+ case IPADDR_NONE:
+ break;
+ }
+
+ return sid;
+}
+
+uint32_t path_ted_query_type_c(struct prefix *prefix, uint8_t algo)
+{
+ uint32_t sid = MPLS_LABEL_NONE;
+ struct ls_subnet *subnet;
+
+ if (!path_ted_is_initialized())
+ return MPLS_LABEL_NONE;
+
+ if (!prefix)
+ return MPLS_LABEL_NONE;
+
+ switch (prefix->family) {
+ case AF_INET:
+ case AF_INET6:
+ subnet = ls_find_subnet(ted_state_g.ted, prefix);
+ if (subnet) {
+ if ((CHECK_FLAG(subnet->ls_pref->flags, LS_PREF_SR))
+ && (subnet->ls_pref->sr.algo == algo))
+ sid = subnet->ls_pref->sr.sid;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return sid;
+}
+
+uint32_t path_ted_query_type_e(struct prefix *prefix, uint32_t iface_id)
+{
+ uint32_t sid = MPLS_LABEL_NONE;
+ struct ls_subnet *subnet;
+ struct listnode *lst_node;
+ struct ls_edge *edge;
+
+ if (!path_ted_is_initialized())
+ return MPLS_LABEL_NONE;
+
+ if (!prefix)
+ return MPLS_LABEL_NONE;
+
+ switch (prefix->family) {
+ case AF_INET:
+ case AF_INET6:
+ subnet = ls_find_subnet(ted_state_g.ted, prefix);
+ if (subnet && subnet->vertex
+ && subnet->vertex->outgoing_edges) {
+ /* from the vertex linked in subnet */
+ /* loop over outgoing edges */
+ for (ALL_LIST_ELEMENTS_RO(
+ subnet->vertex->outgoing_edges, lst_node,
+ edge)) {
+ /* and look for ifaceid */
+ /* so get sid of attribute */
+ if (CHECK_FLAG(edge->attributes->flags,
+ LS_ATTR_LOCAL_ID)
+ && edge->attributes->standard.local_id
+ == iface_id) {
+ sid = subnet->ls_pref->sr.sid;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return sid;
+}
+
+DEFPY (debug_path_ted,
+ debug_path_ted_cmd,
+ "[no] debug pathd mpls-te",
+ NO_STR
+ DEBUG_STR
+ "path debugging\n"
+ "ted debugging\n")
+{
+ uint32_t mode = DEBUG_NODE2MODE(vty->node);
+ bool no_debug = (no != NULL);
+
+ DEBUG_MODE_SET(&ted_state_g.dbg, mode, !no);
+ DEBUG_FLAGS_SET(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC, !no_debug);
+ return CMD_SUCCESS;
+}
+
+/*
+ * Following are vty command functions.
+ */
+/* clang-format off */
+DEFUN (path_ted_on,
+ path_ted_on_cmd,
+ "mpls-te on",
+ NO_STR
+ "Enable the TE database (TED) functionality\n")
+/* clang-format on */
+{
+
+ if (ted_state_g.enabled) {
+ PATH_TED_DEBUG("%s: PATHD-TED: Enabled ON -> ON.", __func__);
+ return CMD_SUCCESS;
+ }
+
+ ted_state_g.ted = path_ted_create_ted();
+ ted_state_g.enabled = true;
+ PATH_TED_DEBUG("%s: PATHD-TED: Enabled OFF -> ON.", __func__);
+
+ return CMD_SUCCESS;
+}
+
+/* clang-format off */
+DEFUN (no_path_ted,
+ no_path_ted_cmd,
+ "no mpls-te [on]",
+ NO_STR
+ NO_STR
+ "Disable the TE Database functionality\n")
+/* clang-format on */
+{
+ if (!ted_state_g.enabled) {
+ PATH_TED_DEBUG("%s: PATHD-TED: OFF -> OFF", __func__);
+ return CMD_SUCCESS;
+ }
+
+ /* Remove TED */
+ ls_ted_del_all(&ted_state_g.ted);
+ ted_state_g.enabled = false;
+ PATH_TED_DEBUG("%s: PATHD-TED: ON -> OFF", __func__);
+ ted_state_g.import = IMPORT_UNKNOWN;
+ if (ls_unregister(zclient, false /*client*/) != 0) {
+ vty_out(vty, "Unable to unregister Link State\n");
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* clang-format off */
+DEFPY(path_ted_import,
+ path_ted_import_cmd,
+ "mpls-te import <ospfv2|ospfv3|isis>$import_daemon",
+ "Enable the TE database (TED) fill with remote igp data\n"
+ "import\n"
+ "Origin ospfv2\n"
+ "Origin ospfv3\n"
+ "Origin isis\n")
+/* clang-format on */
+{
+
+ if (ted_state_g.enabled)
+ if (path_ted_start_importing_igp(import_daemon)) {
+ vty_out(vty, "Unable to start importing\n");
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+/* clang-format off */
+DEFUN (no_path_ted_import,
+ no_path_ted_import_cmd,
+ "no mpls-te import",
+ NO_STR
+ NO_STR
+ "Disable the TE Database fill with remote igp data\n")
+/* clang-format on */
+{
+
+ if (ted_state_g.import) {
+ if (path_ted_stop_importing_igp()) {
+ vty_out(vty, "Unable to stop importing\n");
+ return CMD_WARNING;
+ } else {
+ PATH_TED_DEBUG(
+ "%s: PATHD-TED: Importing igp data already OFF",
+ __func__);
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+/* clang-format off */
+DEFPY (show_pathd_ted_db,
+ show_pathd_ted_db_cmd,
+ "show pathd ted database <verbose|json>$ver_json ",
+ "show command\n"
+ "pathd daemon\n"
+ "traffic eng\n"
+ "database\n"
+ "verbose output\n"
+ "Show complete received TED database\n")
+/* clang-format on */
+{
+ bool st_json = false;
+ json_object *json = NULL;
+
+ if (!ted_state_g.enabled) {
+ vty_out(vty, "Traffic Engineering database is not enabled\n");
+ return CMD_WARNING;
+ }
+ if (strcmp(ver_json, "json") == 0) {
+ st_json = true;
+ json = json_object_new_object();
+ }
+ /* Show the complete TED */
+ ls_show_ted(ted_state_g.ted, vty, json, !st_json);
+ if (st_json)
+ vty_json(vty, json);
+ return CMD_SUCCESS;
+}
+
+/*
+ * Config Write functions
+ */
+
+int path_ted_cli_debug_config_write(struct vty *vty)
+{
+ if (DEBUG_MODE_CHECK(&ted_state_g.dbg, DEBUG_MODE_CONF)) {
+ if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC))
+ vty_out(vty, "debug pathd mpls-te\n");
+ return 1;
+ }
+ return 0;
+}
+
+void path_ted_show_debugging(struct vty *vty)
+{
+ if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC))
+ vty_out(vty, " Path TED debugging is on\n");
+}
+
+int path_ted_cli_debug_set_all(uint32_t flags, bool set)
+{
+ DEBUG_FLAGS_SET(&ted_state_g.dbg, flags, set);
+
+ /* If all modes have been turned off, don't preserve options. */
+ if (!DEBUG_MODE_CHECK(&ted_state_g.dbg, DEBUG_MODE_ALL))
+ DEBUG_CLEAR(&ted_state_g.dbg);
+
+ return 0;
+}
+
+/**
+ * Help fn to show ted related configuration
+ *
+ * @param vty
+ *
+ * @return Status
+ */
+uint32_t path_ted_config_write(struct vty *vty)
+{
+
+ if (ted_state_g.enabled) {
+ vty_out(vty, " mpls-te on\n");
+ switch (ted_state_g.import) {
+ case IMPORT_ISIS:
+ vty_out(vty, " mpls-te import isis\n");
+ break;
+ case IMPORT_OSPFv2:
+ vty_out(vty, " mpls-te import ospfv2\n");
+ break;
+ case IMPORT_OSPFv3:
+ vty_out(vty, " mpls-te import ospfv3\n");
+ break;
+ case IMPORT_UNKNOWN:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Register the fn's for CLI and hook for config show
+ *
+ * @param void
+ *
+ */
+static void path_ted_register_vty(void)
+{
+ install_element(VIEW_NODE, &show_pathd_ted_db_cmd);
+ install_element(SR_TRAFFIC_ENG_NODE, &path_ted_on_cmd);
+ install_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_cmd);
+ install_element(SR_TRAFFIC_ENG_NODE, &path_ted_import_cmd);
+ install_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_import_cmd);
+
+ install_element(CONFIG_NODE, &debug_path_ted_cmd);
+ install_element(ENABLE_NODE, &debug_path_ted_cmd);
+
+ hook_register(nb_client_debug_config_write,
+ path_ted_cli_debug_config_write);
+ hook_register(nb_client_debug_set_all, path_ted_cli_debug_set_all);
+}
+
+/**
+ * UnRegister the fn's for CLI and hook for config show
+ *
+ * @param void
+ *
+ */
+static void path_ted_unregister_vty(void)
+{
+ uninstall_element(VIEW_NODE, &show_pathd_ted_db_cmd);
+ uninstall_element(SR_TRAFFIC_ENG_NODE, &path_ted_on_cmd);
+ uninstall_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_cmd);
+ uninstall_element(SR_TRAFFIC_ENG_NODE, &path_ted_import_cmd);
+ uninstall_element(SR_TRAFFIC_ENG_NODE, &no_path_ted_import_cmd);
+}
+
+/**
+ * Ask igp for a complete TED so far
+ *
+ * @param void
+ *
+ * @return zclient status
+ */
+enum zclient_send_status path_ted_link_state_sync(void)
+{
+ enum zclient_send_status status;
+
+ status = ls_request_sync(zclient);
+ if (status == -1) {
+ PATH_TED_ERROR(
+ "%s: PATHD-TED: Opaque error asking for TED sync ",
+ __func__);
+ return status;
+ } else {
+ PATH_TED_DEBUG("%s: PATHD-TED: Opaque asked for TED sync ",
+ __func__);
+ }
+ event_add_timer(ted_state_g.main, path_ted_timer_handler_sync,
+ &ted_state_g, ted_state_g.link_state_delay_interval,
+ &ted_state_g.t_link_state_sync);
+
+ return status;
+}
+
+/**
+ * Timer cb for check link state sync
+ *
+ * @param thread Current thread
+ *
+ * @return status
+ */
+void path_ted_timer_handler_sync(struct event *thread)
+{
+ /* data unpacking */
+ struct ted_state *data = EVENT_ARG(thread);
+
+ assert(data != NULL);
+ /* Retry the sync */
+ path_ted_link_state_sync();
+}
+
+/**
+ * refresg segment list and create timer to keep up updated
+ *
+ * @param void
+ *
+ * @return status
+ */
+int path_ted_segment_list_refresh(void)
+{
+ int status = 0;
+
+ path_ted_timer_refresh_cancel();
+ event_add_timer(ted_state_g.main, path_ted_timer_handler_refresh,
+ &ted_state_g, ted_state_g.segment_list_refresh_interval,
+ &ted_state_g.t_segment_list_refresh);
+
+ return status;
+}
+
+/**
+ * Timer cb for refreshing sid in segment lists
+ *
+ * @param void
+ *
+ * @return status
+ */
+void path_ted_timer_handler_refresh(struct event *thread)
+{
+ if (!path_ted_is_initialized())
+ return;
+
+ PATH_TED_DEBUG("%s: PATHD-TED: Refresh sid from current TED", __func__);
+ /* data unpacking */
+ struct ted_state *data = EVENT_ARG(thread);
+
+ assert(data != NULL);
+
+ srte_policy_update_ted_sid();
+}
+
+/**
+ * Cancel sync timer
+ *
+ * @param void
+ *
+ * @return void status
+ */
+void path_ted_timer_sync_cancel(void)
+{
+ if (ted_state_g.t_link_state_sync != NULL) {
+ event_cancel(&ted_state_g.t_link_state_sync);
+ ted_state_g.t_link_state_sync = NULL;
+ }
+}
+
+/**
+ * Cancel refresh timer
+ *
+ * @param void
+ *
+ * @return void status
+ */
+void path_ted_timer_refresh_cancel(void)
+{
+ if (ted_state_g.t_segment_list_refresh != NULL) {
+ event_cancel(&ted_state_g.t_segment_list_refresh);
+ ted_state_g.t_segment_list_refresh = NULL;
+ }
+}
+
+/**
+ * Check which igp is configured
+ *
+ * @param igp who want to check against config-
+ *
+ * @return status
+ */
+uint32_t path_ted_get_current_igp(uint32_t igp)
+{
+ switch (igp) {
+ case ISIS_L1:
+ case ISIS_L2:
+ if (ted_state_g.import != IMPORT_ISIS) {
+ PATH_TED_ERROR(
+ "%s: [rcv ted] Incorrect igp origin wait (%s) got (%s) ",
+ __func__,
+ PATH_TED_IGP_PRINT(ted_state_g.import),
+ LS_IGP_PRINT(igp));
+ return 1;
+ }
+ break;
+ case OSPFv2:
+ if (ted_state_g.import != IMPORT_OSPFv2) {
+ PATH_TED_ERROR(
+ "%s: [rcv ted] Incorrect igp origin wait (%s) got (%s) ",
+ __func__,
+ PATH_TED_IGP_PRINT(ted_state_g.import),
+ LS_IGP_PRINT(igp));
+ return 1;
+ }
+ break;
+ case STATIC:
+ break;
+ }
+ return 0;
+}
diff --git a/pathd/path_ted.h b/pathd/path_ted.h
new file mode 100644
index 0000000..a1bc784
--- /dev/null
+++ b/pathd/path_ted.h
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 Volta Networks, Inc
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _PATH_TED_H
+#define _PATH_TED_H
+
+#ifdef __cplusplus
+
+extern "C" {
+#endif
+
+#include <zebra.h>
+
+#include <stdbool.h>
+
+#include <debug.h>
+#include "linklist.h"
+#include "log.h"
+#include "command.h"
+#include "stream.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "link_state.h"
+
+extern struct ted_state ted_state_g;
+#define TIMER_RETRY_DELAY 5 /* Timeout in seconds between ls sync request */
+#define TED_KEY 1
+#define TED_ASN 1
+#define TED_NAME "PATHD TED"
+
+enum igp_import {
+ IMPORT_UNKNOWN = 0,
+ IMPORT_ISIS,
+ IMPORT_OSPFv2,
+ IMPORT_OSPFv3
+};
+struct ted_state {
+ struct event_loop *main;
+ /* Status of TED: enable or disable */
+ bool enabled;
+ /* From which igp is going to receive data */
+ enum igp_import import;
+ /* The TED itself as in link_state.h */
+ struct ls_ted *ted;
+ /* Timer for ted sync */
+ struct event *t_link_state_sync;
+ /* Timer for refresh sid in segment list */
+ struct event *t_segment_list_refresh;
+ /* delay interval in seconds */
+ uint32_t link_state_delay_interval;
+ /* delay interval refresh in seconds */
+ uint32_t segment_list_refresh_interval;
+ struct debug dbg;
+};
+/* Debug flags. */
+#define PATH_TED_DEBUG_BASIC 0x01
+#define PATH_TED_DEBUG(fmt, ...) \
+ do { \
+ if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \
+ DEBUGD(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define PATH_TED_ERROR(fmt, ...) \
+ do { \
+ if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \
+ DEBUGE(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \
+ } while (0)
+#define PATH_TED_WARN(fmt, ...) \
+ do { \
+ if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \
+ DEBUGW(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \
+ } while (0)
+#define PATH_TED_INFO(fmt, ...) \
+ do { \
+ if (DEBUG_FLAGS_CHECK(&ted_state_g.dbg, PATH_TED_DEBUG_BASIC)) \
+ DEBUGI(&ted_state_g.dbg, "mpls-te: " fmt, ##__VA_ARGS__); \
+ } while (0)
+
+/* TED management functions */
+bool path_ted_is_initialized(void);
+void path_ted_init(struct event_loop *master);
+uint32_t path_ted_teardown(void);
+void path_ted_timer_sync_cancel(void);
+void path_ted_timer_refresh_cancel(void);
+int path_ted_segment_list_refresh(void);
+
+/* TED configuration functions */
+uint32_t path_ted_config_write(struct vty *vty);
+void path_ted_show_debugging(struct vty *vty);
+
+/* TED util functions */
+/* clang-format off */
+#define LS_MSG_EVENT_PRINT(event) event == LS_MSG_EVENT_ADD?"add"\
+ : event == LS_MSG_EVENT_DELETE?"del"\
+ : event == LS_MSG_EVENT_UPDATE?"upd"\
+ : event == LS_MSG_EVENT_SYNC?"syn"\
+ : event == LS_MSG_EVENT_SYNC?"und" : "none"
+#define LS_MSG_TYPE_PRINT(type) type == LS_MSG_TYPE_NODE?"node"\
+ : type == LS_MSG_TYPE_ATTRIBUTES?"att"\
+ : type == LS_MSG_TYPE_PREFIX?"pre" : "none"
+#define LS_IGP_PRINT(type) type == ISIS_L1?"ISIS_L1"\
+ : type == ISIS_L2?"ISIS_L2"\
+ : type == DIRECT?"DIRECT"\
+ : type == STATIC?"STATIC"\
+ : type == OSPFv2?"OSPFv2" : "none"
+#define PATH_TED_IGP_PRINT(type) type == IMPORT_OSPFv2?"OSPFv2"\
+ : type == IMPORT_OSPFv3?"OSPFv3"\
+ : type == IMPORT_ISIS?"ISIS" : "none"
+/* clang-format on */
+
+
+uint32_t path_ted_get_current_igp(uint32_t);
+/* TED Query functions */
+
+/*
+ * Type of queries from draft-ietf-spring-segment-routing-policy-07 for types
+ * f,c,e
+ */
+
+/**
+ * Search for sid based in prefix and optional algo
+ *
+ * @param prefix Net prefix to resolv
+ * @param algo Algorithm for link state
+ *
+ * @return sid of attribute
+ */
+uint32_t path_ted_query_type_c(struct prefix *prefix, uint8_t algo);
+
+/**
+ * Search for sid based in prefix and interface id
+ *
+ * @param prefix Net prefix to resolv
+ * @param iface_id The interface id
+ *
+ * @return sid of attribute
+ */
+uint32_t path_ted_query_type_e(struct prefix *prefix, uint32_t iface_id);
+
+/**
+ * Search for sid based in local, remote pair
+ *
+ * @param local local ip of attribute
+ * @param remote remote ip of attribute
+ *
+ * @return sid of attribute
+ */
+uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote);
+
+
+/**
+ * Handle the received opaque msg
+ *
+ * @param msg Holds the ted data
+ *
+ * @return sid of attribute
+ */
+uint32_t path_ted_rcvd_message(struct ls_message *msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PATH_TED_H */
diff --git a/pathd/path_zebra.c b/pathd/path_zebra.c
new file mode 100644
index 0000000..826443f
--- /dev/null
+++ b/pathd/path_zebra.c
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include "frrevent.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_ted.h"
+#include "pathd/path_zebra.h"
+#include "lib/command.h"
+#include "lib/link_state.h"
+
+static int path_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS);
+
+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);
+ }
+}
+
+static int path_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
+{
+ int ret = 0;
+ struct stream *s;
+ struct zapi_opaque_msg info;
+
+ s = zclient->ibuf;
+
+ if (zclient_opaque_decode(s, &info) != 0)
+ return -1;
+
+ switch (info.type) {
+ case LINK_STATE_UPDATE:
+ case LINK_STATE_SYNC:
+ /* Start receiving ls data so cancel request sync timer */
+ path_ted_timer_sync_cancel();
+
+ struct ls_message *msg = ls_parse_msg(s);
+
+ if (msg) {
+ zlog_debug("%s: [rcv ted] ls (%s) msg (%s)-(%s) !",
+ __func__,
+ info.type == LINK_STATE_UPDATE
+ ? "LINK_STATE_UPDATE"
+ : "LINK_STATE_SYNC",
+ LS_MSG_TYPE_PRINT(msg->type),
+ LS_MSG_EVENT_PRINT(msg->event));
+ } else {
+ zlog_err(
+ "%s: [rcv ted] Could not parse LinkState stream message.",
+ __func__);
+ return -1;
+ }
+
+ ret = path_ted_rcvd_message(msg);
+ ls_delete_msg(msg);
+ /* Update local configuration after process update. */
+ path_ted_segment_list_refresh();
+ break;
+ default:
+ zlog_debug("%s: [rcv ted] unknown opaque event (%d) !",
+ __func__, info.type);
+ break;
+ }
+
+ return ret;
+}
+
+static zclient_handler *const path_handlers[] = {
+ [ZEBRA_SR_POLICY_NOTIFY_STATUS] = path_zebra_sr_policy_notify_status,
+ [ZEBRA_ROUTER_ID_UPDATE] = path_zebra_router_id_update,
+ [ZEBRA_OPAQUE_MESSAGE] = path_zebra_opaque_msg_handler,
+};
+
+/**
+ * Initializes Zebra asynchronous connection.
+ *
+ * @param master The master thread
+ */
+void path_zebra_init(struct event_loop *master)
+{
+ struct zclient_options options = zclient_options_default;
+ options.synchronous = true;
+
+ /* Initialize asynchronous zclient. */
+ zclient = zclient_new(master, &zclient_options_default, path_handlers,
+ array_size(path_handlers));
+ zclient_init(zclient, ZEBRA_ROUTE_SRTE, 0, &pathd_privs);
+ zclient->zebra_connected = path_zebra_connected;
+
+ /* Initialize special zclient for synchronous message exchanges. */
+ zclient_sync = zclient_new(master, &options, NULL, 0);
+ 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();
+}
+
+void path_zebra_stop(void)
+{
+ zclient_stop(zclient);
+ zclient_free(zclient);
+ zclient_stop(zclient_sync);
+ zclient_free(zclient_sync);
+}
diff --git a/pathd/path_zebra.h b/pathd/path_zebra.h
new file mode 100644
index 0000000..74a62e3
--- /dev/null
+++ b/pathd/path_zebra.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#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 event_loop *master);
+void path_zebra_stop(void);
+
+#endif /* _FRR_PATH_MPLS_H_ */
diff --git a/pathd/pathd.c b/pathd/pathd.c
new file mode 100644
index 0000000..6c13503
--- /dev/null
+++ b/pathd/pathd.c
@@ -0,0 +1,1476 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "log.h"
+#include "lib_errors.h"
+#include "network.h"
+#include "libfrr.h"
+#include <debug.h>
+#include <hook.h>
+
+#include "pathd/pathd.h"
+#include "pathd/path_zebra.h"
+#include "pathd/path_debug.h"
+#include "pathd/path_ted.h"
+
+#define HOOK_DELAY 3
+
+DEFINE_MGROUP(PATHD, "pathd");
+
+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));
+
+struct debug path_policy_debug;
+
+#define PATH_POLICY_DEBUG(fmt, ...) \
+ do { \
+ if (DEBUG_FLAGS_CHECK(&path_policy_debug, \
+ PATH_POLICY_DEBUG_BASIC)) \
+ DEBUGD(&path_policy_debug, "policy: " fmt, \
+ ##__VA_ARGS__); \
+ } while (0)
+
+
+static void trigger_pathd_candidate_created(struct srte_candidate *candidate);
+static void trigger_pathd_candidate_created_timer(struct event *thread);
+static void trigger_pathd_candidate_updated(struct srte_candidate *candidate);
+static void trigger_pathd_candidate_updated_timer(struct event *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);
+
+static void srte_policy_status_log(struct srte_policy *policy)
+{
+ char endpoint[ENDPOINT_STR_LENGTH];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ if (policy->status == SRTE_POLICY_STATUS_DOWN) {
+ PATH_POLICY_DEBUG("SR-TE(%s, %u): policy is DOWN", endpoint,
+ policy->color);
+ } else if (policy->status == SRTE_POLICY_STATUS_UP) {
+ PATH_POLICY_DEBUG("SR-TE(%s, %u): policy is UP", endpoint,
+ policy->color);
+ }
+}
+
+/**
+ * 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
+ */
+int 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,
+ uint8_t algo, uint8_t pref_len)
+{
+
+ int32_t status = 0;
+ struct prefix pre = {0};
+
+ if (!segment || !local_ip || !remote_ip)
+ return 1;
+
+ 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));
+ status = srte_ted_do_query_type_f(segment, local_ip, remote_ip);
+ 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;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM:
+ pre.family = AF_INET6;
+ pre.prefixlen = pref_len;
+ pre.u.prefix6 = local_ip->ip._v6_addr;
+ segment->nai_local_prefix_len = pref_len;
+ segment->nai_algorithm = algo;
+ status = srte_ted_do_query_type_c(segment, &pre, algo);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM:
+ pre.family = AF_INET;
+ pre.prefixlen = pref_len;
+ pre.u.prefix4 = local_ip->ip._v4_addr;
+ segment->nai_local_prefix_len = pref_len;
+ segment->nai_algorithm = algo;
+ status = srte_ted_do_query_type_c(segment, &pre, algo);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE:
+ pre.family = AF_INET6;
+ pre.prefixlen = pref_len;
+ pre.u.prefix6 = local_ip->ip._v6_addr;
+ segment->nai_local_prefix_len = pref_len;
+ segment->nai_local_iface = local_iface;
+ status = srte_ted_do_query_type_e(segment, &pre, local_iface);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE:
+ pre.family = AF_INET;
+ pre.prefixlen = pref_len;
+ pre.u.prefix4 = local_ip->ip._v4_addr;
+ segment->nai_local_prefix_len = pref_len;
+ segment->nai_local_iface = local_iface;
+ status = srte_ted_do_query_type_e(segment, &pre, local_iface);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_NONE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY_LINK_LOCAL_ADDRESSES:
+ 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 status;
+}
+
+/**
+ * Mark segment as modified depending in protocol and sid conditions
+ *
+ * @param protocol_origin Origin of the segment list
+ * @param s_list Ptr to segment list with flags,sid to modidy
+ * @param s_entry Ptr to segment entry with sid to modidy
+ * @param ted_sid The sid from ted query
+ * @return void
+ */
+void srte_segment_set_local_modification(struct srte_segment_list *s_list,
+ struct srte_segment_entry *s_entry,
+ uint32_t ted_sid)
+{
+ if (!s_list || !s_entry)
+ return;
+
+ if (s_list->protocol_origin == SRTE_ORIGIN_LOCAL
+ && s_entry->sid_value != ted_sid) {
+ s_entry->sid_value = ted_sid;
+ SET_FLAG(s_list->flags, F_SEGMENT_LIST_MODIFIED);
+ }
+}
+
+/**
+ * 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,
+ enum srte_protocol_origin origin,
+ const char *originator)
+{
+ struct srte_policy *policy;
+
+ policy = XCALLOC(MTYPE_PATH_SR_POLICY, sizeof(*policy));
+ policy->color = color;
+ policy->endpoint = *endpoint;
+ policy->binding_sid = MPLS_LABEL_NONE;
+ policy->protocol_origin = origin;
+ if (originator != NULL)
+ strlcpy(policy->originator, originator,
+ sizeof(policy->originator));
+
+ 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);
+}
+
+/*
+ * After new data from igp,local and pce the segment list :
+ * Mark as invalid for origin pce if cannot be validated
+ * Updated for origin local
+ */
+int srte_policy_update_ted_sid(void)
+{
+
+ int number_of_sid_clashed = 0;
+ struct srte_segment_list *s_list;
+ struct srte_segment_entry *s_entry;
+
+ if (!path_ted_is_initialized())
+ return 0;
+ if (RB_EMPTY(srte_segment_list_head, &srte_segment_lists))
+ return 0;
+
+ RB_FOREACH (s_list, srte_segment_list_head, &srte_segment_lists) {
+ if (CHECK_FLAG(s_list->flags, F_SEGMENT_LIST_DELETED))
+ continue;
+ RB_FOREACH (s_entry, srte_segment_entry_head,
+ &s_list->segments) {
+ PATH_TED_DEBUG(
+ "%s:PATHD-TED: SL: Name: %s index:(%d) sid:(%d) prefix_len:(%d) local iface:(%d) algorithm:(%d)",
+ __func__, s_list->name, s_entry->index,
+ s_entry->sid_value,
+ s_entry->nai_local_prefix_len,
+ s_entry->nai_local_iface,
+ s_entry->nai_algorithm);
+ struct prefix prefix_cli = {0};
+
+ switch (s_entry->nai_type) {
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY:
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ADJACENCY:
+ number_of_sid_clashed +=
+ srte_ted_do_query_type_f(
+ s_entry,
+ &s_entry->nai_local_addr,
+ &s_entry->nai_remote_addr);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE:
+ prefix_cli.family = AF_INET6;
+ prefix_cli.prefixlen =
+ s_entry->nai_local_prefix_len;
+ prefix_cli.u.prefix6 =
+ s_entry->nai_local_addr.ip._v6_addr;
+ number_of_sid_clashed +=
+ srte_ted_do_query_type_e(
+ s_entry, &prefix_cli,
+ s_entry->nai_local_iface);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE:
+ prefix_cli.family = AF_INET;
+ prefix_cli.prefixlen =
+ s_entry->nai_local_prefix_len;
+ prefix_cli.u.prefix4 =
+ s_entry->nai_local_addr.ip._v4_addr;
+ number_of_sid_clashed +=
+ srte_ted_do_query_type_e(
+ s_entry, &prefix_cli,
+ s_entry->nai_local_iface);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM:
+ prefix_cli.family = AF_INET6;
+ prefix_cli.prefixlen =
+ s_entry->nai_local_prefix_len;
+ prefix_cli.u.prefix6 =
+ s_entry->nai_local_addr.ip._v6_addr;
+ number_of_sid_clashed +=
+ srte_ted_do_query_type_c(
+ s_entry, &prefix_cli,
+ s_entry->nai_algorithm);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM:
+ prefix_cli.family = AF_INET;
+ prefix_cli.prefixlen =
+ s_entry->nai_local_prefix_len;
+ prefix_cli.u.prefix4 =
+ s_entry->nai_local_addr.ip._v4_addr;
+ number_of_sid_clashed +=
+ srte_ted_do_query_type_c(
+ s_entry, &prefix_cli,
+ s_entry->nai_algorithm);
+ break;
+ case SRTE_SEGMENT_NAI_TYPE_NONE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_NODE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_NODE:
+ case SRTE_SEGMENT_NAI_TYPE_IPV4_UNNUMBERED_ADJACENCY:
+ case SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY_LINK_LOCAL_ADDRESSES:
+ break;
+ }
+ }
+ if (number_of_sid_clashed) {
+ SET_FLAG(s_list->flags, F_SEGMENT_LIST_SID_CONFLICT);
+ number_of_sid_clashed = 0;
+ } else
+ UNSET_FLAG(s_list->flags, F_SEGMENT_LIST_SID_CONFLICT);
+ }
+ srte_apply_changes();
+
+ return 0;
+}
+
+/**
+ * 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
+ && (!CHECK_FLAG(candidate->lsp->segment_list->flags,
+ F_SEGMENT_LIST_SID_CONFLICT)))
+ return candidate;
+ }
+
+ return NULL;
+}
+
+void srte_clean_zebra(void)
+{
+ struct srte_policy *policy, *safe_pol;
+
+ RB_FOREACH_SAFE (policy, srte_policy_head, &srte_policies, safe_pol)
+ srte_policy_del(policy);
+
+ path_zebra_stop();
+}
+
+/**
+ * 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)) {
+ if (policy->status != SRTE_POLICY_STATUS_DOWN) {
+ policy->status = SRTE_POLICY_STATUS_DOWN;
+ srte_policy_status_log(policy);
+ }
+ 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[ENDPOINT_STR_LENGTH];
+
+ 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) {
+ PATH_POLICY_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) {
+ PATH_POLICY_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,
+ enum srte_protocol_origin origin,
+ const char *originator)
+{
+ 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();
+ candidate->protocol_origin = origin;
+ if (originator != NULL) {
+ strlcpy(candidate->originator, originator,
+ sizeof(candidate->originator));
+ lsp->protocol_origin = origin;
+ }
+
+ if (candidate->protocol_origin == SRTE_ORIGIN_PCEP
+ || candidate->protocol_origin == SRTE_ORIGIN_BGP) {
+ candidate->type = SRTE_CANDIDATE_TYPE_DYNAMIC;
+ }
+ 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[ENDPOINT_STR_LENGTH];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ 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);
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ 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;
+ PATH_POLICY_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;
+ case AFFINITY_FILTER_UNDEFINED:
+ return 0;
+ }
+
+ assert(!"Reached end of function we should never hit");
+}
+
+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";
+ case AFFINITY_FILTER_UNDEFINED:
+ return "unknown";
+ }
+
+ assert(!"Reached end of function we should never hit");
+}
+
+/**
+ * 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[ENDPOINT_STR_LENGTH];
+
+ 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;
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ 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;
+ PATH_POLICY_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[ENDPOINT_STR_LENGTH];
+
+ ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint));
+ PATH_POLICY_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;
+ case SRTE_POLICY_STATUS_UNKNOWN:
+ case SRTE_POLICY_STATUS_UP:
+ case SRTE_POLICY_STATUS_GOING_DOWN:
+ policy->status = SRTE_POLICY_STATUS_DOWN;
+ srte_policy_status_log(policy);
+ break;
+ }
+ break;
+ case ZEBRA_SR_POLICY_UP:
+ switch (policy->status) {
+ case SRTE_POLICY_STATUS_UP:
+ return;
+ case SRTE_POLICY_STATUS_UNKNOWN:
+ case SRTE_POLICY_STATUS_DOWN:
+ case SRTE_POLICY_STATUS_GOING_DOWN:
+ case SRTE_POLICY_STATUS_GOING_UP:
+ policy->status = SRTE_POLICY_STATUS_UP;
+ srte_policy_status_log(policy);
+ 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;
+ }
+
+ PATH_POLICY_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) {
+ PATH_POLICY_DEBUG("Unset segment lists checking policy %s",
+ policy->name);
+ struct srte_candidate *candidate;
+ RB_FOREACH (candidate, srte_candidate_head,
+ &policy->candidate_paths) {
+ PATH_POLICY_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) {
+ PATH_POLICY_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";
+ case SRTE_ORIGIN_UNDEFINED:
+ return "Unknown";
+ }
+
+ assert(!"Reached end of function we should never hit");
+}
+
+void path_policy_show_debugging(struct vty *vty)
+{
+ if (DEBUG_FLAGS_CHECK(&path_policy_debug, PATH_POLICY_DEBUG_BASIC))
+ vty_out(vty, " Path policy debugging is on\n");
+}
+
+void pathd_shutdown(void)
+{
+ path_ted_teardown();
+ srte_clean_zebra();
+ frr_fini();
+}
+
+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;
+ event_add_timer(master, trigger_pathd_candidate_created_timer,
+ (void *)candidate, HOOK_DELAY, &candidate->hook_timer);
+}
+
+void trigger_pathd_candidate_created_timer(struct event *thread)
+{
+ struct srte_candidate *candidate = EVENT_ARG(thread);
+ candidate->hook_timer = NULL;
+ 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;
+ event_add_timer(master, trigger_pathd_candidate_updated_timer,
+ (void *)candidate, HOOK_DELAY, &candidate->hook_timer);
+}
+
+void trigger_pathd_candidate_updated_timer(struct event *thread)
+{
+ struct srte_candidate *candidate = EVENT_ARG(thread);
+ candidate->hook_timer = NULL;
+ 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) {
+ event_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";
+ }
+}
+
+int32_t srte_ted_do_query_type_c(struct srte_segment_entry *entry,
+ struct prefix *prefix_cli, uint32_t algo)
+{
+ int32_t status = 0;
+ uint32_t ted_sid = MPLS_LABEL_NONE;
+
+ if (!entry || !prefix_cli)
+ return 0;
+
+ if (!path_ted_is_initialized())
+ return 0;
+
+ ted_sid = path_ted_query_type_c(prefix_cli, algo);
+ if (ted_sid == MPLS_LABEL_NONE) {
+ zlog_warn(" %s: PATHD-TED: SL: ERROR query C : ted-sid (%d)",
+ __func__, ted_sid);
+ } else {
+ PATH_TED_DEBUG(
+ "%s: PATHD-TED: SL: Success query C : ted-sid (%d)",
+ __func__, ted_sid);
+ }
+ if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid,
+ entry->sid_value)) {
+ status = PATH_SID_ERROR;
+ } else
+ srte_segment_set_local_modification(entry->segment_list, entry,
+ ted_sid);
+ return status;
+}
+
+int32_t srte_ted_do_query_type_e(struct srte_segment_entry *entry,
+ struct prefix *prefix_cli,
+ uint32_t local_iface)
+{
+ int32_t status = 0;
+ uint32_t ted_sid = MPLS_LABEL_NONE;
+
+ if (!entry || !prefix_cli)
+ return 0;
+
+ if (!path_ted_is_initialized())
+ return 0;
+
+ ted_sid = path_ted_query_type_e(prefix_cli, local_iface);
+ if (ted_sid == MPLS_LABEL_NONE) {
+ zlog_warn(" %s: PATHD-TED: SL: ERROR query E : ted-sid (%d)",
+ __func__, ted_sid);
+ } else {
+ PATH_TED_DEBUG(
+ "%s: PATHD-TED: SL: Success query E : ted-sid (%d)",
+ __func__, ted_sid);
+ }
+ if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid,
+ entry->sid_value)) {
+ status = PATH_SID_ERROR;
+ } else
+ srte_segment_set_local_modification(entry->segment_list, entry,
+ ted_sid);
+ return status;
+}
+
+int32_t srte_ted_do_query_type_f(struct srte_segment_entry *entry,
+ struct ipaddr *local, struct ipaddr *remote)
+{
+ int32_t status = 0;
+ uint32_t ted_sid = MPLS_LABEL_NONE;
+
+ if (!entry || !local || !remote)
+ return 0;
+
+ if (!path_ted_is_initialized())
+ return status;
+
+ ted_sid = path_ted_query_type_f(local, remote);
+ if (ted_sid == MPLS_LABEL_NONE) {
+ zlog_warn("%s:SL: ERROR query F : ted-sid (%d)", __func__,
+ ted_sid);
+ } else {
+ PATH_TED_DEBUG("%s:SL: Success query F : ted-sid (%d)",
+ __func__, ted_sid);
+ }
+ if (CHECK_SID(entry->segment_list->protocol_origin, ted_sid,
+ entry->sid_value)) {
+ status = PATH_SID_ERROR;
+ } else
+ srte_segment_set_local_modification(entry->segment_list, entry,
+ ted_sid);
+ return status;
+}
diff --git a/pathd/pathd.h b/pathd/pathd.h
new file mode 100644
index 0000000..73ad492
--- /dev/null
+++ b/pathd/pathd.h
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ */
+
+#ifndef _FRR_PATHD_H_
+#define _FRR_PATHD_H_
+
+#include "lib/memory.h"
+#include "lib/mpls.h"
+#include "lib/ipaddr.h"
+#include "lib/srte.h"
+#include "lib/hook.h"
+#include "lib/prefix.h"
+
+#define PATH_SID_ERROR 1
+#define PATH_SID_NO_ERROR 0
+#define CHECK_SID(or, ts, es) \
+ ((or == SRTE_ORIGIN_PCEP && (ts == MPLS_LABEL_NONE || es != ts)) \
+ || (or == SRTE_ORIGIN_LOCAL && ts == MPLS_LABEL_NONE))
+
+DECLARE_MGROUP(PATHD);
+
+DECLARE_HOOK(pathd_srte_config_write, (struct vty *vty), (vty));
+
+enum srte_protocol_origin {
+ SRTE_ORIGIN_UNDEFINED = 0,
+ SRTE_ORIGIN_PCEP = 1,
+ SRTE_ORIGIN_BGP = 2,
+ SRTE_ORIGIN_LOCAL = 3,
+};
+
+extern struct debug path_policy_debug;
+
+#define PATH_POLICY_DEBUG_BASIC 0x01
+
+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,
+ SRTE_SEGMENT_NAI_TYPE_IPV6_ADJACENCY_LINK_LOCAL_ADDRESSES = 6,
+ SRTE_SEGMENT_NAI_TYPE_IPV4_LOCAL_IFACE = 7,
+ SRTE_SEGMENT_NAI_TYPE_IPV6_LOCAL_IFACE = 8,
+ SRTE_SEGMENT_NAI_TYPE_IPV4_ALGORITHM = 9,
+ SRTE_SEGMENT_NAI_TYPE_IPV6_ALGORITHM = 10
+};
+
+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;
+ /* Support draft-ietf-spring-segment-routing-policy sl types queries*/
+ uint8_t nai_local_prefix_len;
+ uint8_t nai_algorithm;
+};
+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
+#define F_SEGMENT_LIST_SID_CONFLICT 0x0010
+};
+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 event *hook_timer;
+};
+
+RB_HEAD(srte_candidate_head, srte_candidate);
+RB_PROTOTYPE(srte_candidate_head, srte_candidate, entry, srte_candidate_compare)
+
+#define ENDPOINT_STR_LENGTH IPADDR_STRING_SIZE
+
+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;
+
+ /* The Protocol-Origin. */
+ enum srte_protocol_origin protocol_origin;
+
+ /* The Originator */
+ char originator[64];
+
+ /* 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
+ /* SRP id for PcInitiated support */
+ int srp_id;
+};
+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 event_loop *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);
+int 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,
+ uint8_t algo, uint8_t pref_len);
+void srte_segment_set_local_modification(struct srte_segment_list *s_list,
+ struct srte_segment_entry *s_entry,
+ uint32_t ted_sid);
+struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint,
+ enum srte_protocol_origin origin,
+ const char *originator);
+void srte_policy_del(struct srte_policy *policy);
+struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint);
+int srte_policy_update_ted_sid(void);
+void srte_policy_update_binding_sid(struct srte_policy *policy,
+ uint32_t binding_sid);
+void srte_apply_changes(void);
+void srte_clean_zebra(void);
+void srte_policy_apply_changes(struct srte_policy *policy);
+struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
+ uint32_t preference,
+ enum srte_protocol_origin origin,
+ const char *originator);
+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);
+void pathd_shutdown(void);
+void path_policy_show_debugging(struct vty *vty);
+
+/* path_cli.c */
+void path_cli_init(void);
+
+
+/**
+ * Search for sid based in prefix and algorithm
+ *
+ * @param Prefix The prefix to use
+ * @param algo Algorithm we want to query for
+ * @param ted_sid Sid to query
+ *
+ * @return void
+ */
+int32_t srte_ted_do_query_type_c(struct srte_segment_entry *entry,
+ struct prefix *prefix_cli, uint32_t algo);
+
+/**
+ * Search for sid based in prefix and interface id
+ *
+ * @param Prefix The prefix to use
+ * @param local_iface The id of interface
+ * @param ted_sid Sid to query
+ *
+ * @return void
+ */
+int32_t srte_ted_do_query_type_e(struct srte_segment_entry *entry,
+ struct prefix *prefix_cli,
+ uint32_t local_iface);
+/**
+ * Search for sid based in local and remote ip
+ *
+ * @param entry entry to update
+ * @param local Local addr for query
+ * @param remote Local addr for query
+ *
+ * @return void
+ */
+int32_t srte_ted_do_query_type_f(struct srte_segment_entry *entry,
+ struct ipaddr *local, struct ipaddr *remote);
+#endif /* _FRR_PATHD_H_ */
diff --git a/pathd/subdir.am b/pathd/subdir.am
new file mode 100644
index 0000000..29be8f4
--- /dev/null
+++ b/pathd/subdir.am
@@ -0,0 +1,80 @@
+#
+# pathd
+#
+
+if PATHD
+noinst_LIBRARIES += pathd/libpath.a
+sbin_PROGRAMS += pathd/pathd
+vtysh_daemons += pathd
+# TODO add man page
+#man8 += $(MANBUILD)/pathd.8
+
+if PATHD_PCEP
+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_nb.c \
+ pathd/path_nb_config.c \
+ pathd/path_nb_state.c \
+ pathd/path_ted.c \
+ pathd/path_zebra.c \
+ pathd/pathd.c \
+ # end
+
+clippy_scan += \
+ pathd/path_cli.c \
+ pathd/path_pcep_cli.c \
+ pathd/path_ted.c \
+ # end
+
+noinst_HEADERS += \
+ pathd/path_debug.h \
+ pathd/path_errors.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_config.h \
+ pathd/path_pcep_pcc.h \
+ pathd/path_ted.h \
+ pathd/path_zebra.h \
+ pathd/pathd.h \
+ # end
+
+pathd_pathd_SOURCES = \
+ pathd/path_main.c \
+ # end
+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_config.c \
+ pathd/path_pcep_pcc.c \
+ # end
+
+if PATHD_PCEP
+pathd_pathd_pcep_la_CPPFLAGS = -I./pceplib $(AM_CPPFLAGS)
+pathd_pathd_pcep_la_LIBADD = pceplib/libpcep_pcc.la
+else
+pathd_pathd_pcep_la_CPPFLAGS = $(AM_CPPFLAGS)
+pathd_pathd_pcep_la_LIBADD =
+endif
+
+
+#pathd_pathd_pcep_la_CFLAGS = $(AM_CFLAGS)
+pathd_pathd_pcep_la_LDFLAGS = $(MODULE_LDFLAGS)