1337 lines
41 KiB
C
1337 lines
41 KiB
C
|
// 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);
|
||
|
}
|