summaryrefslogtreecommitdiffstats
path: root/pceplib/pcep_pcc.c
diff options
context:
space:
mode:
Diffstat (limited to 'pceplib/pcep_pcc.c')
-rw-r--r--pceplib/pcep_pcc.c520
1 files changed, 520 insertions, 0 deletions
diff --git a/pceplib/pcep_pcc.c b/pceplib/pcep_pcc.c
new file mode 100644
index 0000000..18ccf25
--- /dev/null
+++ b/pceplib/pcep_pcc.c
@@ -0,0 +1,520 @@
+/*
+ * This file is part of the PCEPlib, a PCEP protocol library.
+ *
+ * Copyright (C) 2020 Volta Networks https://voltanet.io/
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * 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/>.
+ *
+ * Author : Brady Johnson <brady@voltanet.io>
+ *
+ */
+
+
+/*
+ * Sample PCC implementation
+ */
+
+#include <zebra.h>
+
+#include <netdb.h> // gethostbyname
+#include <netinet/tcp.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "pcep_pcc_api.h"
+#include "pcep_utils_double_linked_list.h"
+#include "pcep_utils_logging.h"
+#include "pcep_utils_memory.h"
+
+/*
+ * PCEP PCC design spec:
+ * https://docs.google.com/presentation/d/1DYc3ZhYA1c_qg9A552HjhneJXQKdh_yrKW6v3NRYPtnbw/edit?usp=sharing
+ */
+#define MAX_SRC_IP_STR 40
+#define MAX_DST_IP_STR 40
+struct cmd_line_args {
+ char src_ip_str[MAX_SRC_IP_STR];
+ char dest_ip_str[MAX_DST_IP_STR];
+ short src_tcp_port;
+ short dest_tcp_port;
+ char tcp_md5_str[PCEP_MD5SIG_MAXKEYLEN]; /* RFC 2385 */
+ bool is_ipv6;
+ bool eventpoll; /* poll for pcep_event's, or use callback (default) */
+};
+
+bool pcc_active_ = true;
+pcep_session *session = NULL;
+struct cmd_line_args *cmd_line_args = NULL;
+/* pcep_event callback variables */
+bool pcep_event_condition = false;
+struct pcep_event *event = NULL;
+pthread_mutex_t pcep_event_mutex;
+pthread_cond_t pcep_event_cond_var;
+
+static const char DEFAULT_DEST_HOSTNAME[] = "localhost";
+static const char DEFAULT_DEST_HOSTNAME_IPV6[] = "ip6-localhost";
+static const short DEFAULT_SRC_TCP_PORT = 4999;
+
+// Private fn's
+struct cmd_line_args *get_cmdline_args(int argc, char *argv[]);
+void handle_signal_action(int sig_number);
+int setup_signals(void);
+void send_pce_path_request_message(pcep_session *session);
+void send_pce_report_message(pcep_session *session);
+void print_queue_event(struct pcep_event *event);
+void pcep_event_callback(void *cb_data, pcep_event *e);
+
+struct cmd_line_args *get_cmdline_args(int argc, char *argv[])
+{
+ /* Allocate and set default values */
+ struct cmd_line_args *cmd_line_args =
+ malloc(sizeof(struct cmd_line_args));
+ memset(cmd_line_args, 0, sizeof(struct cmd_line_args));
+ strlcpy(cmd_line_args->dest_ip_str, DEFAULT_DEST_HOSTNAME,
+ MAX_DST_IP_STR);
+ cmd_line_args->src_tcp_port = DEFAULT_SRC_TCP_PORT;
+ cmd_line_args->is_ipv6 = false;
+
+ /* Parse the cmd_line args:
+ * -ipv6
+ * -srcip localhost
+ * -destip 192.168.0.2
+ * -srcport 4999
+ * -dstport 4189
+ * -tcpmd5 hello
+ * -event_poll */
+ int i = 1;
+ for (; i < argc; ++i) {
+ if (strcmp(argv[i], "-help") == 0
+ || strcmp(argv[i], "--help") == 0
+ || strcmp(argv[i], "-h") == 0) {
+ pcep_log(
+ LOG_INFO,
+ "%s: pcep_pcc [-ipv6] [-srcip localhost] [-destip 192.168.0.1] [-srcport 4999] [-dstport 4189] [-tcpmd5 authstr] [-eventpoll]",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ } else if (strcmp(argv[i], "-ipv6") == 0) {
+ cmd_line_args->is_ipv6 = true;
+ if (argc == 2) {
+ strlcpy(cmd_line_args->dest_ip_str,
+ DEFAULT_DEST_HOSTNAME_IPV6,
+ MAX_DST_IP_STR);
+ }
+ } else if (strcmp(argv[i], "-eventpoll") == 0) {
+ cmd_line_args->eventpoll = true;
+ } else if (strcmp(argv[i], "-srcip") == 0) {
+ if (argc >= i + 2) {
+ strlcpy(cmd_line_args->src_ip_str, argv[++i],
+ MAX_SRC_IP_STR);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-srcip\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-destip") == 0) {
+ if (argc >= i + 2) {
+ strlcpy(cmd_line_args->dest_ip_str, argv[++i],
+ MAX_DST_IP_STR);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-destip\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-srcport") == 0) {
+ if (argc >= i + 2) {
+ cmd_line_args->src_tcp_port = atoi(argv[++i]);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-srcport\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-destport") == 0) {
+ if (argc >= i + 2) {
+ cmd_line_args->dest_tcp_port = atoi(argv[++i]);
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-destport\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else if (strcmp(argv[i], "-tcpmd5") == 0) {
+ if (argc >= i + 2) {
+ strlcpy(cmd_line_args->tcp_md5_str, argv[++i],
+ sizeof(cmd_line_args->tcp_md5_str));
+ } else {
+ pcep_log(
+ LOG_ERR,
+ "%s: Invalid number of cmd_line_args for \"-tcpmd5\"",
+ __func__);
+ free(cmd_line_args);
+ return NULL;
+ }
+ } else {
+ pcep_log(LOG_ERR, "%s: Invalid cmd_line_arg[%d] = %s",
+ __func__, i, argv[i]);
+ free(cmd_line_args);
+ return NULL;
+ }
+ }
+
+ return cmd_line_args;
+}
+
+void handle_signal_action(int sig_number)
+{
+ if (sig_number == SIGINT) {
+ pcep_log(LOG_INFO, "%s: SIGINT was caught!", __func__);
+ pcc_active_ = false;
+ if (cmd_line_args->eventpoll == false) {
+ pthread_mutex_lock(&pcep_event_mutex);
+ pcep_event_condition = true;
+ pthread_cond_signal(&pcep_event_cond_var);
+ pthread_mutex_unlock(&pcep_event_mutex);
+ }
+ } else if (sig_number == SIGUSR1) {
+ pcep_log(LOG_INFO, "%s: SIGUSR1 was caught, dumping counters",
+ __func__);
+ dump_pcep_session_counters(session);
+ pceplib_memory_dump();
+ } else if (sig_number == SIGUSR2) {
+ pcep_log(LOG_INFO, "%s: SIGUSR2 was caught, reseting counters",
+ __func__);
+ reset_pcep_session_counters(session);
+ }
+}
+
+
+int setup_signals()
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = handle_signal_action;
+ if (sigaction(SIGINT, &sa, 0) != 0) {
+ perror("sigaction()");
+ return -1;
+ }
+
+ if (sigaction(SIGUSR1, &sa, 0) != 0) {
+ perror("sigaction()");
+ return -1;
+ }
+
+ if (sigaction(SIGUSR2, &sa, 0) != 0) {
+ perror("sigaction()");
+ return -1;
+ }
+
+ return 0;
+}
+
+void send_pce_path_request_message(pcep_session *session)
+{
+ struct in_addr src_ipv4;
+ struct in_addr dst_ipv4;
+ inet_pton(AF_INET, "1.2.3.4", &src_ipv4);
+ inet_pton(AF_INET, "10.20.30.40", &dst_ipv4);
+
+ struct pcep_object_rp *rp_object =
+ pcep_obj_create_rp(1, false, false, false, false, 42, NULL);
+ struct pcep_object_endpoints_ipv4 *ep_object =
+ pcep_obj_create_endpoint_ipv4(&src_ipv4, &dst_ipv4);
+
+ struct pcep_message *path_request =
+ pcep_msg_create_request(rp_object, ep_object, NULL);
+ send_message(session, path_request, true);
+}
+
+void send_pce_report_message(pcep_session *session)
+{
+ double_linked_list *report_list = dll_initialize();
+
+ /* SRP Path Setup Type TLV */
+ struct pcep_object_tlv_path_setup_type *pst_tlv =
+ pcep_tlv_create_path_setup_type(SR_TE_PST);
+ double_linked_list *srp_tlv_list = dll_initialize();
+ dll_append(srp_tlv_list, pst_tlv);
+
+ /*
+ * Create the SRP object
+ */
+ uint32_t srp_id_number = 0x10203040;
+ struct pcep_object_header *obj =
+ (struct pcep_object_header *)pcep_obj_create_srp(
+ false, srp_id_number, srp_tlv_list);
+ if (obj == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message SRP object was NULL",
+ __func__);
+ dll_destroy_with_data(report_list);
+ return;
+ }
+ dll_append(report_list, obj);
+
+ /* LSP Symbolic path name TLV */
+ char symbolic_path_name[] = "second-default";
+ struct pcep_object_tlv_symbolic_path_name *spn_tlv =
+ pcep_tlv_create_symbolic_path_name(symbolic_path_name, 14);
+ double_linked_list *lsp_tlv_list = dll_initialize();
+ dll_append(lsp_tlv_list, spn_tlv);
+
+ /* LSP IPv4 LSP ID TLV */
+ struct in_addr ipv4_tunnel_sender;
+ struct in_addr ipv4_tunnel_endpoint;
+ inet_pton(AF_INET, "9.9.1.1", &ipv4_tunnel_sender);
+ inet_pton(AF_INET, "9.9.2.1", &ipv4_tunnel_endpoint);
+ struct pcep_object_tlv_ipv4_lsp_identifier *ipv4_lsp_id_tlv =
+ pcep_tlv_create_ipv4_lsp_identifiers(&ipv4_tunnel_sender,
+ &ipv4_tunnel_endpoint, 42,
+ 1, NULL);
+ dll_append(lsp_tlv_list, ipv4_lsp_id_tlv);
+
+ /*
+ * Create the LSP object
+ */
+ uint32_t plsp_id = 42;
+ enum pcep_lsp_operational_status lsp_status =
+ PCEP_LSP_OPERATIONAL_ACTIVE;
+ bool c_flag = false; /* Lsp was created by PcInitiate msg */
+ bool a_flag = false; /* Admin state, active / inactive */
+ bool r_flag = false; /* true if LSP has been removed */
+ bool s_flag = true; /* Synchronization */
+ bool d_flag = false; /* Delegate LSP to PCE */
+ obj = (struct pcep_object_header *)pcep_obj_create_lsp(
+ plsp_id, lsp_status, c_flag, a_flag, r_flag, s_flag, d_flag,
+ lsp_tlv_list);
+ if (obj == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message LSP object was NULL",
+ __func__);
+ dll_destroy_with_data(report_list);
+ return;
+ }
+ dll_append(report_list, obj);
+
+ /* Create 2 ERO NONAI sub-objects */
+ double_linked_list *ero_subobj_list = dll_initialize();
+ struct pcep_ro_subobj_sr *sr_subobj_nonai1 =
+ pcep_obj_create_ro_subobj_sr_nonai(false, 503808, true, true);
+ dll_append(ero_subobj_list, sr_subobj_nonai1);
+
+ struct pcep_ro_subobj_sr *sr_subobj_nonai2 =
+ pcep_obj_create_ro_subobj_sr_nonai(false, 1867776, true, true);
+ dll_append(ero_subobj_list, sr_subobj_nonai2);
+
+ /* Create ERO IPv4 node sub-object */
+ struct in_addr sr_subobj_ipv4;
+ inet_pton(AF_INET, "9.9.9.1", &sr_subobj_ipv4);
+ struct pcep_ro_subobj_sr *sr_subobj_ipv4node =
+ pcep_obj_create_ro_subobj_sr_ipv4_node(
+ false, false, false, true, 16060, &sr_subobj_ipv4);
+ if (sr_subobj_ipv4node == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message ERO sub-object was NULL",
+ __func__);
+ return;
+ }
+ dll_append(ero_subobj_list, sr_subobj_ipv4node);
+
+ /*
+ * Create the ERO object
+ */
+ obj = (struct pcep_object_header *)pcep_obj_create_ero(ero_subobj_list);
+ if (obj == NULL) {
+ pcep_log(LOG_WARNING,
+ "%s: send_pce_report_message ERO object was NULL",
+ __func__);
+ dll_destroy_with_data(report_list);
+ return;
+ }
+ dll_append(report_list, obj);
+
+ /*
+ * Create the Metric object
+ */
+ obj = (struct pcep_object_header *)pcep_obj_create_metric(
+ PCEP_METRIC_TE, false, true, 16.0);
+ dll_append(report_list, obj);
+
+ /* Create and send the report message */
+ struct pcep_message *report_msg = pcep_msg_create_report(report_list);
+ send_message(session, report_msg, true);
+}
+
+void print_queue_event(struct pcep_event *event)
+{
+ pcep_log(
+ LOG_INFO,
+ "%s: [%ld-%ld] Received Event: type [%s] on session [%d] occurred at [%ld]",
+ __func__, time(NULL), pthread_self(),
+ get_event_type_str(event->event_type),
+ event->session->session_id, event->event_time);
+
+ if (event->event_type == MESSAGE_RECEIVED) {
+ pcep_log(
+ LOG_INFO, "%s: \t Event message type [%s]", __func__,
+ get_message_type_str(event->message->msg_header->type));
+ }
+}
+
+/* Called by pcep_session_logic when pcep_event's are ready */
+void pcep_event_callback(void *cb_data, pcep_event *e)
+{
+ (void)cb_data;
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] pcep_event_callback", __func__,
+ time(NULL), pthread_self());
+ pthread_mutex_lock(&pcep_event_mutex);
+ event = e;
+ pcep_event_condition = true;
+ pthread_cond_signal(&pcep_event_cond_var);
+ pthread_mutex_unlock(&pcep_event_mutex);
+}
+
+int main(int argc, char **argv)
+{
+ pcep_log(LOG_NOTICE, "%s: [%ld-%ld] starting pcc_pcep example client",
+ __func__, time(NULL), pthread_self());
+
+ cmd_line_args = get_cmdline_args(argc, argv);
+ if (cmd_line_args == NULL) {
+ return -1;
+ }
+
+ setup_signals();
+
+ if (cmd_line_args->eventpoll == false) {
+ struct pceplib_infra_config infra_config;
+ memset(&infra_config, 0, sizeof(infra_config));
+ infra_config.pcep_event_func = pcep_event_callback;
+ if (!initialize_pcc_infra(&infra_config)) {
+ pcep_log(LOG_ERR,
+ "%s: Error initializing PCC with infra.",
+ __func__);
+ return -1;
+ }
+ } else {
+ if (!initialize_pcc()) {
+ pcep_log(LOG_ERR, "%s: Error initializing PCC.",
+ __func__);
+ return -1;
+ }
+ }
+
+ pcep_configuration *config = create_default_pcep_configuration();
+ config->pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = true;
+ config->src_pcep_port = cmd_line_args->src_tcp_port;
+ config->is_tcp_auth_md5 = true;
+
+ strlcpy(config->tcp_authentication_str, cmd_line_args->tcp_md5_str,
+ sizeof(config->tcp_authentication_str));
+
+ int af = (cmd_line_args->is_ipv6 ? AF_INET6 : AF_INET);
+ struct hostent *host_info =
+ gethostbyname2(cmd_line_args->dest_ip_str, af);
+ if (host_info == NULL) {
+ pcep_log(LOG_ERR, "%s: Error getting IP address.", __func__);
+ return -1;
+ }
+
+ if (cmd_line_args->is_ipv6) {
+ struct in6_addr host_address;
+ memcpy(&host_address, host_info->h_addr, host_info->h_length);
+ session = connect_pce_ipv6(config, &host_address);
+ } else {
+ struct in_addr host_address;
+ memcpy(&host_address, host_info->h_addr, host_info->h_length);
+ session = connect_pce(config, &host_address);
+ }
+
+ if (session == NULL) {
+ pcep_log(LOG_WARNING, "%s: Error in connect_pce.", __func__);
+ destroy_pcep_configuration(config);
+ return -1;
+ }
+
+ sleep(2);
+
+ send_pce_report_message(session);
+ /*send_pce_path_request_message(session);*/
+
+ /* Wait for pcep_event's either by polling the event queue or by
+ * callback */
+ if (cmd_line_args->eventpoll == true) {
+ /* Poll the pcep_event queue*/
+ while (pcc_active_) {
+ if (event_queue_is_empty() == false) {
+ struct pcep_event *event =
+ event_queue_get_event();
+ print_queue_event(event);
+ destroy_pcep_event(event);
+ }
+
+ sleep(5);
+ }
+ } else {
+ /* Get events via callback and conditional variable */
+ pthread_mutex_init(&pcep_event_mutex, NULL);
+ pthread_cond_init(&pcep_event_cond_var, NULL);
+ while (pcc_active_) {
+ pthread_mutex_lock(&pcep_event_mutex);
+
+ /* this internal loop helps avoid spurious interrupts */
+ while (!pcep_event_condition) {
+ pthread_cond_wait(&pcep_event_cond_var,
+ &pcep_event_mutex);
+ }
+
+ /* Check if we have been interrupted by SIGINT */
+ if (pcc_active_) {
+ print_queue_event(event);
+ destroy_pcep_event(event);
+ }
+
+ pcep_event_condition = false;
+ pthread_mutex_unlock(&pcep_event_mutex);
+ }
+
+ pthread_mutex_destroy(&pcep_event_mutex);
+ pthread_cond_destroy(&pcep_event_cond_var);
+ }
+
+ pcep_log(LOG_NOTICE, "%s: Disconnecting from PCE", __func__);
+ disconnect_pce(session);
+ destroy_pcep_configuration(config);
+ free(cmd_line_args);
+
+ if (!destroy_pcc()) {
+ pcep_log(LOG_NOTICE, "%s: Error stopping PCC.", __func__);
+ }
+
+ pceplib_memory_dump();
+
+ return 0;
+}