/*
* 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 .
*
* Author : Brady Johnson
*
*/
/*
* Sample PCC implementation
*/
#include
#include // gethostbyname
#include
#include
#include
#include
#include
#include
#include
#include
#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(struct sigaction));
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;
}