diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
commit | 2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch) | |
tree | c05dc0f8e6aa3accc84e3e5cffc933ed94941383 /pceplib | |
parent | Initial commit. (diff) | |
download | frr-upstream.tar.xz frr-upstream.zip |
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pceplib')
100 files changed, 26648 insertions, 0 deletions
diff --git a/pceplib/.gitignore b/pceplib/.gitignore new file mode 100644 index 0000000..a82fe5a --- /dev/null +++ b/pceplib/.gitignore @@ -0,0 +1,26 @@ +pcep_pcc +test/pcep_msg_tests +test/pcep_msg_tests.log +test/pcep_msg_tests.trs +test/pcep_pcc_api_tests +test/pcep_pcc_api_tests.log +test/pcep_pcc_api_tests.trs +test/pcep_session_logic_tests +test/pcep_session_logic_tests.log +test/pcep_session_logic_tests.trs +test/pcep_socket_comm_tests +test/pcep_socket_comm_tests.log +test/pcep_socket_comm_tests.trs +test/pcep_timers_tests +test/pcep_timers_tests.log +test/pcep_timers_tests.trs +test/pcep_utils_tests +test/pcep_utils_tests.log +test/pcep_utils_tests.trs +test/valgrind.pcep_msg_tests.log +test/valgrind.pcep_pcc_api_tests.log +test/valgrind.pcep_session_logic_tests.log +test/valgrind.pcep_socket_comm_tests.log +test/valgrind.pcep_timers_tests.log +test/valgrind.pcep_utils_tests.log +../test-driver diff --git a/pceplib/pcep.h b/pceplib/pcep.h new file mode 100644 index 0000000..b5d02c7 --- /dev/null +++ b/pceplib/pcep.h @@ -0,0 +1,54 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + + +#ifndef PCEP_H_ +#define PCEP_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(linux) || defined(GNU_LINUX) + +#define ipv6_u __in6_u +#else +/* bsd family */ +#define ipv6_u __u6_addr +#ifdef __FreeBSD__ +#include <sys/endian.h> +#else +#include <endian.h> +#endif /* __FreeBSD__ */ +#endif + +#include <sys/socket.h> +#include <netinet/in.h> +#include <pthread.h> + +/* Cross-compilation seems to have trouble finding this */ +#if defined(TCP_MD5SIG_MAXKEYLEN) +#define PCEP_MD5SIG_MAXKEYLEN TCP_MD5SIG_MAXKEYLEN +#else +#define PCEP_MD5SIG_MAXKEYLEN 80 +#endif + +#endif diff --git a/pceplib/pcep_msg_encoding.h b/pceplib/pcep_msg_encoding.h new file mode 100644 index 0000000..d835b87 --- /dev/null +++ b/pceplib/pcep_msg_encoding.h @@ -0,0 +1,140 @@ +/* + * 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> + * + */ + + +/* + * Definitions for encoding and decoding PCEP messages, objects, and TLVs. + */ + +#ifndef PCEP_ENCODING_H +#define PCEP_ENCODING_H + +#include <stdbool.h> + +#include "pcep_msg_messages.h" +#include "pcep_msg_objects.h" +#include "pcep_msg_tlvs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct pcep_versioning { + bool draft_ietf_pce_segment_routing_07; /* If false, use draft16 */ + /* As more draft versions are incorporated, add appropriate attributes + */ +}; + +#define MESSAGE_HEADER_LENGTH 4 +#define PCEP_MESSAGE_LENGTH 65535 +#define OBJECT_HEADER_LENGTH 4 +#define OBJECT_RO_SUBOBJ_HEADER_LENGTH 2 +#define TLV_HEADER_LENGTH 4 +#define LENGTH_1WORD sizeof(uint32_t) +#define LENGTH_2WORDS sizeof(uint32_t) * 2 +#define LENGTH_3WORDS sizeof(uint32_t) * 3 +#define LENGTH_4WORDS sizeof(uint32_t) * 4 +#define LENGTH_5WORDS sizeof(uint32_t) * 5 +#define LENGTH_6WORDS sizeof(uint32_t) * 6 +#define LENGTH_7WORDS sizeof(uint32_t) * 7 +#define LENGTH_8WORDS sizeof(uint32_t) * 8 +#define LENGTH_9WORDS sizeof(uint32_t) * 9 +#define LENGTH_10WORDS sizeof(uint32_t) * 10 +#define LENGTH_11WORDS sizeof(uint32_t) * 11 +#define LENGTH_12WORDS sizeof(uint32_t) * 12 +#define LENGTH_13WORDS sizeof(uint32_t) * 13 + +/* When iterating sub-objects or TLVs, limit to 10 in case corrupt data is + * received */ +#define MAX_ITERATIONS 10 + +struct pcep_versioning *create_default_pcep_versioning(void); +void destroy_pcep_versioning(struct pcep_versioning *versioning); + +/* + * Message encoding / decoding functions + */ + +/* Called before sending messages to encode the message to a byte buffer in + * Network byte order. This function will also encode all the objects and their + * TLVs in the message. The result will be stored in the encoded_message field + * in the pcep_message. Implemented in pcep-messages-encoding.c */ +void pcep_encode_message(struct pcep_message *message, + struct pcep_versioning *versioning); + +/* Decode the message header and return the message length. + * Returns < 0 for invalid message headers. */ +int32_t pcep_decode_validate_msg_header(const uint8_t *msg_buf); + +/* Decode the entire message */ +struct pcep_message *pcep_decode_message(const uint8_t *message_buffer); + + +/* + * Object encoding / decoding functions + */ + +/* Implemented in pcep-objects-encoding.c + * Encode the object in struct pcep_object_header* into the uint8_t *buf, + * and return the encoded object_length. */ +uint16_t pcep_encode_object(struct pcep_object_header *object_hdr, + struct pcep_versioning *versioning, uint8_t *buf); + +/* Implemented in pcep-objects-encoding.c + * Decode the object, including the TLVs (if any) and return the object. + * Returns object on success, NULL otherwise. */ +struct pcep_object_header *pcep_decode_object(const uint8_t *msg_buf); + +/* Internal util functions implemented in pcep-objects-encoding.c */ +void encode_ipv6(struct in6_addr *src_ipv6, uint32_t *dst); +void decode_ipv6(const uint32_t *src, struct in6_addr *dst_ipv6); +uint16_t normalize_pcep_tlv_length(uint16_t length); +bool pcep_object_has_tlvs(struct pcep_object_header *object_hdr); +uint16_t pcep_object_get_length_by_hdr(struct pcep_object_header *object_hdr); +uint16_t pcep_object_get_length(enum pcep_object_classes object_class, + enum pcep_object_types object_type); + + +/* + * TLV encoding / decoding functions + */ + +/* Implemented in pcep-tlv-encoding.c + * Encode the tlv in struct pcep_tlv_header* into the uint8_t *buf, + * and return the encoded tlv_length. */ +uint16_t pcep_encode_tlv(struct pcep_object_tlv_header *tlv_hdr, + struct pcep_versioning *versioning, uint8_t *buf); + +/* Decode the TLV in tlv_buf and return a pointer to the object */ +struct pcep_object_tlv_header *pcep_decode_tlv(const uint8_t *tlv_buf); + + +/* + * utils mainly for testing purposes + */ +bool validate_message_objects(struct pcep_message *msg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pceplib/pcep_msg_messages.c b/pceplib/pcep_msg_messages.c new file mode 100644 index 0000000..9bbfc53 --- /dev/null +++ b/pceplib/pcep_msg_messages.c @@ -0,0 +1,312 @@ +/* + * 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> + * + */ + + +/* + * This is the implementation of a High Level PCEP message API. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <arpa/inet.h> +#include <stdarg.h> +#include <unistd.h> + +#include "pcep_msg_encoding.h" +#include "pcep_msg_messages.h" +#include "pcep_msg_objects.h" +#include "pcep_utils_double_linked_list.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +static struct pcep_message * +pcep_msg_create_common_with_obj_list(enum pcep_message_types msg_type, + double_linked_list *obj_list) +{ + struct pcep_message *message = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message)); + memset(message, 0, sizeof(struct pcep_message)); + message->msg_header = pceplib_malloc( + PCEPLIB_MESSAGES, sizeof(struct pcep_message_header)); + memset(message->msg_header, 0, sizeof(struct pcep_message_header)); + message->msg_header->type = msg_type; + message->msg_header->pcep_version = PCEP_MESSAGE_HEADER_VERSION; + message->obj_list = ((obj_list == NULL) ? dll_initialize() : obj_list); + + return message; +} + +static struct pcep_message * +pcep_msg_create_common(enum pcep_message_types msg_type) +{ + return pcep_msg_create_common_with_obj_list(msg_type, NULL); +} + +struct pcep_message *pcep_msg_create_open(uint8_t keepalive, uint8_t deadtimer, + uint8_t sid) +{ + struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_OPEN); + dll_append(message->obj_list, + pcep_obj_create_open(keepalive, deadtimer, sid, NULL)); + + return message; +} + +struct pcep_message * +pcep_msg_create_open_with_tlvs(uint8_t keepalive, uint8_t deadtimer, + uint8_t sid, double_linked_list *tlv_list) +{ + struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_OPEN); + dll_append(message->obj_list, + pcep_obj_create_open(keepalive, deadtimer, sid, tlv_list)); + + return message; +} + + +struct pcep_message * +pcep_msg_create_request(struct pcep_object_rp *rp, + struct pcep_object_endpoints_ipv4 *endpoints, + double_linked_list *object_list) +{ + if ((rp == NULL) || (endpoints == NULL)) { + return NULL; + } + + struct pcep_message *message = pcep_msg_create_common_with_obj_list( + PCEP_TYPE_PCREQ, object_list); + dll_prepend(message->obj_list, endpoints); + dll_prepend(message->obj_list, rp); + + return message; +} + +struct pcep_message * +pcep_msg_create_request_ipv6(struct pcep_object_rp *rp, + struct pcep_object_endpoints_ipv6 *endpoints, + double_linked_list *object_list) +{ + if ((rp == NULL) || (endpoints == NULL)) { + return NULL; + } + + struct pcep_message *message = pcep_msg_create_common_with_obj_list( + PCEP_TYPE_PCREQ, object_list); + dll_prepend(message->obj_list, endpoints); + dll_prepend(message->obj_list, rp); + + return message; +} + +struct pcep_message *pcep_msg_create_reply(struct pcep_object_rp *rp, + double_linked_list *object_list) +{ + struct pcep_message *message = pcep_msg_create_common_with_obj_list( + PCEP_TYPE_PCREP, object_list); + + if (rp != NULL) { + dll_prepend(message->obj_list, rp); + } + + return message; +} + +struct pcep_message *pcep_msg_create_close(uint8_t reason) +{ + struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_CLOSE); + dll_append(message->obj_list, pcep_obj_create_close(reason)); + + return message; +} + +struct pcep_message *pcep_msg_create_error(uint8_t error_type, + uint8_t error_value) +{ + struct pcep_message *message = pcep_msg_create_common(PCEP_TYPE_ERROR); + dll_append(message->obj_list, + pcep_obj_create_error(error_type, error_value)); + + return message; +} + +struct pcep_message * +pcep_msg_create_error_with_objects(uint8_t error_type, uint8_t error_value, + double_linked_list *object_list) +{ + struct pcep_message *message = pcep_msg_create_common_with_obj_list( + PCEP_TYPE_ERROR, object_list); + dll_prepend(message->obj_list, + pcep_obj_create_error(error_type, error_value)); + + return message; +} + +struct pcep_message *pcep_msg_create_keepalive() +{ + return (pcep_msg_create_common(PCEP_TYPE_KEEPALIVE)); +} + +struct pcep_message * +pcep_msg_create_report(double_linked_list *state_report_object_list) +{ + return (state_report_object_list == NULL + ? NULL + : pcep_msg_create_common_with_obj_list( + PCEP_TYPE_REPORT, state_report_object_list)); +} + +struct pcep_message * +pcep_msg_create_update(double_linked_list *update_request_object_list) +{ + if (update_request_object_list == NULL) { + pcep_log( + LOG_INFO, + "%s: pcep_msg_create_update NULL update_request_object_list", + __func__); + return NULL; + } + + /* There must be at least 3 objects: + * These 3 are mandatory: SRP, LSP, and ERO. The ERO may be empty */ + if (update_request_object_list->num_entries < 3) { + pcep_log( + LOG_INFO, + "%s: pcep_msg_create_update there must be at least 3 update objects", + __func__); + return NULL; + } + + double_linked_list_node *node = update_request_object_list->head; + struct pcep_object_header *obj_hdr = + (struct pcep_object_header *)node->data; + + /* Check for the mandatory first SRP object */ + if (obj_hdr->object_class != PCEP_OBJ_CLASS_SRP) { + /* If the SRP object is missing, the receiving PCC MUST send a + * PCErr message with Error-type=6 (Mandatory Object missing) + * and Error-value=10 (SRP object missing). */ + pcep_log( + LOG_INFO, + "%s: pcep_msg_create_update missing mandatory first SRP object", + __func__); + return NULL; + } + + /* Check for the mandatory 2nd LSP object */ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + if (obj_hdr->object_class != PCEP_OBJ_CLASS_LSP) { + /* If the LSP object is missing, the receiving PCC MUST send a + * PCErr message with Error-type=6 (Mandatory Object missing) + * and Error-value=8 (LSP object missing). */ + pcep_log( + LOG_INFO, + "%s: pcep_msg_create_update missing mandatory second LSP object", + __func__); + return NULL; + } + + /* Check for the mandatory 3rd ERO object */ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + if (obj_hdr->object_class != PCEP_OBJ_CLASS_ERO) { + /* If the ERO object is missing, the receiving PCC MUST send a + * PCErr message with Error-type=6 (Mandatory Object missing) + * and Error-value=9 (ERO object missing). */ + pcep_log( + LOG_INFO, + "%s: pcep_msg_create_update missing mandatory third ERO object", + __func__); + return NULL; + } + + return (pcep_msg_create_common_with_obj_list( + PCEP_TYPE_UPDATE, update_request_object_list)); +} + +struct pcep_message * +pcep_msg_create_initiate(double_linked_list *lsp_object_list) +{ + if (lsp_object_list == NULL) { + pcep_log( + LOG_INFO, + "%s: pcep_msg_create_initiate NULL update_request_object_list", + __func__); + return NULL; + } + + /* There must be at least 2 objects: SRP and LSP. */ + if (lsp_object_list->num_entries < 2) { + pcep_log( + LOG_INFO, + "%s: pcep_msg_create_initiate there must be at least 2 objects", + __func__); + return NULL; + } + + double_linked_list_node *node = lsp_object_list->head; + struct pcep_object_header *obj_hdr = + (struct pcep_object_header *)node->data; + + /* Check for the mandatory first SRP object */ + if (obj_hdr->object_class != PCEP_OBJ_CLASS_SRP) { + pcep_log( + LOG_INFO, + "%s: pcep_msg_create_initiate missing mandatory first SRP object", + __func__); + return NULL; + } + + /* Check for the mandatory 2nd LSP object */ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + if (obj_hdr->object_class != PCEP_OBJ_CLASS_LSP) { + pcep_log( + LOG_INFO, + "%s: pcep_msg_create_initiate missing mandatory second LSP object", + __func__); + return NULL; + } + + return (pcep_msg_create_common_with_obj_list(PCEP_TYPE_INITIATE, + lsp_object_list)); +} + +struct pcep_message *pcep_msg_create_notify(struct pcep_object_notify *notify, + double_linked_list *object_list) +{ + if (notify == NULL) { + pcep_log(LOG_INFO, + "%s: pcep_msg_create_notify NULL notify object", + __func__); + return NULL; + } + + struct pcep_message *message = pcep_msg_create_common_with_obj_list( + PCEP_TYPE_PCNOTF, object_list); + dll_prepend(message->obj_list, notify); + + return message; +} diff --git a/pceplib/pcep_msg_messages.h b/pceplib/pcep_msg_messages.h new file mode 100644 index 0000000..8542ea1 --- /dev/null +++ b/pceplib/pcep_msg_messages.h @@ -0,0 +1,132 @@ +/* + * 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> + */ + + +/* + * This is a High Level PCEP message API. + */ + +#ifndef PCEP_MESSAGES_H +#define PCEP_MESSAGES_H + +#include <stdint.h> +#include <netinet/in.h> /* struct in_addr */ + +#include "pcep_utils_double_linked_list.h" +#include "pcep_msg_objects.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum pcep_message_types { + PCEP_TYPE_OPEN = 1, + PCEP_TYPE_KEEPALIVE = 2, + PCEP_TYPE_PCREQ = 3, + PCEP_TYPE_PCREP = 4, + PCEP_TYPE_PCNOTF = 5, + PCEP_TYPE_ERROR = 6, + PCEP_TYPE_CLOSE = 7, + PCEP_TYPE_REPORT = 10, + PCEP_TYPE_UPDATE = 11, + PCEP_TYPE_INITIATE = 12, + PCEP_TYPE_START_TLS = 13, + PCEP_TYPE_MAX, +}; + +#define PCEP_MESSAGE_HEADER_VERSION 1 + +struct pcep_message_header { + uint8_t pcep_version; /* Current version is 1. */ + enum pcep_message_types + type; /* Defines message type: + OPEN/KEEPALIVE/PCREQ/PCREP/PCNOTF/ERROR/CLOSE */ +}; + +/* The obj_list is a double_linked_list of struct pcep_object_header pointers. + */ +struct pcep_message { + struct pcep_message_header *msg_header; + double_linked_list *obj_list; + uint8_t *encoded_message; + uint16_t encoded_message_length; +}; + + +/* + * Regarding memory usage: + * When creating messages, any objects and tlvs passed into these APIs will be + * free'd when the pcep_message is free'd. That includes the + * double_linked_list's. So, just create the objects and TLVs, put them in their + * double_linked_list's, and everything will be managed internally. The message + * will be deleted by pcep_msg_free_message() or pcep_msg_free_message_list() + * which, in turn will call one of: pcep_obj_free_object() and + * pcep_obj_free_tlv(). For received messages, call pcep_msg_free_message() to + * free them. + */ + +struct pcep_message *pcep_msg_create_open(uint8_t keepalive, uint8_t deadtimer, + uint8_t sid); +struct pcep_message * +pcep_msg_create_open_with_tlvs(uint8_t keepalive, uint8_t deadtimer, + uint8_t sid, double_linked_list *tlv_list); +struct pcep_message * +pcep_msg_create_request(struct pcep_object_rp *rp, + struct pcep_object_endpoints_ipv4 *endpoints, + double_linked_list *object_list); +struct pcep_message * +pcep_msg_create_request_ipv6(struct pcep_object_rp *rp, + struct pcep_object_endpoints_ipv6 *endpoints, + double_linked_list *object_list); +struct pcep_message *pcep_msg_create_reply(struct pcep_object_rp *rp, + double_linked_list *object_list); +struct pcep_message *pcep_msg_create_close(uint8_t reason); +struct pcep_message *pcep_msg_create_error(uint8_t error_type, + uint8_t error_value); +struct pcep_message *pcep_msg_create_error_with_objects( + uint8_t error_type, uint8_t error_value, + double_linked_list *object_list); /* include the offending objects */ +struct pcep_message *pcep_msg_create_keepalive(void); +struct pcep_message *pcep_msg_create_notify(struct pcep_object_notify *notify, + double_linked_list *object_list); + +/* Message defined in RFC 8231 section 6.1. Expecting double_linked_list of + * struct pcep_object_header* objects of type SRP, LSP, or path (ERO, Bandwidth, + * metrics, and RRO objects). */ +struct pcep_message * +pcep_msg_create_report(double_linked_list *state_report_object_list); +/* Message defined in RFC 8231. Expecting double_linked_list of at least 3 + * struct pcep_object_header* objects of type SRP, LSP, and path (ERO and + * intended-attribute-list). The ERO must be present, but may be empty if + * the PCE cannot find a valid path for a delegated LSP. */ +struct pcep_message * +pcep_msg_create_update(double_linked_list *update_request_object_list); +/* Message defined in RFC 8281. Expecting double_linked_list of at least 2 + * struct pcep_object_header* objects of type SRP and LSP for LSP deletion, and + * may also contain Endpoints, ERO and an attribute list for LSP creation. */ +struct pcep_message * +pcep_msg_create_initiate(double_linked_list *lsp_object_list); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pceplib/pcep_msg_messages_encoding.c b/pceplib/pcep_msg_messages_encoding.c new file mode 100644 index 0000000..e90ca1c --- /dev/null +++ b/pceplib/pcep_msg_messages_encoding.c @@ -0,0 +1,363 @@ +/* + * 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> + * + */ + + +/* + * Encoding and decoding for PCEP messages. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pcep_msg_encoding.h" +#include "pcep_msg_messages.h" +#include "pcep_msg_objects.h" +#include "pcep_msg_tools.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +#define ANY_OBJECT 0 +#define NO_OBJECT -1 +#define NUM_CHECKED_OBJECTS 4 +/* It wont compile with this definition: + static const int + MANDATORY_MESSAGE_OBJECT_CLASSES[PCEP_TYPE_INITIATE+1][NUM_CHECKED_OBJECTS] + */ +static const enum pcep_object_classes MANDATORY_MESSAGE_OBJECT_CLASSES[13][4] = + { + {NO_OBJECT, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* unsupported message ID = 0 */ + {PCEP_OBJ_CLASS_OPEN, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* PCEP_TYPE_OPEN = 1 */ + {NO_OBJECT, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* PCEP_TYPE_KEEPALIVE = 2 */ + {PCEP_OBJ_CLASS_RP, PCEP_OBJ_CLASS_ENDPOINTS, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_PCREQ = 3 */ + {PCEP_OBJ_CLASS_RP, ANY_OBJECT, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_PCREP = 4 */ + {PCEP_OBJ_CLASS_NOTF, ANY_OBJECT, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_PCNOTF = 5 */ + {PCEP_OBJ_CLASS_ERROR, ANY_OBJECT, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_ERROR = 6 */ + {PCEP_OBJ_CLASS_CLOSE, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* PCEP_TYPE_CLOSE = 7 */ + {NO_OBJECT, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* unsupported message ID = 8 */ + {NO_OBJECT, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* unsupported message ID = 9 */ + {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_REPORT = 10 */ + {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_UPDATE = 11 */ + {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_INITIATE = 12 */ +}; + +/* PCEP Message Common Header, According to RFC 5440 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ver | Flags | Message-Type | Message-Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Ver (Version - 3 bits): PCEP version number. Current version is version 1. + * + * Flags (5 bits): No flags are currently defined. Unassigned bits are + * considered as reserved. They MUST be set to zero on transmission + * and MUST be ignored on receipt. + */ +void pcep_encode_message(struct pcep_message *message, + struct pcep_versioning *versioning) +{ + if (message == NULL) { + return; + } + + if (message->msg_header == NULL) { + return; + } + + /* Internal buffer used for the entire message. Later, once the entire + * length is known, memory will be allocated and this buffer will be + * copied. */ + uint8_t message_buffer[PCEP_MESSAGE_LENGTH] = {0}; + + /* Write the message header. The message header length will be + * written when the entire length is known. */ + uint32_t message_length = MESSAGE_HEADER_LENGTH; + uint16_t net_order_length = 0; + message_buffer[0] = (message->msg_header->pcep_version << 5) & 0xf0; + message_buffer[1] = message->msg_header->type; + + if (message->obj_list == NULL) { + net_order_length = htons(message_length); + memcpy(message_buffer + 2, &net_order_length, + sizeof(net_order_length)); + message->encoded_message = + pceplib_malloc(PCEPLIB_MESSAGES, message_length); + memcpy(message->encoded_message, message_buffer, + message_length); + message->encoded_message_length = message_length; + + return; + } + + /* Encode each of the objects */ + double_linked_list_node *node = message->obj_list->head; + for (; node != NULL; node = node->next_node) { + message_length += + pcep_encode_object(node->data, versioning, + message_buffer + message_length); + if (message_length >= PCEP_MESSAGE_LENGTH) { + message->encoded_message = NULL; + message->encoded_message_length = 0; + return; + } + } + + net_order_length = htons(message_length); + memcpy(message_buffer + 2, &net_order_length, sizeof(net_order_length)); + message->encoded_message = + pceplib_malloc(PCEPLIB_MESSAGES, message_length); + memcpy(message->encoded_message, message_buffer, message_length); + message->encoded_message_length = message_length; +} + +/* + * Decoding functions + */ + +/* Expecting Host byte ordered header */ +static bool validate_msg_header(uint8_t msg_version, uint8_t msg_flags, + uint8_t msg_type, uint16_t msg_length) +{ + /* Invalid message if the length is less than the header + * size or if its not a multiple of 4 */ + if (msg_length < MESSAGE_HEADER_LENGTH || (msg_length % 4) != 0) { + pcep_log(LOG_INFO, + "%s: Invalid PCEP message header length [%d]", + __func__, msg_length); + return false; + } + + if (msg_version != PCEP_MESSAGE_HEADER_VERSION) { + pcep_log( + LOG_INFO, + "%s: Invalid PCEP message header version [0x%x] expected version [0x%x]", + __func__, msg_version, PCEP_MESSAGE_HEADER_VERSION); + return false; + } + + if (msg_flags != 0) { + pcep_log(LOG_INFO, + "%s: Invalid PCEP message header flags [0x%x]", + __func__, msg_flags); + return false; + } + + switch (msg_type) { + /* Supported message types */ + case PCEP_TYPE_OPEN: + case PCEP_TYPE_KEEPALIVE: + case PCEP_TYPE_PCREQ: + case PCEP_TYPE_PCREP: + case PCEP_TYPE_PCNOTF: + case PCEP_TYPE_ERROR: + case PCEP_TYPE_CLOSE: + case PCEP_TYPE_REPORT: + case PCEP_TYPE_UPDATE: + case PCEP_TYPE_INITIATE: + break; + default: + pcep_log(LOG_INFO, "%s: Invalid PCEP message header type [%d]", + __func__, msg_type); + return false; + break; + } + + return true; +} + +/* Internal util function */ +static uint16_t pcep_decode_msg_header(const uint8_t *msg_buf, + uint8_t *msg_version, uint8_t *msg_flags, + uint8_t *msg_type) +{ + // Check RFC 5440 for version and flags position. + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + //| Ver | Flags | Message-Type | Message-Length | + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + *msg_version = (msg_buf[0] >> 5) & 0x07; + *msg_flags = (msg_buf[0] & 0x1f); + *msg_type = msg_buf[1]; + uint16_t host_order_length; + memcpy(&host_order_length, msg_buf + 2, sizeof(host_order_length)); + return ntohs(host_order_length); +} + +/* Decode the message header and return the message length */ +int32_t pcep_decode_validate_msg_header(const uint8_t *msg_buf) +{ + uint8_t msg_version; + uint8_t msg_flags; + uint8_t msg_type; + uint32_t msg_length; + + msg_length = pcep_decode_msg_header(msg_buf, &msg_version, &msg_flags, + &msg_type); + + return ((validate_msg_header(msg_version, msg_flags, msg_type, + msg_length) + == false) + ? -1 + : (int32_t)msg_length); +} + +bool validate_message_objects(struct pcep_message *msg) +{ + if (msg->msg_header->type >= PCEP_TYPE_START_TLS) { + pcep_log( + LOG_INFO, + "%s: Rejecting received message: Unknown message type [%d]", + __func__, msg->msg_header->type); + return false; + } + + const enum pcep_object_classes *object_classes = + MANDATORY_MESSAGE_OBJECT_CLASSES[msg->msg_header->type]; + double_linked_list_node *node; + int index; + for (node = (msg->obj_list == NULL ? NULL : msg->obj_list->head), + index = 0; + index < NUM_CHECKED_OBJECTS; + index++, (node = (node == NULL ? NULL : node->next_node))) { + struct pcep_object_header *obj = + ((node == NULL) + ? NULL + : (struct pcep_object_header *)node->data); + + if ((int)object_classes[index] == NO_OBJECT) { + if (node != NULL) { + pcep_log( + LOG_INFO, + "%s: Rejecting received message: Unexpected object [%d] present", + __func__, obj->object_class); + return false; + } + } else if (object_classes[index] != ANY_OBJECT) { + if (node == NULL) { + pcep_log( + LOG_INFO, + "%s: Rejecting received message: Expecting object in position [%d], but none received", + __func__, index); + return false; + } else if (object_classes[index] != obj->object_class) { + pcep_log( + LOG_INFO, + "%s: Rejecting received message: Unexpected Object Class received [%d]", + __func__, object_classes[index]); + return false; + } + } + } + + return true; +} + +struct pcep_message *pcep_decode_message(const uint8_t *msg_buf) +{ + uint8_t msg_version; + uint8_t msg_flags; + uint8_t msg_type; + uint16_t msg_length; + + msg_length = pcep_decode_msg_header(msg_buf, &msg_version, &msg_flags, + &msg_type); + if (msg_length == 0) { + pcep_log(LOG_INFO, "%s: Discarding empty message", __func__); + return NULL; + } + if (msg_length >= PCEP_MESSAGE_LENGTH) { + pcep_log(LOG_INFO, "%s: Discarding message too big", __func__); + return NULL; + } + + struct pcep_message *msg = + pceplib_calloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message)); + + msg->msg_header = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct pcep_message_header)); + msg->msg_header->pcep_version = msg_version; + msg->msg_header->type = msg_type; + + msg->obj_list = dll_initialize(); + msg->encoded_message = pceplib_malloc(PCEPLIB_MESSAGES, msg_length); + memcpy(msg->encoded_message, msg_buf, msg_length); + msg->encoded_message_length = msg_length; + + uint16_t bytes_read = MESSAGE_HEADER_LENGTH; + while ((msg_length - bytes_read) >= OBJECT_HEADER_LENGTH) { + struct pcep_object_header *obj_hdr = + pcep_decode_object(msg_buf + bytes_read); + + if (obj_hdr == NULL) { + pcep_log(LOG_INFO, "%s: Discarding invalid message", + __func__); + pcep_msg_free_message(msg); + + return NULL; + } + + dll_append(msg->obj_list, obj_hdr); + bytes_read += obj_hdr->encoded_object_length; + } + + if (validate_message_objects(msg) == false) { + pcep_log(LOG_INFO, "%s: Discarding invalid message", __func__); + pcep_msg_free_message(msg); + + return NULL; + } + + return msg; +} + +struct pcep_versioning *create_default_pcep_versioning() +{ + struct pcep_versioning *versioning = + pceplib_malloc(PCEPLIB_INFRA, sizeof(struct pcep_versioning)); + memset(versioning, 0, sizeof(struct pcep_versioning)); + + return versioning; +} + +void destroy_pcep_versioning(struct pcep_versioning *versioning) +{ + pceplib_free(PCEPLIB_INFRA, versioning); +} diff --git a/pceplib/pcep_msg_object_error_types.c b/pceplib/pcep_msg_object_error_types.c new file mode 100644 index 0000000..c72dfd7 --- /dev/null +++ b/pceplib/pcep_msg_object_error_types.c @@ -0,0 +1,393 @@ +/* + * 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> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> + +#include "pcep_msg_object_error_types.h" +#include "pcep_utils_logging.h" + +/* All of these values were copied from: + * https://www.iana.org/assignments/pcep/pcep.xhtml#pcep-error-object + * Which was last updated 2020-06-02 */ + +static const char *error_type_strings[] = { + "Reserved", + "PCEP session establishment failure", + "Capability not supported", + "Unknown Object", + "Not supported object", + "Policy violation", + "Mandatory Object missing", + "Synchronized path computation request missing", + "Unknown request reference", + "Attempt to establish a second PCEP session", + + "Reception of an invalid object", /* 10 */ + "Unrecognized EXRS subobject", + "Diffserv-aware TE error", + "BRPC procedure completion failure", + "Unassigned 14", + "Global Concurrent Optimization Error", + "P2MP Capability Error", + "P2MP END-POINTS Error", + "P2MP Fragmentation Error", + "Invalid Operation", + + "LSP State Synchronization Error", /* 20 */ + "Invalid traffic engineering path setup type", + "Unassigned 22", + "Bad parameter value", + "LSP instantiation error", + "PCEP StartTLS failure", + "Association Error", + "WSON RWA Error", + "H-PCE Error", + "Path computation failure", + "Unassigned 30"}; + +static const char *error_value_strings[MAX_ERROR_TYPE][MAX_ERROR_VALUE] = { + + /* 0 Reserved */ + {"Unassigned"}, + + /* 1 PCEP session establishment failure */ + { + "Unassigned", + "reception of an invalid Open message or a non Open message.", + "no Open message received before the expiration of the OpenWait timer", + "unacceptable and non negotiable session characteristics", + "unacceptable but negotiable session characteristics", + "reception of a second Open message with still unacceptable session characteristics", + "reception of a PCErr message proposing unacceptable session characteristics", + "No Keepalive or PCErr message received before the expiration of the KeepWait timer", + "PCEP version not supported", + }, + + /* 2 Capability not supported */ + {"Unassigned"}, + + /* 3 Unknown Object */ + { + "Unassigned", + "Unrecognized object class", + "Unrecognized object Type", + }, + + /* 4 Not supported object */ + { + "Unassigned", + "Not supported object class", + "Not supported object Type", + "Unassigned", + "Unsupported parameter", + "Unsupported network performance constraint", + "Bandwidth Object type 3 or 4 not supported", + "Unsupported endpoint type in END-POINTS Generalized Endpoint object type", + "Unsupported TLV present in END-POINTS Generalized Endpoint object type", + "Unsupported granularity in the RP object flags", + }, + + /* 5 Policy violation */ + { + "Unassigned", + "C bit of the METRIC object set (request rejected)", + "O bit of the RP object cleared (request rejected)", + "objective function not allowed (request rejected)", + "OF bit of the RP object set (request rejected)", + "Global concurrent optimization not allowed", + "Monitoring message supported but rejected due to policy violation", + "P2MP Path computation is not allowed", + "Not allowed network performance constraint", + }, + + /* 6 Mandatory Object missing */ + { + "Unassigned", + "RP object missing", + "RRO missing for a reoptimization request (R bit of the RP object set)", + "END-POINTS object missing", + "MONITORING object missing", + "Unassigned", + "Unassigned", + "Unassigned", + "LSP object missing", + "ERO object missing", + "SRP object missing", + "LSP-IDENTIFIERS TLV missing", + "LSP-DB-VERSION TLV missing", + "S2LS object missing", + "P2MP-LSP-IDENTIFIERS TLV missing", + "DISJOINTNESS-CONFIGURATION TLV missing", + }, + + /* 7 Synchronized path computation request missing */ + {"Unassigned"}, + + /* 8 Unknown request reference */ + {"Unassigned"}, + + /* 9 Attempt to establish a second PCEP session */ + {"Unassigned"}, + + /* 10 Reception of an invalid object */ + { + "Unassigned", + "reception of an object with P flag not set although the P-flag must be set according to this specification.", + "Bad label value", + "Unsupported number of SR-ERO subobjects", + "Bad label format", + "ERO mixes SR-ERO subobjects with other subobject types", + "Both SID and NAI are absent in the SR-ERO subobject", + "Both SID and NAI are absent in the SR-RRO subobject", + "SYMBOLIC-PATH-NAME TLV missing", + "MSD exceeds the default for the PCEP session", + "RRO mixes SR-RRO subobjects with other subobject types", + "Malformed object", + "Missing PCE-SR-CAPABILITY sub-TLV", + "Unsupported NAI Type in the SR-ERO/SR-RRO subobject", + "Unknown SID", + "NAI cannot be resolved to a SID", + "Could not find SRGB", + "SID index exceeds SRGB size", + "Could not find SRLB", + "SID index exceeds SRLB size", + "Inconsistent SIDs in SR-ERO / SR-RRO subobjects", + "MSD must be nonzero", + "Mismatch of O field in S2LS and LSP object", + "Incompatible OF codes in H-PCE", + "Bad Bandwidth Object type 3 (Generalized bandwidth) or 4 (Generalized bandwidth of existing TE-LSP for which a reoptimization is requested)", + "Unsupported LSP Protection Flags in PROTECTION-ATTRIBUTE TLV", + "Unsupported Secondary LSP Protection Flags in PROTECTION-ATTRIBUTE TLV", + "Unsupported Link Protection Type in PROTECTION-ATTRIBUTE TLV", + "LABEL-SET TLV present with 0 bit set but without R bit set in RP", + "Wrong LABEL-SET TLV present with 0 and L bit set", + "Wrong LABEL-SET with O bit set and wrong format", + "Missing GMPLS-CAPABILITY TLV", + "Incompatible OF code", + }, + + /* 11 Unrecognized EXRS subobject */ + {"Unassigned"}, + + /* 12 Diffserv-aware TE error */ + { + "Unassigned", + "Unsupported class-type", + "Invalid class-type", + "Class-Type and setup priority do not form a configured TE-class", + }, + + /* 13 BRPC procedure completion failure */ + { + "Unassigned", + "BRPC procedure not supported by one or more PCEs along the domain path", + }, + + /* 14 Unassigned */ + {"Unassigned"}, + + /* 15 Global Concurrent Optimization Error */ + { + "Unassigned", + "Insufficient memory", + "Global concurrent optimization not supported", + }, + + /* 16 P2MP Capability Error */ + { + "Unassigned", + "The PCE cannot satisfy the request due to insufficient memory", + "The PCE is not capable of P2MP computation", + }, + + /* 17 P2MP END-POINTS Error */ + { + "Unassigned", + "The PCE cannot satisfy the request due to no END-POINTS with leaf type 2", + "The PCE cannot satisfy the request due to no END-POINTS with leaf type 3", + "The PCE cannot satisfy the request due to no END-POINTS with leaf type 4", + "The PCE cannot satisfy the request due to inconsistent END-POINTS", + }, + + /* 18 P2MP Fragmentation Error */ + { + "Unassigned", + "Fragmented request failure", + "Fragmented Report failure", + "Fragmented Update failure", + "Fragmented Instantiation failure", + }, + + /* 19 Invalid Operation */ + { + "Unassigned", + "Attempted LSP Update Request for a non-delegated LSP. The PCEP-ERROR object is followed by the LSP object that identifies the LSP.", + "Attempted LSP Update Request if the stateful PCE capability was not advertised.", + "Attempted LSP Update Request for an LSP identified by an unknown PLSP-ID.", + "Unassigned", + "Attempted LSP State Report if active stateful PCE capability was not advertised.", + "PCE-initiated LSP limit reached", + "Delegation for PCE-initiated LSP cannot be revoked", + "Non-zero PLSP-ID in LSP Initiate Request", + "LSP is not PCE initiated", + "PCE-initiated operation-frequency limit reached", + "Attempted LSP State Report for P2MP if stateful PCE capability for P2MP was not advertised", + "Attempted LSP Update Request for P2MP if active stateful PCE capability for P2MP was not advertised", + "Attempted LSP Instantiation Request for P2MP if stateful PCE instantiation capability for P2MP was not advertised", + "Auto-Bandwidth capability was not advertised", + }, + + /* 20 LSP State Synchronization Error */ + { + "Unassigned", + "A PCE indicates to a PCC that it cannot process (an otherwise valid) LSP State Report. The PCEP- ERROR object is followed by the LSP object that identifies the LSP.", + "LSP-DB version mismatch.", + "Attempt to trigger synchronization before PCE trigger.", + "Attempt to trigger a synchronization when the PCE triggered synchronization capability has not been advertised.", + "A PCC indicates to a PCE that it cannot complete the State Synchronization.", + "Received an invalid LSP-DB Version Number.", + "Received an invalid Speaker Entity Identifier.", + }, + + /* 21 Invalid traffic engineering path setup type */ + { + "Unassigned", + "Unsupported path setup type", + "Mismatched path setup type", + }, + + /* 22 Unassigned */ + {"Unassigned"}, + + /* 23 Bad parameter value */ + { + "Unassigned", + "SYMBOLIC-PATH-NAME in use", + "Speaker identity included for an LSP that is not PCE initiated", + }, + + /* 24 LSP instantiation error */ + { + "Unassigned", + "Unacceptable instantiation parameters", + "Internal error", + "Signaling error", + }, + + /* 25 PCEP StartTLS failure */ + { + "Unassigned", + "Reception of StartTLS after any PCEP exchange", + "Reception of any other message apart from StartTLS, Open, or PCErr", + "Failure, connection without TLS is not possible", + "Failure, connection without TLS is possible", + "No StartTLS message (nor PCErr/Open) before StartTLSWait timer expiry", + }, + + /* 26 Association Error */ + { + "Unassigned", + "Association Type is not supported", + "Too many LSPs in the association group", + "Too many association groups", + "Association unknown", + "Operator-configured association information mismatch", + "Association information mismatch", + "Cannot join the association group", + "Association ID not in range", + "Tunnel ID or End points mismatch for Path Protection Association", + "Attempt to add another working/protection LSP for Path Protection Association", + "Protection type is not supported", + }, + + /* 27 WSON RWA Error */ + { + "Unassigned", + "Insufficient Memory", + "RWA computation Not supported", + "Syntactical Encoding error", + }, + + /* 28 H-PCE Error */ + { + "Unassigned", + "H-PCE Capability not advertised", + "Parent PCE Capability cannot be provided", + }, + + /* 29 Path computation failure */ + { + "Unassigned", + "Unacceptable request message", + "Generalized bandwidth value not supported", + "Label Set constraint could not be met", + "Label constraint could not be met", + } + + /* 30-255 Unassigned */ +}; + + +const char *get_error_type_str(enum pcep_error_type error_type) +{ + if (error_type < 0 || error_type >= MAX_ERROR_TYPE) { + pcep_log( + LOG_DEBUG, + "%s: get_error_type_str: error_type [%d] out of range [0..%d]", + __func__, error_type, MAX_ERROR_TYPE); + + return NULL; + } + + return error_type_strings[error_type]; +} + +const char *get_error_value_str(enum pcep_error_type error_type, + enum pcep_error_value error_value) +{ + if (error_type < 0 || error_type >= MAX_ERROR_TYPE) { + pcep_log( + LOG_DEBUG, + "%s: get_error_value_str: error_type [%d] out of range [0..%d]", + __func__, error_type, MAX_ERROR_TYPE); + + return NULL; + } + + if (error_value < 0 || error_value >= MAX_ERROR_VALUE) { + pcep_log( + LOG_DEBUG, + "%s: get_error_value_str: error_value [%d] out of range [0..%d]", + __func__, error_value, MAX_ERROR_VALUE); + + return NULL; + } + + if (error_value_strings[error_type][error_value] == NULL) { + return "Unassigned"; + } + + return error_value_strings[error_type][error_value]; +} diff --git a/pceplib/pcep_msg_object_error_types.h b/pceplib/pcep_msg_object_error_types.h new file mode 100644 index 0000000..d62cc7e --- /dev/null +++ b/pceplib/pcep_msg_object_error_types.h @@ -0,0 +1,284 @@ +/* + * 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> + */ + + +/* + * Error Object Type and Value definitions + */ + +#ifndef PCEP_OBJECT_ERROR_TYPES_H +#define PCEP_OBJECT_ERROR_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_ERROR_TYPE 30 +#define MAX_ERROR_VALUE 255 + +enum pcep_error_type { + PCEP_ERRT_SESSION_FAILURE = 1, + PCEP_ERRT_CAPABILITY_NOT_SUPPORTED = 2, + PCEP_ERRT_UNKNOW_OBJECT = 3, + PCEP_ERRT_NOT_SUPPORTED_OBJECT = 4, + PCEP_ERRT_POLICY_VIOLATION = 5, + PCEP_ERRT_MANDATORY_OBJECT_MISSING = 6, + PCEP_ERRT_SYNC_PC_REQ_MISSING = 7, + PCEP_ERRT_UNKNOWN_REQ_REF = 8, + PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION = 9, + PCEP_ERRT_RECEPTION_OF_INV_OBJECT = 10, + + PCEP_ERRT_UNRECOGNIZED_EXRS_SUBOBJ = 11, + PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR = 12, + PCEP_ERRT_BRPC_PROC_COMPLETION_ERROR = 13, + PCEP_ERRT_UNASSIGNED14 = 14, + PCEP_ERRT_GLOBAL_CONCURRENT_ERROR = 15, + PCEP_ERRT_P2PMP_CAP_ERROR = 16, + PCEP_ERRT_P2P_ENDPOINTS_ERROR = 17, + PCEP_ERRT_P2P_FRAGMENTATION_ERROR = 18, + PCEP_ERRT_INVALID_OPERATION = 19, + PCEP_ERRT_LSP_STATE_SYNC_ERROR = 20, + + PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE = 21, + PCEP_ERRT_UNASSIGNED22 = 22, + PCEP_ERRT_BAD_PARAMETER_VALUE = 23, + PCEP_ERRT_LSP_INSTANTIATE_ERROR = 24, + PCEP_ERRT_START_TLS_FAILURE = 25, + PCEP_ERRT_ASSOCIATION_ERROR = 26, + PCEP_ERRT_WSON_RWA_ERROR = 27, + PCEP_ERRT_H_PCE_ERROR = 28, + PCEP_ERRT_PATH_COMP_FAILURE = 29, + PCEP_ERRT_UNASSIGNED30 = 30 /* 30 - 255 Unassigned */ +}; + +enum pcep_error_value { + /* Error Value for Error Types that do not use an Error Value: + * PCEP_ERRT_CAPABILITY_NOT_SUPPORTED=2 + * PCEP_ERRT_SYNC_PC_REQ_MISSING=7 + * PCEP_ERRT_UNKNOWN_REQ_REF=8 + * PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION=9 + * PCEP_ERRT_UNRECOGNIZED_EXRS_SUBOBJ=11 */ + PCEP_ERRV_UNASSIGNED = 0, + + /* Error Values for PCEP_ERRT_SESSION_FAILURE=1 */ + PCEP_ERRV_RECVD_INVALID_OPEN_MSG = 1, + PCEP_ERRV_OPENWAIT_TIMED_OUT = 2, + PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NO_NEG = 3, + PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG = 4, + PCEP_ERRV_RECVD_SECOND_OPEN_MSG_UNACCEPTABLE = 5, + PCEP_ERRV_RECVD_PCERR = 6, + PCEP_ERRV_KEEPALIVEWAIT_TIMED_OUT = 7, + PCEP_ERRV_PCEP_VERSION_NOT_SUPPORTED = 8, + + /* Error Values for PCEP_ERRT_UNKNOW_OBJECT=3 */ + PCEP_ERRV_UNREC_OBJECT_CLASS = 1, + PCEP_ERRV_UNREC_OBJECT_TYPE = 2, + + /* Error Values for PCEP_ERRT_NOT_SUPPORTED_OBJECT=4 */ + PCEP_ERRV_NOT_SUPPORTED_OBJECT_CLASS = 1, + PCEP_ERRV_NOT_SUPPORTED_OBJECT_TYPE = 2, + /* 3: Unassigned */ + PCEP_ERRV_UNSUPPORTED_PARAM = 4, + PCEP_ERRV_UNSUPPORTED_NW_PERF_CONSTRAINT = 5, + PCEP_ERRV_NOT_SUPPORTED_BW_OBJECT_3_4 = 6, + PCEP_ERRV_UNSUPPORTED_ENDPOINT_TYPE = 7, + PCEP_ERRV_UNSUPPORTED_ENDPOINT_TLV = 8, + PCEP_ERRV_UNSUPPORTED_RP_FLAG_GRANULARITY = 9, + + /* Error Values for PCEP_ERRT_POLICY_VIOLATION=5 */ + PCEP_ERRV_C_BIT_SET_IN_METRIC_OBJECT = 1, + PCEP_ERRV_O_BIT_CLEARD_IN_RP_OBJECT = 2, + PCEP_ERRV_OBJECTIVE_FUNC_NOT_ALLOWED = 3, + PCEP_ERRV_RP_OF_BIT_SET = 4, + PCEP_ERRV_GLOBAL_CONCURRENCY_NOT_ALLOWED = 5, + PCEP_ERRV_MONITORING_MSG_REJECTED = 6, + PCEP_ERRV_P2MP_PATH_COMP_NOT_ALLOWED = 7, + PCEP_ERRV_UNALLOWED_NW_PERF_CONSTRAINT = 8, + + /* Error Values for PCEP_ERRT_MANDATORY_OBJECT_MISSING=6 */ + PCEP_ERRV_RP_OBJECT_MISSING = 1, + PCEP_ERRV_RRO_OBJECT_MISSING_FOR_REOP = 2, + PCEP_ERRV_EP_OBJECT_MISSING = 3, + PCEP_ERRV_MONITOR_OBJECT_MISSING = 4, + /* 5 - 7 Unassigned */ + PCEP_ERRV_LSP_OBJECT_MISSING = 8, + PCEP_ERRV_ERO_OBJECT_MISSING = 9, + PCEP_ERRV_SRP_OBJECT_MISSING = 10, + PCEP_ERRV_LSP_ID_TLV_MISSING = 11, + PCEP_ERRV_LSP_DB_TLV_MISSING = 12, + PCEP_ERRV_S2LS_OBJECT_MISSING = 13, + PCEP_ERRV_P2MP_LSP_ID_TLV_MISSING = 14, + PCEP_ERRV_DISJOINTED_CONF_TLV_MISSING = 15, + + /* Error Values for PCEP_ERRT_RECEPTION_OF_INV_OBJECT=10 */ + PCEP_ERRV_P_FLAG_NOT_CORRECT_IN_OBJECT = 1, + PCEP_ERRV_BAD_LABEL_VALUE = 2, + PCEP_ERRV_UNSUPPORTED_NUM_SR_ERO_SUBOBJECTS = 3, + PCEP_ERRV_BAD_LABEL_FORMAT = 4, + PCEP_ERRV_ERO_SR_ERO_MIX = 5, + PCEP_ERRV_SR_ERO_SID_NAI_ABSENT = 6, + PCEP_ERRV_SR_RRO_SID_NAI_ABSENT = 7, + PCEP_ERRV_SYMBOLIC_PATH_NAME_TLV_MISSING = 8, + PCEP_ERRV_MSD_EXCEEDS_PCEP_SESSION_MAX = 9, + + PCEP_ERRV_RRO_SR_RRO_MIX = 10, + PCEP_ERRV_MALFORMED_OBJECT = 11, + PCEP_ERRV_MISSING_PCE_SR_CAP_TLV = 12, + PCEP_ERRV_UNSUPPORTED_NAI = 13, + PCEP_ERRV_UNKNOWN_SID = 14, + PCEP_ERRV_CANNOT_RESOLVE_NAI_TO_SID = 15, + PCEP_ERRV_COULD_NOT_FIND_SRGB = 16, + PCEP_ERRV_SID_EXCEEDS_SRGB = 17, + PCEP_ERRV_COULD_NOT_FIND_SRLB = 18, + PCEP_ERRV_SID_EXCEEDS_SRLB = 19, + + PCEP_ERRV_INCONSISTENT_SID = 20, + PCEP_ERRV_MSD_MUST_BE_NONZERO = 21, + PCEP_ERRV_MISMATCH_O_S2LS_LSP = 22, + PCEP_ERRV_INCOMPATIBLE_H_PCE_OF = 23, + PCEP_ERRV_BAD_BANDWIDTH_TYPE_3_4 = 24, + PCEP_ERRV_UNSUPPORTED_LSP_PROT_FLAGS = 25, + PCEP_ERRV_UNSUPPORTED_2ND_LSP_PROT_FLAGS = 26, + PCEP_ERRV_UNSUPPORTED_LINK_PROT_TYPE = 27, + PCEP_ERRV_LABEL_SET_TLV_NO_RP_R = 28, + PCEP_ERRV_WRONG_LABEL_SET_TLV_O_L_SET = 29, + + PCEP_ERRV_WRONG_LABEL_SET_O_SET = 30, + PCEP_ERRV_MISSING_GMPLS_CAP_TLV = 31, + PCEP_ERRV_INCOMPATIBLE_OF_CODE = 32, + + /* PCEP_ERRT_DIFFSERV_AWARE_TE_ERROR = 12 */ + PCEP_ERRV_UNSUPPORTED_CLASS_TYPE = 1, + PCEP_ERRV_INVALID_CLASS_TYPE = 2, + PCEP_ERRV_CLASS_SETUP_TYPE_NOT_TE_CLASS = 3, + + /* PCEP_ERRT_BRPC_PROC_COMPLETION_ERROR = 13 */ + PCEP_ERRV_BRPC_PROC_NOT_SUPPORTED = 1, + + /* PCEP_ERRT_UNASSIGNED14 = 14 */ + + /* PCEP_ERRT_GLOBAL_CONCURRENT_ERROR = 15 */ + PCEP_ERRV_INSUFFICIENT_MEMORY = 1, + PCEP_ERRV_GLOBAL_CONCURRENT_OPT_NOT_SUPPORTED = 2, + + /* PCEP_ERRT_P2PMP_CAP_ERROR = 16 */ + PCEP_ERRV_PCE_INSUFFICIENT_MEMORY = 1, + PCEP_ERRV_PCE_NOT_CAPABLE_P2MP_COMP = 2, + + /* PCEP_ERRT_P2P_ENDPOINTS_ERROR = 17 */ + PCEP_ERRV_NO_EP_WITH_LEAF_TYPE2 = 1, + PCEP_ERRV_NO_EP_WITH_LEAF_TYPE3 = 2, + PCEP_ERRV_NO_EP_WITH_LEAF_TYPE4 = 3, + PCEP_ERRV_INCONSITENT_EP = 4, + + /* PCEP_ERRT_P2P_FRAGMENTATION_ERROR = 18 */ + PCEP_ERRV_FRAG_REQUEST_FAILURE = 1, + PCEP_ERRV_FRAG_REPORT_FAILURE = 2, + PCEP_ERRV_FRAG_UPDATE_FAILURE = 3, + PCEP_ERRV_FRAG_INSTANTIATION_FAILURE = 4, + + /* Error Values for PCEP_ERRT_INVALID_OPERATION=19 */ + PCEP_ERRV_LSP_UPDATE_FOR_NON_DELEGATED_LSP = 1, + PCEP_ERRV_LSP_UPDATE_NON_ADVERTISED_PCE = 2, + PCEP_ERRV_LSP_UPDATE_UNKNOWN_PLSP_ID = 3, + /* 4: unassigned */ + PCEP_ERRV_LSP_REPORT_NON_ADVERTISED_PCE = 5, + PCEP_ERRV_PCE_INIT_LSP_LIMIT_REACHED = 6, + PCEP_ERRV_PCE_INIT_LSP_DELEGATION_CANT_REVOKE = 7, + PCEP_ERRV_LSP_INIT_NON_ZERO_PLSP_ID = 8, + PCEP_ERRV_LSP_NOT_PCE_INITIATED = 9, + PCEP_ERRV_PCE_INIT_OP_FREQ_LIMIT_REACHED = 10, + PCEP_ERRV_LSP_REPORT_P2MP_NOT_ADVERTISED = 11, + PCEP_ERRV_LSP_UPDATE_P2MP_NOT_ADVERTISED = 12, + PCEP_ERRV_LSP_INSTANTIATION_P2MP_NOT_ADVERTISED = 13, + PCEP_ERRV_AUTO_BW_CAP_NOT_ADVERTISED = 14, + + /* Error Values for PCEP_ERRT_LSP_STATE_SYNC_ERROR=20 */ + PCEP_ERRV_PCE_CANT_PROCESS_LSP_REPORT = 1, + PCEP_ERRV_LSP_DB_VERSION_MISMATCH = 2, + PCEP_ERRV_TRIGGER_ATTEMPT_BEFORE_PCE_TRIGGER = 3, + PCEP_ERRV_TRIGGER_ATTEMPT_NO_PCE_TRIGGER_CAP = 4, + PCEP_ERRV_PCC_CANT_COMPLETE_STATE_SYNC = 5, + PCEP_ERRV_INVALID_LSP_DB_VERSION_NUMBER = 6, + PCEP_ERRV_INVALID_SPEAKER_ENTITY_ID = 7, + + /* PCEP_ERRT_INVALID_TE_PATH_SETUP_TYPE = 21 */ + PCEP_ERRV_UNSUPPORTED_PATH_SETUP_TYPE = 1, + PCEP_ERRV_MISMATCHED_PATH_SETUP_TYPE = 2, + + /* PCEP_ERRT_UNASSIGNED22 = 22 */ + + /* Error Values for PCEP_ERRT_BAD_PARAMETER_VALUE=23 */ + PCEP_ERRV_SYMBOLIC_PATH_NAME_IN_USE = 1, + PCEP_ERRV_LSP_SPEAKER_ID_NOT_PCE_INITIATED = 2, + + /* Error Values for PCEP_ERRT_LSP_INSTANTIATE_ERROR=24 */ + PCEP_ERRV_UNACCEPTABLE_INSTANTIATE_ERROR = 1, + PCEP_ERRV_INTERNAL_ERROR = 2, + PCEP_ERRV_SIGNALLING_ERROR = 3, + + /* PCEP_ERRT_START_TLS_FAILURE = 25 */ + PCEP_ERRV_START_TLS_AFTER_PCEP_EXCHANGE = 1, + PCEP_ERRV_MSG_NOT_START_TLS_OPEN_ERROR = 2, + PCEP_ERRV_CONNECTION_WO_TLS_NOT_POSSIBLE = 3, + PCEP_ERRV_CONNECTION_WO_TLS_IS_POSSIBLE = 4, + PCEP_ERRV_NO_START_TLS_BEFORE_START_TLS_WAIT_TIMER = 5, + + /* PCEP_ERRT_ASSOCIATION_ERROR = 26 */ + PCEP_ERRV_ASSOC_TYPE_NOT_SUPPORTED = 1, + PCEP_ERRV_TOO_MANY_LSPS_IN_ASSOC_GRP = 2, + PCEP_ERRV_TOO_MANY_ASSOC_GROUPS = 3, + PCEP_ERRV_ASSOCIATION_UNKNOWN = 4, + PCEP_ERRV_OP_CONF_ASSOC_INFO_MISMATCH = 5, + PCEP_ERRV_ASSOC_INFO_MISMATCH = 6, + PCEP_ERRV_CANNOT_JOIN_ASSOC_GROUP = 7, + PCEP_ERRV_ASSOC_ID_NOT_IN_RANGE = 8, + PCEP_ERRV_TUNNEL_EP_MISMATCH_PATH_PROT_ASSOC = 9, + PCEP_ERRV_ATTEMPTED_ADD_LSP_PATH_PROT_ASSOC = 10, + PCEP_ERRV_PROTECTION_TYPE_NOT_SUPPORTED = 11, + + /* PCEP_ERRT_WSON_RWA_ERROR = 27 */ + PCEP_ERRV_RWA_INSUFFICIENT_MEMORY = 1, + PCEP_ERRV_RWA_COMP_NOT_SUPPORTED = 2, + PCEP_ERRV_SYNTAX_ENC_ERROR = 3, + + /* PCEP_ERRT_H_PCE_ERROR = 28 */ + PCEP_ERRV_H_PCE_CAP_NOT_ADVERTISED = 1, + PCEP_ERRV_PARENT_PCE_CAP_CANT_BE_PROVIDED = 2, + + /* PCEP_ERRT_PATH_COMP_FAILURE = 29 */ + PCEP_ERRV_UNACCEPTABLE_REQUEST_MSG = 1, + PCEP_ERRV_GENERALIZED_BW_VAL_NOT_SUPPORTED = 2, + PCEP_ERRV_LABEL_SET_CONSTRAINT_COULD_NOT_BE_MET = 3, + PCEP_ERRV_LABEL_CONSTRAINT_COULD_NOT_BE_MET = 4, + +}; + +const char *get_error_type_str(enum pcep_error_type error_type); +const char *get_error_value_str(enum pcep_error_type error_type, + enum pcep_error_value error_value); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pceplib/pcep_msg_objects.c b/pceplib/pcep_msg_objects.c new file mode 100644 index 0000000..ce41ee9 --- /dev/null +++ b/pceplib/pcep_msg_objects.c @@ -0,0 +1,859 @@ +/* + * 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> + * + */ + + +/* + * This is the implementation of a High Level PCEP message object API. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <arpa/inet.h> +#include <stdarg.h> +#include <unistd.h> + +#include "pcep_msg_objects.h" +#include "pcep_msg_tlvs.h" +#include "pcep_utils_double_linked_list.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +/* Internal common function used to create a pcep_object and populate the header + */ +static struct pcep_object_header *pcep_obj_create_common_with_tlvs( + uint8_t obj_length, enum pcep_object_classes object_class, + enum pcep_object_types object_type, double_linked_list *tlv_list) +{ + uint8_t *buffer = pceplib_malloc(PCEPLIB_MESSAGES, obj_length); + memset(buffer, 0, obj_length); + + /* The flag_p and flag_i flags will be set externally */ + struct pcep_object_header *hdr = (struct pcep_object_header *)buffer; + hdr->object_class = object_class; + hdr->object_type = object_type; + hdr->tlv_list = tlv_list; + + return hdr; +} + +static struct pcep_object_header * +pcep_obj_create_common(uint8_t obj_length, + enum pcep_object_classes object_class, + enum pcep_object_types object_type) +{ + return pcep_obj_create_common_with_tlvs(obj_length, object_class, + object_type, NULL); +} + +struct pcep_object_open *pcep_obj_create_open(uint8_t keepalive, + uint8_t deadtimer, uint8_t sid, + double_linked_list *tlv_list) +{ + struct pcep_object_open *open = + (struct pcep_object_open *)pcep_obj_create_common_with_tlvs( + sizeof(struct pcep_object_open), PCEP_OBJ_CLASS_OPEN, + PCEP_OBJ_TYPE_OPEN, tlv_list); + + open->open_version = + PCEP_OBJECT_OPEN_VERSION; /* PCEP version. Current version is 1 + /No flags are currently defined. */ + open->open_keepalive = + keepalive; /* Maximum period of time between two consecutive + PCEP messages sent by the sender. */ + open->open_deadtimer = deadtimer; /* Specifies the amount of time before + closing the session down. */ + open->open_sid = sid; /* PCEP session number that identifies the current + session. */ + + return open; +} + +struct pcep_object_rp *pcep_obj_create_rp(uint8_t priority, bool flag_r, + bool flag_b, bool flag_s, + bool flag_of, uint32_t reqid, + double_linked_list *tlv_list) +{ + if (priority > OBJECT_RP_MAX_PRIORITY) { + pcep_log( + LOG_INFO, + "%s: Error creating RP object, invalid priority [%d], max priority [%d].", + __func__, priority, OBJECT_RP_MAX_PRIORITY); + return NULL; + } + + struct pcep_object_rp *obj = + (struct pcep_object_rp *)pcep_obj_create_common_with_tlvs( + sizeof(struct pcep_object_rp), PCEP_OBJ_CLASS_RP, + PCEP_OBJ_TYPE_RP, tlv_list); + + obj->priority = priority; + obj->flag_reoptimization = flag_r; + obj->flag_bidirectional = flag_b; + obj->flag_strict = flag_s; + obj->flag_of = flag_of; + obj->request_id = reqid; + + return obj; +} + +struct pcep_object_notify * +pcep_obj_create_notify(enum pcep_notification_types notification_type, + enum pcep_notification_values notification_value) +{ + struct pcep_object_notify *obj = + (struct pcep_object_notify *)pcep_obj_create_common( + sizeof(struct pcep_object_notify), PCEP_OBJ_CLASS_NOTF, + PCEP_OBJ_TYPE_NOTF); + + obj->notification_type = notification_type; + obj->notification_value = notification_value; + + return obj; +} + +struct pcep_object_nopath * +pcep_obj_create_nopath(uint8_t ni, bool flag_c, + enum pcep_nopath_tlv_err_codes error_code) +{ + struct pcep_object_tlv_nopath_vector *tlv = + pcep_tlv_create_nopath_vector(error_code); + double_linked_list *tlv_list = dll_initialize(); + dll_append(tlv_list, tlv); + + struct pcep_object_nopath *obj = + (struct pcep_object_nopath *)pcep_obj_create_common_with_tlvs( + sizeof(struct pcep_object_nopath), + PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH, tlv_list); + + obj->ni = ni; + obj->flag_c = flag_c; + obj->err_code = error_code; + + return obj; +} + +struct pcep_object_association_ipv4 * +pcep_obj_create_association_ipv4(bool r_flag, uint16_t association_type, + uint16_t association_id, struct in_addr src) +{ + struct pcep_object_association_ipv4 *obj = + (struct pcep_object_association_ipv4 *)pcep_obj_create_common( + sizeof(struct pcep_object_association_ipv4), + PCEP_OBJ_CLASS_ASSOCIATION, + PCEP_OBJ_TYPE_ASSOCIATION_IPV4); + + obj->R_flag = r_flag; + obj->association_type = association_type; + obj->association_id = association_id; + obj->src = src; + + return obj; +} +struct pcep_object_association_ipv6 * +pcep_obj_create_association_ipv6(bool r_flag, uint16_t association_type, + uint16_t association_id, struct in6_addr src) +{ + struct pcep_object_association_ipv6 *obj = + (struct pcep_object_association_ipv6 *)pcep_obj_create_common( + sizeof(struct pcep_object_association_ipv6), + PCEP_OBJ_CLASS_ASSOCIATION, + PCEP_OBJ_TYPE_ASSOCIATION_IPV6); + + obj->R_flag = r_flag; + obj->association_type = association_type; + obj->association_id = association_id; + obj->src = src; + + return obj; +} +struct pcep_object_endpoints_ipv4 * +pcep_obj_create_endpoint_ipv4(const struct in_addr *src_ipv4, + const struct in_addr *dst_ipv4) +{ + if (src_ipv4 == NULL || dst_ipv4 == NULL) { + return NULL; + } + + struct pcep_object_endpoints_ipv4 *obj = + (struct pcep_object_endpoints_ipv4 *)pcep_obj_create_common( + sizeof(struct pcep_object_endpoints_ipv4), + PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV4); + + obj->src_ipv4.s_addr = src_ipv4->s_addr; + obj->dst_ipv4.s_addr = dst_ipv4->s_addr; + + return obj; +} + +struct pcep_object_endpoints_ipv6 * +pcep_obj_create_endpoint_ipv6(const struct in6_addr *src_ipv6, + const struct in6_addr *dst_ipv6) +{ + if (src_ipv6 == NULL || dst_ipv6 == NULL) { + return NULL; + } + + struct pcep_object_endpoints_ipv6 *obj = + (struct pcep_object_endpoints_ipv6 *)pcep_obj_create_common( + sizeof(struct pcep_object_endpoints_ipv6), + PCEP_OBJ_CLASS_ENDPOINTS, PCEP_OBJ_TYPE_ENDPOINT_IPV6); + + memcpy(&obj->src_ipv6, src_ipv6, sizeof(struct in6_addr)); + memcpy(&obj->dst_ipv6, dst_ipv6, sizeof(struct in6_addr)); + + return obj; +} + +struct pcep_object_bandwidth *pcep_obj_create_bandwidth(float bandwidth) +{ + struct pcep_object_bandwidth *obj = + (struct pcep_object_bandwidth *)pcep_obj_create_common( + sizeof(struct pcep_object_bandwidth), + PCEP_OBJ_CLASS_BANDWIDTH, PCEP_OBJ_TYPE_BANDWIDTH_REQ); + + obj->bandwidth = bandwidth; + + return obj; +} + +struct pcep_object_metric *pcep_obj_create_metric(enum pcep_metric_types type, + bool flag_b, bool flag_c, + float value) +{ + struct pcep_object_metric *obj = + (struct pcep_object_metric *)pcep_obj_create_common( + sizeof(struct pcep_object_metric), + PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC); + + obj->flag_b = flag_b; + obj->flag_c = flag_c; + obj->type = type; + obj->value = value; + + return obj; +} + +struct pcep_object_lspa * +pcep_obj_create_lspa(uint32_t exclude_any, uint32_t include_any, + uint32_t include_all, uint8_t setup_priority, + uint8_t holding_priority, bool flag_local_protection) +{ + struct pcep_object_lspa *obj = + (struct pcep_object_lspa *)pcep_obj_create_common( + sizeof(struct pcep_object_lspa), PCEP_OBJ_CLASS_LSPA, + PCEP_OBJ_TYPE_LSPA); + + obj->lspa_exclude_any = exclude_any; + obj->lspa_include_any = include_any; + obj->lspa_include_all = include_all; + obj->setup_priority = setup_priority; + obj->holding_priority = holding_priority; + obj->flag_local_protection = flag_local_protection; + + return obj; +} + +struct pcep_object_svec * +pcep_obj_create_svec(bool srlg, bool node, bool link, + double_linked_list *request_id_list) +{ + if (request_id_list == NULL) { + return NULL; + } + + struct pcep_object_svec *obj = + (struct pcep_object_svec *)pcep_obj_create_common( + sizeof(struct pcep_object_svec), PCEP_OBJ_CLASS_SVEC, + PCEP_OBJ_TYPE_SVEC); + + obj->flag_srlg_diverse = srlg; + obj->flag_node_diverse = node; + obj->flag_link_diverse = link; + obj->request_id_list = request_id_list; + + return obj; +} + +struct pcep_object_error * +pcep_obj_create_error(enum pcep_error_type error_type, + enum pcep_error_value error_value) +{ + struct pcep_object_error *obj = + (struct pcep_object_error *)pcep_obj_create_common( + sizeof(struct pcep_object_error), PCEP_OBJ_CLASS_ERROR, + PCEP_OBJ_TYPE_ERROR); + + obj->error_type = error_type; + obj->error_value = error_value; + + return obj; +} + +struct pcep_object_close *pcep_obj_create_close(enum pcep_close_reason reason) +{ + struct pcep_object_close *obj = + (struct pcep_object_close *)pcep_obj_create_common( + sizeof(struct pcep_object_close), PCEP_OBJ_CLASS_CLOSE, + PCEP_OBJ_TYPE_CLOSE); + + obj->reason = reason; + + return obj; +} + +struct pcep_object_srp *pcep_obj_create_srp(bool lsp_remove, + uint32_t srp_id_number, + double_linked_list *tlv_list) +{ + struct pcep_object_srp *obj = + (struct pcep_object_srp *)pcep_obj_create_common_with_tlvs( + sizeof(struct pcep_object_srp), PCEP_OBJ_CLASS_SRP, + PCEP_OBJ_TYPE_SRP, tlv_list); + + obj->flag_lsp_remove = lsp_remove; + obj->srp_id_number = srp_id_number; + + return obj; +} + +struct pcep_object_lsp * +pcep_obj_create_lsp(uint32_t plsp_id, enum pcep_lsp_operational_status status, + bool c_flag, bool a_flag, bool r_flag, bool s_flag, + bool d_flag, double_linked_list *tlv_list) +{ + /* The plsp_id is only 20 bits */ + if (plsp_id > MAX_PLSP_ID) { + pcep_log( + LOG_INFO, + "%s: pcep_obj_create_lsp invalid plsp_id [%d] max value [%d]", + __func__, plsp_id, MAX_PLSP_ID); + return NULL; + } + + /* The status is only 3 bits */ + if (status > MAX_LSP_STATUS) { + pcep_log( + LOG_INFO, + "%s: pcep_obj_create_lsp invalid status [%d] max value [%d]", + __func__, plsp_id, MAX_PLSP_ID); + return NULL; + } + + struct pcep_object_lsp *obj = + (struct pcep_object_lsp *)pcep_obj_create_common_with_tlvs( + sizeof(struct pcep_object_lsp), PCEP_OBJ_CLASS_LSP, + PCEP_OBJ_TYPE_LSP, tlv_list); + + obj->plsp_id = plsp_id; + obj->operational_status = status; + obj->flag_c = c_flag; + obj->flag_a = a_flag; + obj->flag_r = r_flag; + obj->flag_s = s_flag; + obj->flag_d = d_flag; + + return obj; +} + +struct pcep_object_vendor_info * +pcep_obj_create_vendor_info(uint32_t enterprise_number, + uint32_t enterprise_spec_info) +{ + struct pcep_object_vendor_info *obj = + (struct pcep_object_vendor_info *)pcep_obj_create_common( + sizeof(struct pcep_object_vendor_info), + PCEP_OBJ_CLASS_VENDOR_INFO, PCEP_OBJ_TYPE_VENDOR_INFO); + + obj->enterprise_number = enterprise_number; + obj->enterprise_specific_info = enterprise_spec_info; + + return obj; +} + +struct pcep_object_inter_layer * +pcep_obj_create_inter_layer(bool flag_i, bool flag_m, bool flag_t) +{ + struct pcep_object_inter_layer *obj = + (struct pcep_object_inter_layer *)pcep_obj_create_common( + sizeof(struct pcep_object_inter_layer), + PCEP_OBJ_CLASS_INTER_LAYER, PCEP_OBJ_TYPE_INTER_LAYER); + + obj->flag_i = flag_i; + obj->flag_m = flag_m; + obj->flag_t = flag_t; + + return obj; +} + +struct pcep_object_switch_layer * +pcep_obj_create_switch_layer(double_linked_list *switch_layer_rows) +{ + struct pcep_object_switch_layer *obj = + (struct pcep_object_switch_layer *)pcep_obj_create_common( + sizeof(struct pcep_object_switch_layer), + PCEP_OBJ_CLASS_SWITCH_LAYER, + PCEP_OBJ_TYPE_SWITCH_LAYER); + + obj->switch_layer_rows = switch_layer_rows; + + return obj; +} + +struct pcep_object_req_adap_cap * +pcep_obj_create_req_adap_cap(enum pcep_switching_capability sw_cap, + enum pcep_lsp_encoding_type encoding) +{ + struct pcep_object_req_adap_cap *obj = + (struct pcep_object_req_adap_cap *)pcep_obj_create_common( + sizeof(struct pcep_object_req_adap_cap), + PCEP_OBJ_CLASS_REQ_ADAP_CAP, + PCEP_OBJ_TYPE_REQ_ADAP_CAP); + + obj->switching_capability = sw_cap; + obj->encoding = encoding; + + return obj; +} + +struct pcep_object_server_indication * +pcep_obj_create_server_indication(enum pcep_switching_capability sw_cap, + enum pcep_lsp_encoding_type encoding, + double_linked_list *tlv_list) +{ + struct pcep_object_server_indication *obj = + (struct pcep_object_server_indication *) + pcep_obj_create_common_with_tlvs( + sizeof(struct pcep_object_server_indication), + PCEP_OBJ_CLASS_SERVER_IND, + PCEP_OBJ_TYPE_SERVER_IND, tlv_list); + + obj->switching_capability = sw_cap; + obj->encoding = encoding; + + return obj; +} + +struct pcep_object_objective_function * +pcep_obj_create_objective_function(uint16_t of_code, + double_linked_list *tlv_list) +{ + struct pcep_object_objective_function *obj = + (struct pcep_object_objective_function *) + pcep_obj_create_common_with_tlvs( + sizeof(struct pcep_object_objective_function), + PCEP_OBJ_CLASS_OF, PCEP_OBJ_TYPE_OF, tlv_list); + + obj->of_code = of_code; + + return obj; +} + +/* Wrap a list of ro subobjects in a structure with an object header */ +struct pcep_object_ro *pcep_obj_create_ero(double_linked_list *ero_list) +{ + struct pcep_object_ro *ero = + (struct pcep_object_ro *)pcep_obj_create_common( + sizeof(struct pcep_object_ro), PCEP_OBJ_CLASS_ERO, + PCEP_OBJ_TYPE_ERO); + ero->sub_objects = ero_list; + + return ero; +} + +/* Wrap a list of ro subobjects in a structure with an object header */ +struct pcep_object_ro *pcep_obj_create_iro(double_linked_list *iro_list) +{ + struct pcep_object_ro *iro = + (struct pcep_object_ro *)pcep_obj_create_common( + sizeof(struct pcep_object_ro), PCEP_OBJ_CLASS_IRO, + PCEP_OBJ_TYPE_IRO); + iro->sub_objects = iro_list; + + return iro; +} + +/* Wrap a list of ro subobjects in a structure with an object header */ +struct pcep_object_ro *pcep_obj_create_rro(double_linked_list *rro_list) +{ + struct pcep_object_ro *rro = + (struct pcep_object_ro *)pcep_obj_create_common( + sizeof(struct pcep_object_ro), PCEP_OBJ_CLASS_RRO, + PCEP_OBJ_TYPE_RRO); + rro->sub_objects = rro_list; + + return rro; +} + +/* + * Route Object Sub-object creation functions + */ + +static struct pcep_object_ro_subobj * +pcep_obj_create_ro_subobj_common(uint8_t subobj_size, + enum pcep_ro_subobj_types ro_subobj_type, + bool flag_subobj_loose_hop) +{ + struct pcep_object_ro_subobj *ro_subobj = + pceplib_malloc(PCEPLIB_MESSAGES, subobj_size); + memset(ro_subobj, 0, subobj_size); + ro_subobj->flag_subobj_loose_hop = flag_subobj_loose_hop; + ro_subobj->ro_subobj_type = ro_subobj_type; + + return ro_subobj; +} + +struct pcep_ro_subobj_ipv4 * +pcep_obj_create_ro_subobj_ipv4(bool loose_hop, const struct in_addr *rro_ipv4, + uint8_t prefix_length, bool flag_local_prot) +{ + if (rro_ipv4 == NULL) { + return NULL; + } + + struct pcep_ro_subobj_ipv4 *obj = + (struct pcep_ro_subobj_ipv4 *)pcep_obj_create_ro_subobj_common( + sizeof(struct pcep_ro_subobj_ipv4), RO_SUBOBJ_TYPE_IPV4, + loose_hop); + obj->ip_addr.s_addr = rro_ipv4->s_addr; + obj->prefix_length = prefix_length; + obj->flag_local_protection = flag_local_prot; + + return obj; +} + +struct pcep_ro_subobj_ipv6 * +pcep_obj_create_ro_subobj_ipv6(bool loose_hop, const struct in6_addr *rro_ipv6, + uint8_t prefix_length, bool flag_local_prot) +{ + if (rro_ipv6 == NULL) { + return NULL; + } + + struct pcep_ro_subobj_ipv6 *obj = + (struct pcep_ro_subobj_ipv6 *)pcep_obj_create_ro_subobj_common( + sizeof(struct pcep_ro_subobj_ipv6), RO_SUBOBJ_TYPE_IPV6, + loose_hop); + obj->prefix_length = prefix_length; + obj->flag_local_protection = flag_local_prot; + memcpy(&obj->ip_addr, rro_ipv6, sizeof(struct in6_addr)); + + return obj; +} + +struct pcep_ro_subobj_unnum * +pcep_obj_create_ro_subobj_unnum(struct in_addr *router_id, uint32_t if_id) +{ + if (router_id == NULL) { + return NULL; + } + + struct pcep_ro_subobj_unnum *obj = + (struct pcep_ro_subobj_unnum *)pcep_obj_create_ro_subobj_common( + sizeof(struct pcep_ro_subobj_unnum), + RO_SUBOBJ_TYPE_UNNUM, false); + obj->interface_id = if_id; + obj->router_id.s_addr = router_id->s_addr; + + return obj; +} + +struct pcep_ro_subobj_32label * +pcep_obj_create_ro_subobj_32label(bool flag_global_label, uint8_t class_type, + uint32_t label) +{ + struct pcep_ro_subobj_32label *obj = (struct pcep_ro_subobj_32label *) + pcep_obj_create_ro_subobj_common( + sizeof(struct pcep_ro_subobj_32label), + RO_SUBOBJ_TYPE_LABEL, false); + obj->class_type = class_type; + obj->flag_global_label = flag_global_label; + obj->label = label; + + return obj; +} + +struct pcep_ro_subobj_asn *pcep_obj_create_ro_subobj_asn(uint16_t asn) +{ + struct pcep_ro_subobj_asn *obj = + (struct pcep_ro_subobj_asn *)pcep_obj_create_ro_subobj_common( + sizeof(struct pcep_ro_subobj_asn), RO_SUBOBJ_TYPE_ASN, + false); + obj->asn = asn; + + return obj; +} + +/* Internal util function to create pcep_ro_subobj_sr sub-objects */ +static struct pcep_ro_subobj_sr * +pcep_obj_create_ro_subobj_sr_common(enum pcep_sr_subobj_nai nai_type, + bool loose_hop, bool f_flag, bool s_flag, + bool c_flag_in, bool m_flag_in) +{ + struct pcep_ro_subobj_sr *obj = + (struct pcep_ro_subobj_sr *)pcep_obj_create_ro_subobj_common( + sizeof(struct pcep_ro_subobj_sr), RO_SUBOBJ_TYPE_SR, + loose_hop); + + /* Flag logic according to draft-ietf-pce-segment-routing-16 */ + bool c_flag = c_flag_in; + bool m_flag = m_flag_in; + if (s_flag) { + c_flag = false; + m_flag = false; + } + + if (m_flag == false) { + c_flag = false; + } + + obj->nai_type = nai_type; + obj->flag_f = f_flag; + obj->flag_s = s_flag; + obj->flag_c = c_flag; + obj->flag_m = m_flag; + + return obj; +} + +struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_nonai(bool loose_hop, + uint32_t sid, + bool c_flag, + bool m_flag) +{ + /* According to draft-ietf-pce-segment-routing-16#section-5.2.1 + * If NT=0, the F bit MUST be 1, the S bit MUST be zero and the + * Length MUST be 8. */ + struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common( + PCEP_SR_SUBOBJ_NAI_ABSENT, loose_hop, true, false, c_flag, + m_flag); + obj->sid = sid; + + return obj; +} + +struct pcep_ro_subobj_sr * +pcep_obj_create_ro_subobj_sr_ipv4_node(bool loose_hop, bool sid_absent, + bool c_flag, bool m_flag, uint32_t sid, + struct in_addr *ipv4_node_id) +{ + if (ipv4_node_id == NULL) { + return NULL; + } + + /* According to draft-ietf-pce-segment-routing-16#section-5.2.1 + * If NT=1, the F bit MUST be zero. If the S bit is 1, the Length + * MUST be 8, otherwise the Length MUST be 12 */ + struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common( + PCEP_SR_SUBOBJ_NAI_IPV4_NODE, loose_hop, false, sid_absent, + c_flag, m_flag); + + if (!sid_absent) { + obj->sid = sid; + } + obj->nai_list = dll_initialize(); + /* Since the IP has to be stored in the list, copy it so the caller + * doesn't have any restrictions about the type of memory used + * externally for the IP. This memory will be freed with the object is + * freed. */ + struct in_addr *ipv4_node_id_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in_addr)); + ipv4_node_id_copy->s_addr = ipv4_node_id->s_addr; + dll_append(obj->nai_list, ipv4_node_id_copy); + + return obj; +} + +struct pcep_ro_subobj_sr * +pcep_obj_create_ro_subobj_sr_ipv6_node(bool loose_hop, bool sid_absent, + bool c_flag, bool m_flag, uint32_t sid, + struct in6_addr *ipv6_node_id) +{ + if (ipv6_node_id == NULL) { + return NULL; + } + + /* According to draft-ietf-pce-segment-routing-16#section-5.2.1 + * If NT=2, the F bit MUST be zero. If the S bit is 1, the Length + * MUST be 20, otherwise the Length MUST be 24. */ + struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common( + PCEP_SR_SUBOBJ_NAI_IPV6_NODE, loose_hop, false, sid_absent, + c_flag, m_flag); + + if (!sid_absent) { + obj->sid = sid; + } + obj->nai_list = dll_initialize(); + struct in6_addr *ipv6_node_id_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr)); + memcpy(ipv6_node_id_copy, ipv6_node_id, sizeof(struct in6_addr)); + dll_append(obj->nai_list, ipv6_node_id_copy); + + return obj; +} + +struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv4_adj( + bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid, + struct in_addr *local_ipv4, struct in_addr *remote_ipv4) +{ + if (local_ipv4 == NULL || remote_ipv4 == NULL) { + return NULL; + } + + /* According to draft-ietf-pce-segment-routing-16#section-5.2.1 + * If NT=3, the F bit MUST be zero. If the S bit is 1, the Length + * MUST be 12, otherwise the Length MUST be 16 */ + struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common( + PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, loose_hop, false, sid_absent, + c_flag, m_flag); + + if (!sid_absent) { + obj->sid = sid; + } + obj->nai_list = dll_initialize(); + struct in_addr *local_ipv4_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in_addr)); + struct in_addr *remote_ipv4_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in_addr)); + local_ipv4_copy->s_addr = local_ipv4->s_addr; + remote_ipv4_copy->s_addr = remote_ipv4->s_addr; + dll_append(obj->nai_list, local_ipv4_copy); + dll_append(obj->nai_list, remote_ipv4_copy); + + return obj; +} + +struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv6_adj( + bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid, + struct in6_addr *local_ipv6, struct in6_addr *remote_ipv6) +{ + if (local_ipv6 == NULL || remote_ipv6 == NULL) { + return NULL; + } + + /* According to draft-ietf-pce-segment-routing-16#section-5.2.1 + * If NT=4, the F bit MUST be zero. If the S bit is 1, the Length + * MUST be 36, otherwise the Length MUST be 40 */ + struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common( + PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, loose_hop, false, sid_absent, + c_flag, m_flag); + + if (!sid_absent) { + obj->sid = sid; + } + obj->nai_list = dll_initialize(); + struct in6_addr *local_ipv6_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr)); + struct in6_addr *remote_ipv6_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr)); + memcpy(local_ipv6_copy, local_ipv6, sizeof(struct in6_addr)); + memcpy(remote_ipv6_copy, remote_ipv6, sizeof(struct in6_addr)); + dll_append(obj->nai_list, local_ipv6_copy); + dll_append(obj->nai_list, remote_ipv6_copy); + + return obj; +} + +struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj( + bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid, + uint32_t local_node_id, uint32_t local_if_id, uint32_t remote_node_id, + uint32_t remote_if_id) +{ + /* According to draft-ietf-pce-segment-routing-16#section-5.2.1 + * If NT=5, the F bit MUST be zero. If the S bit is 1, the Length + * MUST be 20, otherwise the Length MUST be 24. */ + struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common( + PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, loose_hop, false, + sid_absent, c_flag, m_flag); + + if (!sid_absent) { + obj->sid = sid; + } + + obj->nai_list = dll_initialize(); + uint32_t *local_node_id_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t)); + *local_node_id_copy = local_node_id; + dll_append(obj->nai_list, local_node_id_copy); + + uint32_t *local_if_id_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t)); + *local_if_id_copy = local_if_id; + dll_append(obj->nai_list, local_if_id_copy); + + uint32_t *remote_node_id_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t)); + *remote_node_id_copy = remote_node_id; + dll_append(obj->nai_list, remote_node_id_copy); + + uint32_t *remote_if_id_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t)); + *remote_if_id_copy = remote_if_id; + dll_append(obj->nai_list, remote_if_id_copy); + + return obj; +} + +struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj( + bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid, + struct in6_addr *local_ipv6, uint32_t local_if_id, + struct in6_addr *remote_ipv6, uint32_t remote_if_id) +{ + if (local_ipv6 == NULL || remote_ipv6 == NULL) { + return NULL; + } + + /* According to draft-ietf-pce-segment-routing-16#section-5.2.1 + * If NT=6, the F bit MUST be zero. If the S bit is 1, the Length + * MUST be 44, otherwise the Length MUST be 48 */ + struct pcep_ro_subobj_sr *obj = pcep_obj_create_ro_subobj_sr_common( + PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, loose_hop, false, + sid_absent, c_flag, m_flag); + + if (!sid_absent) { + obj->sid = sid; + } + obj->nai_list = dll_initialize(); + struct in6_addr *local_ipv6_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr)); + memcpy(local_ipv6_copy, local_ipv6, sizeof(struct in6_addr)); + dll_append(obj->nai_list, local_ipv6_copy); + + uint32_t *local_if_id_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t)); + *local_if_id_copy = local_if_id; + dll_append(obj->nai_list, local_if_id_copy); + + struct in6_addr *remote_ipv6_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct in6_addr)); + memcpy(remote_ipv6_copy, remote_ipv6, sizeof(struct in6_addr)); + dll_append(obj->nai_list, remote_ipv6_copy); + + uint32_t *remote_if_id_copy = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t)); + *remote_if_id_copy = remote_if_id; + dll_append(obj->nai_list, remote_if_id_copy); + + return obj; +} diff --git a/pceplib/pcep_msg_objects.h b/pceplib/pcep_msg_objects.h new file mode 100644 index 0000000..6d7d3be --- /dev/null +++ b/pceplib/pcep_msg_objects.h @@ -0,0 +1,746 @@ +/* + * 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> + */ + + +/* + * This is a High Level PCEP message object API. + */ + +#ifndef PCEP_OBJECTS_H +#define PCEP_OBJECTS_H + +#include <stdbool.h> +#include <stdint.h> + +#include "pcep.h" +#include "pcep_utils_double_linked_list.h" +#include "pcep_msg_object_error_types.h" +#include "pcep_msg_tlvs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Regarding memory usage: + * When creating objects, any objects passed into these APIs will be free'd when + * the enclosing pcep_message is free'd. That includes the double_linked_list's. + * So, just create the objects and TLVs, put them in their double_linked_list's, + * and everything will be managed internally. The enclosing message will be + * deleted by pcep_msg_free_message() or pcep_msg_free_message_list() which, + * in turn will call one of: pcep_obj_free_object() and pcep_obj_free_tlv(). + * For received messages with objects, call pcep_msg_free_message() to free + * them. + */ + +enum pcep_object_classes { + PCEP_OBJ_CLASS_OPEN = 1, + PCEP_OBJ_CLASS_RP = 2, + PCEP_OBJ_CLASS_NOPATH = 3, + PCEP_OBJ_CLASS_ENDPOINTS = 4, + PCEP_OBJ_CLASS_BANDWIDTH = 5, + PCEP_OBJ_CLASS_METRIC = 6, + PCEP_OBJ_CLASS_ERO = 7, + PCEP_OBJ_CLASS_RRO = 8, + PCEP_OBJ_CLASS_LSPA = 9, + PCEP_OBJ_CLASS_IRO = 10, + PCEP_OBJ_CLASS_SVEC = 11, + PCEP_OBJ_CLASS_NOTF = 12, + PCEP_OBJ_CLASS_ERROR = 13, + PCEP_OBJ_CLASS_CLOSE = 15, + PCEP_OBJ_CLASS_OF = 21, + PCEP_OBJ_CLASS_LSP = 32, + PCEP_OBJ_CLASS_SRP = 33, + PCEP_OBJ_CLASS_VENDOR_INFO = 34, + PCEP_OBJ_CLASS_INTER_LAYER = 36, /* RFC 8282 */ + PCEP_OBJ_CLASS_SWITCH_LAYER = 37, /* RFC 8282 */ + PCEP_OBJ_CLASS_REQ_ADAP_CAP = 38, /* RFC 8282 */ + PCEP_OBJ_CLASS_SERVER_IND = 39, /* RFC 8282 */ + PCEP_OBJ_CLASS_ASSOCIATION = 40, /*draft-ietf-pce-association-group-10*/ + PCEP_OBJ_CLASS_MAX, +}; + +enum pcep_object_types { + PCEP_OBJ_TYPE_OPEN = 1, + PCEP_OBJ_TYPE_RP = 1, + PCEP_OBJ_TYPE_NOPATH = 1, + PCEP_OBJ_TYPE_ENDPOINT_IPV4 = 1, + PCEP_OBJ_TYPE_ENDPOINT_IPV6 = 2, + PCEP_OBJ_TYPE_BANDWIDTH_REQ = 1, + PCEP_OBJ_TYPE_BANDWIDTH_TELSP = 2, + PCEP_OBJ_TYPE_BANDWIDTH_CISCO = + 5, /* IANA unassigned, but rcvd from Cisco PCE */ + PCEP_OBJ_TYPE_SRP = 1, + PCEP_OBJ_TYPE_VENDOR_INFO = 1, + PCEP_OBJ_TYPE_LSP = 1, + PCEP_OBJ_TYPE_METRIC = 1, + PCEP_OBJ_TYPE_ERO = 1, + PCEP_OBJ_TYPE_RRO = 1, + PCEP_OBJ_TYPE_LSPA = 1, + PCEP_OBJ_TYPE_IRO = 1, + PCEP_OBJ_TYPE_SVEC = 1, + PCEP_OBJ_TYPE_NOTF = 1, + PCEP_OBJ_TYPE_ERROR = 1, + PCEP_OBJ_TYPE_CLOSE = 1, + PCEP_OBJ_TYPE_INTER_LAYER = 1, + PCEP_OBJ_TYPE_SWITCH_LAYER = 1, + PCEP_OBJ_TYPE_REQ_ADAP_CAP = 1, + PCEP_OBJ_TYPE_SERVER_IND = 1, + PCEP_OBJ_TYPE_ASSOCIATION_IPV4 = + 1, /*draft-ietf-pce-association-group-10*/ + PCEP_OBJ_TYPE_ASSOCIATION_IPV6 = + 2, /*draft-ietf-pce-association-group-10*/ + PCEP_OBJ_TYPE_OF = 1, + PCEP_OBJ_TYPE_MAX = 2, +}; + +#define OBJECT_HEADER_FLAG_I 0x01 +#define OBJECT_HEADER_FLAG_P 0x02 + +/* The flag_p and flag_i arent set via the APIs, if they need to be set, just + * set them on the returned object once it has been created. */ +struct pcep_object_header { + enum pcep_object_classes object_class; + enum pcep_object_types object_type; + bool flag_p; /* PCC Processing rule bit: When set, the object MUST be + taken into account, when cleared the object is optional. + */ + bool flag_i; /* PCE Ignore bit: indicates to a PCC whether or not an + optional object was processed */ + double_linked_list *tlv_list; + /* Pointer into encoded_message field from the pcep_message */ + const uint8_t *encoded_object; + uint16_t encoded_object_length; +}; + +#define PCEP_OBJECT_OPEN_VERSION 1 + +struct pcep_object_open { + struct pcep_object_header header; + uint8_t open_version; /* PCEP version. Current version is 1 */ + uint8_t open_keepalive; /* Maximum period of time between two + consecutive PCEP messages sent by the sender. + */ + uint8_t open_deadtimer; /* Specifies the amount of time before closing + the session down. */ + uint8_t open_sid; /* PCEP session number that identifies the current + session. */ +}; + +#define OBJECT_RP_FLAG_R 0x08 +#define OBJECT_RP_FLAG_B 0x10 +#define OBJECT_RP_FLAG_O 0x20 +#define OBJECT_RP_FLAG_OF 0x80 +#define OBJECT_RP_MAX_PRIORITY 0x07 + +struct pcep_object_rp { + struct pcep_object_header header; + uint8_t priority; /* 3 bit priority, max priority is 7 */ + bool flag_reoptimization; + bool flag_bidirectional; + bool flag_strict; /* when set, a loose path is acceptable */ + bool flag_of; /* Supply Objective Function on Response */ + uint32_t request_id; /* The Request-id-number value combined with the + source for PCC & PCE creates a uniquely number. + */ +}; + +enum pcep_notification_types { + PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED = 1, + PCEP_NOTIFY_TYPE_PCE_OVERLOADED = 2 +}; + +enum pcep_notification_values { + PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST = 1, + PCEP_NOTIFY_VALUE_PCE_CANCELLED_REQUEST = 2, + PCEP_NOTIFY_VALUE_PCE_CURRENTLY_OVERLOADED = 1, + PCEP_NOTIFY_VALUE_PCE_NO_LONGER_OVERLOADED = 2 +}; + +struct pcep_object_notify { + struct pcep_object_header header; + enum pcep_notification_types notification_type; + enum pcep_notification_values notification_value; +}; + +enum pcep_association_type { + PCEP_ASSOCIATION_TYPE_PATH_PROTECTION_ASSOCIATION = + 1, // iana unique value define as 2020-01-08! + PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE = + 65535 // TBD1 draft-barth-pce-segment-routing-policy-cp-04 +}; +#define OBJECT_ASSOCIATION_FLAG_R 0x01 +struct pcep_object_association_ipv4 { // draft-ietf-pce-association-group-10 + struct pcep_object_header header; + bool R_flag; + uint16_t association_type; + uint16_t association_id; + struct in_addr src; +}; + +struct pcep_object_association_ipv6 { // draft-ietf-pce-association-group-10 + struct pcep_object_header header; + bool R_flag; + uint16_t association_type; + uint16_t association_id; + struct in6_addr src; +}; + + +enum pcep_nopath_nature_of_issue { + PCEP_NOPATH_NI_NO_PATH_FOUND = 0, + PCEP_NOPATH_NI_PCE_CHAIN_BROKEN = 1, +}; + +enum pcep_nopath_tlv_err_codes { + PCEP_NOPATH_TLV_ERR_NO_TLV = 0, + PCEP_NOPATH_TLV_ERR_PCE_UNAVAILABLE = 1, + PCEP_NOPATH_TLV_ERR_UNKNOWN_DST = 2, + PCEP_NOPATH_TLV_ERR_UNKNOWN_SRC = 3 +}; + +#define OBJECT_NOPATH_FLAG_C 0x80 + +struct pcep_object_nopath { + struct pcep_object_header header; + uint8_t ni; /* Nature of Issue, reports the nature of the issue that led + to a negative reply */ + bool flag_c; /* when set, indicates the unsatisfied constraints by + including relevant PCEP objects. */ + enum pcep_nopath_tlv_err_codes + err_code; /* When set other than 0, an appropriate TLV will be + included */ +}; + +struct pcep_object_endpoints_ipv4 { + struct pcep_object_header header; + struct in_addr src_ipv4; + struct in_addr dst_ipv4; +}; + +struct pcep_object_endpoints_ipv6 { + struct pcep_object_header header; + struct in6_addr src_ipv6; + struct in6_addr dst_ipv6; +}; + +/* PCEP floats are encoded according to: + * https://en.wikipedia.org/wiki/IEEE_754-1985 + * Luckily, this is the same encoding used by C */ +struct pcep_object_bandwidth { + struct pcep_object_header header; + float bandwidth; +}; + +enum pcep_metric_types { + /* RFC 5440 */ + PCEP_METRIC_IGP = 1, + PCEP_METRIC_TE = 2, + PCEP_METRIC_HOP_COUNT = 3, + /* RFC 5541 */ + PCEP_METRIC_AGGREGATE_BW = 4, + PCEP_METRIC_MOST_LOADED_LINK = 5, + PCEP_METRIC_CUMULATIVE_IGP = 6, + PCEP_METRIC_CUMULATIVE_TE = 7, + /* RFC 8306 */ + PCEP_METRIC_P2MP_IGP = 8, + PCEP_METRIC_P2MP_TE = 9, + PCEP_METRIC_P2MP_HOP_COUNT = 10, + /* RFC 8864 */ + PCEP_METRIC_SEGMENT_ID_DEPTH = 11, + /* RFC 8233 */ + PCEP_METRIC_PATH_DELAY = 12, + PCEP_METRIC_PATH_DELAY_VARIATION = 13, + PCEP_METRIC_PATH_LOSS = 14, + PCEP_METRIC_P2MP_PATH_DELAY = 15, + PCEP_METRIC_P2MP_PATH_DELAY_VARIATION = 16, + PCEP_METRIC_P2MP_PATH_LOSS = 17, + /* RFC 8282 */ + PCEP_METRIC_NUM_PATH_ADAPTATIONS = 18, + PCEP_METRIC_NUM_PATH_LAYERS = 19, + /* RFC 8685 */ + PCEP_METRIC_DOMAIN_COUNT = 20, + PCEP_METRIC_BORDER_NODE_COUNT = 21, +}; + +#define OBJECT_METRIC_FLAC_B 0x01 +#define OBJECT_METRIC_FLAC_C 0x02 + +/* PCEP floats are encoded according to: + * https://en.wikipedia.org/wiki/IEEE_754-1985 + * Luckily, this is the same encoding used by C */ +struct pcep_object_metric { + struct pcep_object_header header; + enum pcep_metric_types type; + bool flag_b; /* Bound flag */ + bool flag_c; /* Computed metric */ + float value; /* Metric value in 32 bits */ +}; + +#define OBJECT_LSPA_FLAG_L 0x01 + +struct pcep_object_lspa { + struct pcep_object_header header; + uint32_t lspa_exclude_any; + uint32_t lspa_include_any; + uint32_t lspa_include_all; + uint8_t setup_priority; + uint8_t holding_priority; + bool flag_local_protection; /* Local protection desired bit */ +}; + +/* The SVEC object with some custom extensions. */ +#define OBJECT_SVEC_FLAG_L 0x01 +#define OBJECT_SVEC_FLAG_N 0x02 +#define OBJECT_SVEC_FLAG_S 0x04 + +struct pcep_object_svec { + struct pcep_object_header header; + bool flag_link_diverse; + bool flag_node_diverse; + bool flag_srlg_diverse; + double_linked_list + *request_id_list; /* list of 32-bit request ID pointers */ +}; + +struct pcep_object_error { + struct pcep_object_header header; + enum pcep_error_type error_type; + enum pcep_error_value error_value; +}; + +struct pcep_object_load_balancing { + struct pcep_object_header header; + uint8_t load_maxlsp; /* Maximum number of TE LSPs in the set */ + uint32_t load_minband; /* Specifies the minimum bandwidth of each + element */ +}; + +enum pcep_close_reason { + PCEP_CLOSE_REASON_NO = 1, + PCEP_CLOSE_REASON_DEADTIMER = 2, + PCEP_CLOSE_REASON_FORMAT = 3, + PCEP_CLOSE_REASON_UNKNOWN_REQ = 4, + PCEP_CLOSE_REASON_UNREC_MSG = 5 +}; + +struct pcep_object_close { + struct pcep_object_header header; + enum pcep_close_reason reason; +}; + +/* Stateful PCE Request Parameters RFC 8231, 8281 */ + +#define OBJECT_SRP_FLAG_R 0x01 + +struct pcep_object_srp { + struct pcep_object_header header; + bool flag_lsp_remove; /* RFC 8281 */ + uint32_t srp_id_number; +}; + +/* Label Switched Path Object RFC 8231 */ +enum pcep_lsp_operational_status { + PCEP_LSP_OPERATIONAL_DOWN = 0, + PCEP_LSP_OPERATIONAL_UP = 1, + PCEP_LSP_OPERATIONAL_ACTIVE = 2, + PCEP_LSP_OPERATIONAL_GOING_DOWN = 3, + PCEP_LSP_OPERATIONAL_GOING_UP = 4, +}; + +#define MAX_PLSP_ID 0x000fffff /* The plsp_id is only 20 bits */ +#define MAX_LSP_STATUS 0x0007 /* The status is only 3 bits */ +#define OBJECT_LSP_FLAG_D 0x01 +#define OBJECT_LSP_FLAG_S 0x02 +#define OBJECT_LSP_FLAG_R 0x04 +#define OBJECT_LSP_FLAG_A 0x08 +#define OBJECT_LSP_FLAG_C 0x80 + +struct pcep_object_lsp { + struct pcep_object_header header; + uint32_t plsp_id; /* plsp_id is 20 bits, must be <= MAX_PLSP_ID*/ + enum pcep_lsp_operational_status operational_status; /* max 3 bits */ + bool flag_d; + bool flag_s; + bool flag_r; + bool flag_a; + bool flag_c; +}; + +#define ENTERPRISE_NUMBER_CISCO 9 +#define ENTERPRISE_COLOR_CISCO 65540 +/* RFC 7470 */ +struct pcep_object_vendor_info { + struct pcep_object_header header; + uint32_t enterprise_number; + uint32_t enterprise_specific_info; + uint32_t enterprise_specific_info1; /* cisco sends color for PcInit */ + uint32_t enterprise_specific_info2; + uint32_t enterprise_specific_info3; +}; + +/* RFC 8282 */ +#define OBJECT_INTER_LAYER_FLAG_I 0x01 +#define OBJECT_INTER_LAYER_FLAG_M 0x02 +#define OBJECT_INTER_LAYER_FLAG_T 0x04 + +struct pcep_object_inter_layer { + struct pcep_object_header header; + bool flag_i; + bool flag_m; + bool flag_t; +}; + +/* RFC 8282 */ +#define OBJECT_SWITCH_LAYER_FLAG_I 0x01 +enum pcep_lsp_encoding_type { + /* Values taken from RFC 3471 as suggested by RFC 8282 */ + PCEP_LSP_ENC_PACKET = 1, + PCEP_LSP_ENC_ETHERNET = 2, + PCEP_LSP_ENC_PDH = 3, + PCEP_LSP_ENC_RESERVED4 = 4, + PCEP_LSP_ENC_SDH_SONET = 5, + PCEP_LSP_ENC_RESERVED6 = 6, + PCEP_LSP_ENC_DIG_WRAPPER = 7, + PCEP_LSP_ENC_LAMBDA = 8, + PCEP_LSP_ENC_FIBER = 9, + PCEP_LSP_ENC_RESERVED10 = 10, + PCEP_LSP_ENC_FIBER_CHAN = 11 +}; + +enum pcep_switching_capability { + /* Switching capability values taken from RFC 4203/3471 as suggested by + RFC 8282 */ + PCEP_SW_CAP_PSC1 = 1, /* Packet-Switch Capable-1 (PSC-1) */ + PCEP_SW_CAP_PSC2 = 2, + PCEP_SW_CAP_PSC3 = 3, + PCEP_SW_CAP_PSC4 = 4, + PCEP_SW_CAP_L2SC = 51, /* Layer-2 Switch Capable */ + PCEP_SW_CAP_TDM = 100, /* Time-Division-Multiplex Capable */ + PCEP_SW_CAP_LSC = 150, /* Lambda-Switch Capable */ + PCEP_SW_CAP_FSC = 200 /* Fiber-Switch Capable */ +}; + +struct pcep_object_switch_layer_row { + enum pcep_lsp_encoding_type lsp_encoding_type; + enum pcep_switching_capability switching_type; + bool flag_i; +}; + +struct pcep_object_switch_layer { + struct pcep_object_header header; + double_linked_list + *switch_layer_rows; /* list of struct + pcep_object_switch_layer_row */ +}; + +/* RFC 8282 + * Requested Adaptation capability */ + +struct pcep_object_req_adap_cap { + struct pcep_object_header header; + enum pcep_switching_capability switching_capability; + enum pcep_lsp_encoding_type encoding; +}; + +/* RFC 8282 */ + +struct pcep_object_server_indication { + struct pcep_object_header header; + enum pcep_switching_capability switching_capability; + enum pcep_lsp_encoding_type encoding; + /* This object is identical to req_adap_cap, except it allows TLVs */ +}; + +/* Objective Function Object: RFC 5541 */ + +struct pcep_object_objective_function { + struct pcep_object_header header; + uint16_t of_code; +}; + +/* + * Common Route Object sub-object definitions + * used by ERO, IRO, and RRO + */ + +/* Common Route Object sub-object types + * used by ERO, IRO, and RRO */ +enum pcep_ro_subobj_types { + RO_SUBOBJ_TYPE_IPV4 = 1, /* RFC 3209 */ + RO_SUBOBJ_TYPE_IPV6 = 2, /* RFC 3209 */ + RO_SUBOBJ_TYPE_LABEL = 3, /* RFC 3209 */ + RO_SUBOBJ_TYPE_UNNUM = 4, /* RFC 3477 */ + RO_SUBOBJ_TYPE_ASN = 32, /* RFC 3209, Section 4.3.3.4 */ + RO_SUBOBJ_TYPE_SR = 36, /* RFC 8408, draft-ietf-pce-segment-routing-16. + Type 5 for draft07 has been assigned to + something else. */ + RO_SUBOBJ_UNKNOWN +}; + +struct pcep_object_ro { + struct pcep_object_header header; + double_linked_list + *sub_objects; /* list of struct pcep_object_ro_subobj */ +}; + +struct pcep_object_ro_subobj { + bool flag_subobj_loose_hop; /* L subobj flag */ + enum pcep_ro_subobj_types ro_subobj_type; +}; + +#define OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT 0x01 + +struct pcep_ro_subobj_ipv4 { + struct pcep_object_ro_subobj ro_subobj; + struct in_addr ip_addr; + uint8_t prefix_length; + bool flag_local_protection; +}; + +struct pcep_ro_subobj_ipv6 { + struct pcep_object_ro_subobj ro_subobj; + struct in6_addr ip_addr; + uint8_t prefix_length; + bool flag_local_protection; +}; + +struct pcep_ro_subobj_unnum { + struct pcep_object_ro_subobj ro_subobj; + struct in_addr router_id; + uint32_t interface_id; +}; + +#define OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL 0x01 +struct pcep_ro_subobj_32label { + struct pcep_object_ro_subobj ro_subobj; + bool flag_global_label; + uint8_t class_type; /* label class-type (generalized label = 2) */ + uint32_t label; /* label supported */ +}; + +struct pcep_ro_subobj_asn { + struct pcep_object_ro_subobj ro_subobj; + uint16_t asn; /* Autonomous system number */ +}; + +/* The SR ERO and SR RRO subobjects are the same, except + * the SR-RRO does not have the L flag in the Type field. + * Defined in draft-ietf-pce-segment-routing-16 */ +enum pcep_sr_subobj_nai { + PCEP_SR_SUBOBJ_NAI_ABSENT = 0, + PCEP_SR_SUBOBJ_NAI_IPV4_NODE = 1, + PCEP_SR_SUBOBJ_NAI_IPV6_NODE = 2, + PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY = 3, + PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY = 4, + PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY = 5, + PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY = 6, + PCEP_SR_SUBOBJ_NAI_UNKNOWN +}; + +#define OBJECT_SUBOBJ_SR_FLAG_M 0x01 +#define OBJECT_SUBOBJ_SR_FLAG_C 0x02 +#define OBJECT_SUBOBJ_SR_FLAG_S 0x04 +#define OBJECT_SUBOBJ_SR_FLAG_F 0x08 + +struct pcep_ro_subobj_sr { + struct pcep_object_ro_subobj ro_subobj; + enum pcep_sr_subobj_nai nai_type; + bool flag_f; + bool flag_s; + bool flag_c; + bool flag_m; + + /* The SID and NAI are optional depending on the flags, + * and the NAI can be variable length */ + uint32_t sid; + double_linked_list + *nai_list; /* double linked list of in_addr or in6_addr */ +}; + +/* Macros to make a SID Label + * + * 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Label + | Label | TC |S| TTL | Stack + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Entry + */ +#define ENCODE_SR_ERO_SID(label_20bits, tc_3bits, stack_bottom_bit, ttl_8bits) \ + ((((label_20bits) << 12) & 0xfffff000) \ + | (((tc_3bits) << 9) & 0x00000e00) \ + | (((stack_bottom_bit) << 8) & 0x00000100) | ((ttl_8bits)&0xff)) +#define GET_SR_ERO_SID_LABEL(SID) ((SID & 0xfffff000) >> 12) +#define GET_SR_ERO_SID_TC(SID) ((SID & 0x00000e00) >> 9) +#define GET_SR_ERO_SID_S(SID) ((SID & 0x00000100) >> 8) +#define GET_SR_ERO_SID_TTL(SID) ((SID & 0x000000ff)) + +/* + * All created objects will be in Host byte order, except for IPs. + * All IP addresses are expected to be passed-in in Network byte order, + * and any objects received will have their IPs in Network byte order. + * The message containing the objects should be converted to Network byte order + * with pcep_encode_msg_header() before sending, which will also convert the + * Objects, TLVs, and sub-objects. + */ + +struct pcep_object_open *pcep_obj_create_open(uint8_t keepalive, + uint8_t deadtimer, uint8_t sid, + double_linked_list *tlv_list); +struct pcep_object_rp *pcep_obj_create_rp(uint8_t priority, bool flag_r, + bool flag_b, bool flag_s, + bool flag_of, uint32_t reqid, + double_linked_list *tlv_list); +struct pcep_object_notify * +pcep_obj_create_notify(enum pcep_notification_types notification_type, + enum pcep_notification_values notification_value); +struct pcep_object_nopath * +pcep_obj_create_nopath(uint8_t ni, bool flag_c, + enum pcep_nopath_tlv_err_codes error_code); +struct pcep_object_association_ipv4 * +pcep_obj_create_association_ipv4(bool r_flag, uint16_t association_type, + uint16_t association_id, struct in_addr src); +struct pcep_object_association_ipv6 * +pcep_obj_create_association_ipv6(bool r_flag, uint16_t association_type, + uint16_t association_id, struct in6_addr src); +struct pcep_object_endpoints_ipv4 * +pcep_obj_create_endpoint_ipv4(const struct in_addr *src_ipv4, + const struct in_addr *dst_ipv4); +struct pcep_object_endpoints_ipv6 * +pcep_obj_create_endpoint_ipv6(const struct in6_addr *src_ipv6, + const struct in6_addr *dst_ipv6); +struct pcep_object_bandwidth *pcep_obj_create_bandwidth(float bandwidth); +struct pcep_object_metric *pcep_obj_create_metric(enum pcep_metric_types type, + bool flag_b, bool flag_c, + float value); +struct pcep_object_lspa * +pcep_obj_create_lspa(uint32_t exclude_any, uint32_t include_any, + uint32_t include_all, uint8_t setup_priority, + uint8_t holding_priority, bool flag_local_protection); +struct pcep_object_svec * +pcep_obj_create_svec(bool srlg, bool node, bool link, + double_linked_list *request_id_list); +struct pcep_object_error * +pcep_obj_create_error(enum pcep_error_type error_type, + enum pcep_error_value error_value); +struct pcep_object_close *pcep_obj_create_close(enum pcep_close_reason reason); +struct pcep_object_srp *pcep_obj_create_srp(bool lsp_remove, + uint32_t srp_id_number, + double_linked_list *tlv_list); +struct pcep_object_lsp * +pcep_obj_create_lsp(uint32_t plsp_id, enum pcep_lsp_operational_status status, + bool c_flag, bool a_flag, bool r_flag, bool s_flag, + bool d_flag, double_linked_list *tlv_list); +struct pcep_object_vendor_info * +pcep_obj_create_vendor_info(uint32_t enterprise_number, + uint32_t enterprise_spec_info); +struct pcep_object_inter_layer * +pcep_obj_create_inter_layer(bool flag_i, bool flag_m, bool flag_t); +struct pcep_object_switch_layer * +pcep_obj_create_switch_layer(double_linked_list *switch_layer_rows); +struct pcep_object_req_adap_cap * +pcep_obj_create_req_adap_cap(enum pcep_switching_capability sw_cap, + enum pcep_lsp_encoding_type encoding); +struct pcep_object_server_indication * +pcep_obj_create_server_indication(enum pcep_switching_capability sw_cap, + enum pcep_lsp_encoding_type encoding, + double_linked_list *tlv_list); +struct pcep_object_objective_function * +pcep_obj_create_objective_function(uint16_t of_code, + double_linked_list *tlv_list); + +/* Route Object (Explicit ero, Reported rro, and Include iro) functions + * First, the sub-objects should be created and appended to a + * double_linked_list, then call one of these Route Object creation functions + * with the subobj list */ +struct pcep_object_ro *pcep_obj_create_ero(double_linked_list *ero_list); +struct pcep_object_ro *pcep_obj_create_rro(double_linked_list *rro_list); +struct pcep_object_ro *pcep_obj_create_iro(double_linked_list *iro_list); +/* Route Object sub-object creation functions */ +struct pcep_ro_subobj_ipv4 * +pcep_obj_create_ro_subobj_ipv4(bool loose_hop, const struct in_addr *ro_ipv4, + uint8_t prefix_len, bool flag_local_prot); +struct pcep_ro_subobj_ipv6 * +pcep_obj_create_ro_subobj_ipv6(bool loose_hop, const struct in6_addr *ro_ipv6, + uint8_t prefix_len, bool flag_local_prot); +struct pcep_ro_subobj_unnum * +pcep_obj_create_ro_subobj_unnum(struct in_addr *router_id, uint32_t if_id); +struct pcep_ro_subobj_32label * +pcep_obj_create_ro_subobj_32label(bool flag_global_label, uint8_t class_type, + uint32_t label); +struct pcep_ro_subobj_asn *pcep_obj_create_ro_subobj_asn(uint16_t asn); + +/* SR ERO and SR RRO creation functions for different NAI (Node/Adj ID) types. + * - The loose_hop is only used for sr ero and must always be false for sr rro. + * - The NAI value will be set internally, depending on which function is used. + * m_flag: + * - If this flag is true, the SID value represents an MPLS label stack + * entry as specified in [RFC3032]. Otherwise, the SID value is an + * administratively configured value which represents an index into + * an MPLS label space (either SRGB or SRLB) per [RFC8402]. + * c_flag: + * - If the M flag and the C flag are both true, then the TC, S, and TTL + * fields in the MPLS label stack entry are specified by the PCE. However, + * a PCC MAY choose to override these values according to its local policy + * and MPLS forwarding rules. + * - If the M flag is true but the C flag is false, then the TC, S, and TTL + * fields MUST be ignored by the PCC. + * - The PCC MUST set these fields according to its local policy and MPLS + * forwarding rules. + * - If the M flag is false then the C bit MUST be false. */ +struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_nonai(bool loose_hop, + uint32_t sid, + bool c_flag, + bool m_flag); + +/* The ipv4_node_id will be copied internally */ +struct pcep_ro_subobj_sr * +pcep_obj_create_ro_subobj_sr_ipv4_node(bool loose_hop, bool sid_absent, + bool c_flag, bool m_flag, uint32_t sid, + struct in_addr *ipv4_node_id); +/* The ipv6_node_id will be copied internally */ +struct pcep_ro_subobj_sr * +pcep_obj_create_ro_subobj_sr_ipv6_node(bool loose_hop, bool sid_absent, + bool c_flag, bool m_flag, uint32_t sid, + struct in6_addr *ipv6_node_id); +/* The local_ipv4 and remote_ipv4 will be copied internally */ +struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv4_adj( + bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid, + struct in_addr *local_ipv4, struct in_addr *remote_ipv4); +/* The local_ipv6 and remote_ipv6 will be copied internally */ +struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_ipv6_adj( + bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid, + struct in6_addr *local_ipv6, struct in6_addr *remote_ipv6); +struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj( + bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid, + uint32_t local_node_id, uint32_t local_if_id, uint32_t remote_node_id, + uint32_t remote_if_id); +/* The local_ipv6 and remote_ipv6 will be copied internally */ +struct pcep_ro_subobj_sr *pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj( + bool loose_hop, bool sid_absent, bool c_flag, bool m_flag, uint32_t sid, + struct in6_addr *local_ipv6, uint32_t local_if_id, + struct in6_addr *remote_ipv6, uint32_t remote_if_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pceplib/pcep_msg_objects_encoding.c b/pceplib/pcep_msg_objects_encoding.c new file mode 100644 index 0000000..75eb112 --- /dev/null +++ b/pceplib/pcep_msg_objects_encoding.c @@ -0,0 +1,1731 @@ +/* + * 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> + * + */ + + +/* + * Encoding and decoding for PCEP Objects. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "pcep_msg_objects.h" +#include "pcep_msg_encoding.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +void write_object_header(struct pcep_object_header *object_hdr, + uint16_t object_length, uint8_t *buf); +void pcep_decode_object_hdr(const uint8_t *obj_buf, + struct pcep_object_header *obj_hdr); +void set_ro_subobj_fields(struct pcep_object_ro_subobj *subobj, bool flag_l, + uint8_t subobj_type); + +/* + * forward declarations for initialize_object_encoders() + */ +uint16_t pcep_encode_obj_open(struct pcep_object_header *obj, + struct pcep_versioning *versioning, uint8_t *buf); +uint16_t pcep_encode_obj_rp(struct pcep_object_header *obj, + struct pcep_versioning *versioning, uint8_t *buf); +uint16_t pcep_encode_obj_nopath(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_endpoints(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_association(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_bandwidth(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_metric(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_ro(struct pcep_object_header *obj, + struct pcep_versioning *versioning, uint8_t *buf); +uint16_t pcep_encode_obj_lspa(struct pcep_object_header *obj, + struct pcep_versioning *versioning, uint8_t *buf); +uint16_t pcep_encode_obj_svec(struct pcep_object_header *obj, + struct pcep_versioning *versioning, uint8_t *buf); +uint16_t pcep_encode_obj_notify(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_error(struct pcep_object_header *error, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_close(struct pcep_object_header *close, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_srp(struct pcep_object_header *obj, + struct pcep_versioning *versioning, uint8_t *buf); +uint16_t pcep_encode_obj_lsp(struct pcep_object_header *obj, + struct pcep_versioning *versioning, uint8_t *buf); +uint16_t pcep_encode_obj_vendor_info(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_inter_layer(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_switch_layer(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_req_adap_cap(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_server_ind(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +uint16_t pcep_encode_obj_objective_function(struct pcep_object_header *obj, + struct pcep_versioning *versioning, + uint8_t *buf); +typedef uint16_t (*object_encoder_funcptr)(struct pcep_object_header *, + struct pcep_versioning *versioning, + uint8_t *buf); + +#define MAX_OBJECT_ENCODER_INDEX 64 + +#define PCEP_ENCODERS_ARGS \ + struct pcep_object_header *, struct pcep_versioning *versioning, \ + uint8_t *buf +uint16_t (*const object_encoders[MAX_OBJECT_ENCODER_INDEX])( + PCEP_ENCODERS_ARGS) = { + [PCEP_OBJ_CLASS_OPEN] = pcep_encode_obj_open, + [PCEP_OBJ_CLASS_RP] = pcep_encode_obj_rp, + [PCEP_OBJ_CLASS_NOPATH] = pcep_encode_obj_nopath, + [PCEP_OBJ_CLASS_ENDPOINTS] = pcep_encode_obj_endpoints, + [PCEP_OBJ_CLASS_BANDWIDTH] = pcep_encode_obj_bandwidth, + [PCEP_OBJ_CLASS_METRIC] = pcep_encode_obj_metric, + [PCEP_OBJ_CLASS_ERO] = pcep_encode_obj_ro, + [PCEP_OBJ_CLASS_RRO] = pcep_encode_obj_ro, + [PCEP_OBJ_CLASS_LSPA] = pcep_encode_obj_lspa, + [PCEP_OBJ_CLASS_IRO] = pcep_encode_obj_ro, + [PCEP_OBJ_CLASS_SVEC] = pcep_encode_obj_svec, + [PCEP_OBJ_CLASS_NOTF] = pcep_encode_obj_notify, + [PCEP_OBJ_CLASS_ERROR] = pcep_encode_obj_error, + [PCEP_OBJ_CLASS_CLOSE] = pcep_encode_obj_close, + [PCEP_OBJ_CLASS_LSP] = pcep_encode_obj_lsp, + [PCEP_OBJ_CLASS_SRP] = pcep_encode_obj_srp, + [PCEP_OBJ_CLASS_ASSOCIATION] = pcep_encode_obj_association, + [PCEP_OBJ_CLASS_INTER_LAYER] = pcep_encode_obj_inter_layer, + [PCEP_OBJ_CLASS_SWITCH_LAYER] = pcep_encode_obj_switch_layer, + [PCEP_OBJ_CLASS_REQ_ADAP_CAP] = pcep_encode_obj_req_adap_cap, + [PCEP_OBJ_CLASS_SERVER_IND] = pcep_encode_obj_server_ind, + [PCEP_OBJ_CLASS_VENDOR_INFO] = pcep_encode_obj_vendor_info, + [PCEP_OBJ_CLASS_OF] = pcep_encode_obj_objective_function, +}; +/* + * forward declarations for initialize_object_decoders() + */ +struct pcep_object_header *pcep_decode_obj_open(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header *pcep_decode_obj_rp(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_nopath(struct pcep_object_header *hdr, const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_endpoints(struct pcep_object_header *hdr, const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_association(struct pcep_object_header *hdr, const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_bandwidth(struct pcep_object_header *hdr, const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_metric(struct pcep_object_header *hdr, const uint8_t *buf); +struct pcep_object_header *pcep_decode_obj_ro(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header *pcep_decode_obj_lspa(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header *pcep_decode_obj_svec(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_notify(struct pcep_object_header *hdr, const uint8_t *buf); +struct pcep_object_header *pcep_decode_obj_error(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header *pcep_decode_obj_close(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header *pcep_decode_obj_srp(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header *pcep_decode_obj_lsp(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_vendor_info(struct pcep_object_header *hdr, const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_inter_layer(struct pcep_object_header *hdr, const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_switch_layer(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_req_adap_cap(struct pcep_object_header *hdr, + const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_server_ind(struct pcep_object_header *hdr, const uint8_t *buf); +struct pcep_object_header * +pcep_decode_obj_objective_function(struct pcep_object_header *hdr, + const uint8_t *buf); +typedef struct pcep_object_header *(*object_decoder_funcptr)( + struct pcep_object_header *, const uint8_t *buf); + +#define PCEP_DECODERS_ARGS struct pcep_object_header *, const uint8_t *buf + +struct pcep_object_header *(*const object_decoders[MAX_OBJECT_ENCODER_INDEX])( + PCEP_DECODERS_ARGS) = { + [PCEP_OBJ_CLASS_OPEN] = pcep_decode_obj_open, + [PCEP_OBJ_CLASS_RP] = pcep_decode_obj_rp, + [PCEP_OBJ_CLASS_NOPATH] = pcep_decode_obj_nopath, + [PCEP_OBJ_CLASS_ENDPOINTS] = pcep_decode_obj_endpoints, + [PCEP_OBJ_CLASS_BANDWIDTH] = pcep_decode_obj_bandwidth, + [PCEP_OBJ_CLASS_METRIC] = pcep_decode_obj_metric, + [PCEP_OBJ_CLASS_ERO] = pcep_decode_obj_ro, + [PCEP_OBJ_CLASS_RRO] = pcep_decode_obj_ro, + [PCEP_OBJ_CLASS_LSPA] = pcep_decode_obj_lspa, + [PCEP_OBJ_CLASS_IRO] = pcep_decode_obj_ro, + [PCEP_OBJ_CLASS_SVEC] = pcep_decode_obj_svec, + [PCEP_OBJ_CLASS_NOTF] = pcep_decode_obj_notify, + [PCEP_OBJ_CLASS_ERROR] = pcep_decode_obj_error, + [PCEP_OBJ_CLASS_CLOSE] = pcep_decode_obj_close, + [PCEP_OBJ_CLASS_LSP] = pcep_decode_obj_lsp, + [PCEP_OBJ_CLASS_SRP] = pcep_decode_obj_srp, + [PCEP_OBJ_CLASS_ASSOCIATION] = pcep_decode_obj_association, + [PCEP_OBJ_CLASS_INTER_LAYER] = pcep_decode_obj_inter_layer, + [PCEP_OBJ_CLASS_SWITCH_LAYER] = pcep_decode_obj_switch_layer, + [PCEP_OBJ_CLASS_REQ_ADAP_CAP] = pcep_decode_obj_req_adap_cap, + [PCEP_OBJ_CLASS_SERVER_IND] = pcep_decode_obj_server_ind, + [PCEP_OBJ_CLASS_VENDOR_INFO] = pcep_decode_obj_vendor_info, + [PCEP_OBJ_CLASS_OF] = pcep_decode_obj_objective_function, +}; + +/* Object lengths, including the Object Header. + * Used by pcep_object_get_length() and pcep_object_has_tlvs() */ +static uint8_t pcep_object_class_lengths[] = { + 0, /* Object class 0 unused */ + 8, /* PCEP_OBJ_CLASS_OPEN = 1 */ + 12, /* PCEP_OBJ_CLASS_RP = 2 */ + 16, /* PCEP_OBJ_CLASS_NOPATH = 3, includes 8 for mandatory TLV */ + 0, /* PCEP_OBJ_CLASS_ENDPOINTS = 4, could be ipv4 or ipv6, setting to 0 + */ + 8, /* PCEP_OBJ_CLASS_BANDWIDTH = 5 */ + 12, /* PCEP_OBJ_CLASS_METRIC = 6 */ + 0, /* PCEP_OBJ_CLASS_ERO = 7, setting 0, ROs cannot have TLVs */ + 0, /* PCEP_OBJ_CLASS_RRO = 8, setting 0, ROs cannot have TLVs */ + 20, /* PCEP_OBJ_CLASS_LSPA = 9 */ + 0, /* PCEP_OBJ_CLASS_IRO = 10, setting 0, ROs cannot have TLVs */ + 0, /* PCEP_OBJ_CLASS_SVEC = 11, SVECs cannot have TLVs */ + 8, /* PCEP_OBJ_CLASS_NOTF = 12 */ + 8, /* PCEP_OBJ_CLASS_ERROR = 13 */ + 0, /* Object class 14 unused */ + 8, /* PCEP_OBJ_CLASS_CLOSE = 15 */ + 0, 0, 0, 0, 0, /* Object classes 16 - 20 are not used */ + 8, /* PCEP_OBJ_CLASS_OF = 21 */ + 0, 0, 0, 0, 0, /* Object classes 22 - 26 are not used */ + 0, 0, 0, 0, 0, /* Object classes 27 - 31 are not used */ + 8, /* PCEP_OBJ_CLASS_LSP = 32 */ + 12, /* PCEP_OBJ_CLASS_SRP = 33 */ + 12, /* PCEP_OBJ_CLASS_VENDOR_INFO = 34 */ + 0, /* Object class 35 unused */ + 0, /* PCEP_OBJ_CLASS_INTER_LAYER = 36, cannot have TLVs */ + 0, /* PCEP_OBJ_CLASS_SWITCH_LAYER = 37, cannot have TLVs */ + 0, /* PCEP_OBJ_CLASS_REQ_ADAP_CAP = 38, cannot have TLVs*/ + 8, /* PCEP_OBJ_CLASS_SERVER_IND = 39 */ + 0, /* PCEP_OBJ_CLASS_ASSOCIATION = 40, cannot have TLVs */ +}; + +/* + * The TLVs can have strange length values, since they do not include padding in + * the TLV header length, but that extra padding must be taken into account by + * the enclosing object by rounding up to the next 4 byte boundary. + * Example returned lengths: + * normalize_length(4) = 4, normalize_length(5) = 8, normalize_length(6) + * = 8, normalize_length(7) = 8, normalize_length(8) = 8 + * normalize_length(9) = 12, normalize_length(10) = 12, normalize_length(11) = + * 12, normalize_length(12) = 12, normalize_length(13) = 13... + */ +uint16_t normalize_pcep_tlv_length(uint16_t length) +{ + return (length % 4 == 0) ? length : (length + (4 - (length % 4))); +} + +/* + * Encoding functions + */ +uint16_t pcep_encode_object(struct pcep_object_header *object_hdr, + struct pcep_versioning *versioning, uint8_t *buf) +{ + + if (object_hdr->object_class >= MAX_OBJECT_ENCODER_INDEX) { + pcep_log(LOG_INFO, + "%s: Cannot encode unknown Object class [%d]", + __func__, object_hdr->object_class); + return 0; + } + + object_encoder_funcptr obj_encoder = + object_encoders[object_hdr->object_class]; + if (obj_encoder == NULL) { + pcep_log(LOG_INFO, + "%s: No object encoder found for Object class [%d]", + __func__, object_hdr->object_class); + return 0; + } + + uint16_t object_length = OBJECT_HEADER_LENGTH + + obj_encoder(object_hdr, versioning, + buf + OBJECT_HEADER_LENGTH); + double_linked_list_node *node = + (object_hdr->tlv_list == NULL ? NULL + : object_hdr->tlv_list->head); + for (; node != NULL; node = node->next_node) { + /* Returns the length of the TLV, including the TLV header */ + object_length += pcep_encode_tlv( + (struct pcep_object_tlv_header *)node->data, versioning, + buf + object_length); + } + object_length = normalize_pcep_tlv_length(object_length); + write_object_header(object_hdr, object_length, buf); + object_hdr->encoded_object = buf; + object_hdr->encoded_object_length = object_length; + + return object_length; +} + + +/* Object Header + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Object-Class | OT |Res|P|I| Object Length (bytes) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | + * // (Object body) // + * | | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + +void write_object_header(struct pcep_object_header *object_hdr, + uint16_t object_length, uint8_t *buf) +{ + buf[0] = object_hdr->object_class; + buf[1] = ((object_hdr->object_type << 4) + | (object_hdr->flag_p ? OBJECT_HEADER_FLAG_P : 0x00) + | (object_hdr->flag_i ? OBJECT_HEADER_FLAG_I : 0x00)); + uint16_t net_order_length = htons(object_length); + memcpy(buf + 2, &net_order_length, sizeof(net_order_length)); +} + + +/* + * Functions to encode objects + * - they will be passed a pointer to a buffer to write the object body, + * which is past the object header. + * - they should return the object body length, not including the object header + * length. + */ + +uint16_t pcep_encode_obj_open(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_open *open = (struct pcep_object_open *)hdr; + obj_body_buf[0] = (open->open_version << 5) & 0xe0; + obj_body_buf[1] = open->open_keepalive; + obj_body_buf[2] = open->open_deadtimer; + obj_body_buf[3] = open->open_sid; + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_rp(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_rp *rp = (struct pcep_object_rp *)hdr; + obj_body_buf[3] = ((rp->flag_strict ? OBJECT_RP_FLAG_O : 0x00) + | (rp->flag_bidirectional ? OBJECT_RP_FLAG_B : 0x00) + | (rp->flag_reoptimization ? OBJECT_RP_FLAG_R : 0x00) + | (rp->flag_of ? OBJECT_RP_FLAG_OF : 0x00) + | (rp->priority & 0x07)); + uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + 4); + *uint32_ptr = htonl(rp->request_id); + + return LENGTH_2WORDS; +} + +uint16_t pcep_encode_obj_notify(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_notify *notify = (struct pcep_object_notify *)hdr; + obj_body_buf[2] = notify->notification_type; + obj_body_buf[3] = notify->notification_value; + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_nopath(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_nopath *nopath = (struct pcep_object_nopath *)hdr; + obj_body_buf[0] = nopath->ni; + obj_body_buf[1] = ((nopath->flag_c) ? OBJECT_NOPATH_FLAG_C : 0x00); + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_association(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + uint16_t *uint16_ptr = (uint16_t *)obj_body_buf; + uint32_t *uint32_ptr = (uint32_t *)obj_body_buf; + if (hdr->object_type == PCEP_OBJ_TYPE_ASSOCIATION_IPV4) { + struct pcep_object_association_ipv4 *ipv4 = + (struct pcep_object_association_ipv4 *)hdr; + obj_body_buf[3] = + (ipv4->R_flag ? OBJECT_ASSOCIATION_FLAG_R : 0x00); + uint16_ptr[2] = htons(ipv4->association_type); + uint16_ptr[3] = htons(ipv4->association_id); + uint32_ptr[2] = ipv4->src.s_addr; + + return LENGTH_3WORDS; + } else { + struct pcep_object_association_ipv6 *ipv6 = + (struct pcep_object_association_ipv6 *)hdr; + obj_body_buf[3] = + (ipv6->R_flag ? OBJECT_ASSOCIATION_FLAG_R : 0x00); + uint16_ptr[2] = htons(ipv6->association_type); + uint16_ptr[3] = htons(ipv6->association_id); + memcpy(uint32_ptr, &ipv6->src, sizeof(struct in6_addr)); + + return LENGTH_6WORDS; + } +} + +uint16_t pcep_encode_obj_endpoints(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + uint32_t *uint32_ptr = (uint32_t *)obj_body_buf; + if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV4) { + struct pcep_object_endpoints_ipv4 *ipv4 = + (struct pcep_object_endpoints_ipv4 *)hdr; + uint32_ptr[0] = ipv4->src_ipv4.s_addr; + uint32_ptr[1] = ipv4->dst_ipv4.s_addr; + + return LENGTH_2WORDS; + } else { + struct pcep_object_endpoints_ipv6 *ipv6 = + (struct pcep_object_endpoints_ipv6 *)hdr; + memcpy(uint32_ptr, &ipv6->src_ipv6, sizeof(struct in6_addr)); + memcpy(&uint32_ptr[4], &ipv6->dst_ipv6, + sizeof(struct in6_addr)); + + return LENGTH_8WORDS; + } +} + +uint16_t pcep_encode_obj_bandwidth(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_bandwidth *bandwidth = + (struct pcep_object_bandwidth *)hdr; + uint32_t *uint32_ptr = (uint32_t *)obj_body_buf; + /* Seems like the compiler doesn't correctly copy the float, so memcpy() + * it */ + memcpy(uint32_ptr, &(bandwidth->bandwidth), sizeof(uint32_t)); + *uint32_ptr = htonl(*uint32_ptr); + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_metric(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_metric *metric = (struct pcep_object_metric *)hdr; + obj_body_buf[2] = ((metric->flag_c ? OBJECT_METRIC_FLAC_C : 0x00) + | (metric->flag_b ? OBJECT_METRIC_FLAC_B : 0x00)); + obj_body_buf[3] = metric->type; + uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + 4); + /* Seems like the compiler doesn't correctly copy the float, so memcpy() + * it */ + memcpy(uint32_ptr, &(metric->value), sizeof(uint32_t)); + *uint32_ptr = htonl(*uint32_ptr); + + return LENGTH_2WORDS; +} + +uint16_t pcep_encode_obj_lspa(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_lspa *lspa = (struct pcep_object_lspa *)hdr; + uint32_t *uint32_ptr = (uint32_t *)obj_body_buf; + uint32_ptr[0] = htonl(lspa->lspa_exclude_any); + uint32_ptr[1] = htonl(lspa->lspa_include_any); + uint32_ptr[2] = htonl(lspa->lspa_include_all); + obj_body_buf[12] = lspa->setup_priority; + obj_body_buf[13] = lspa->holding_priority; + obj_body_buf[14] = + (lspa->flag_local_protection ? OBJECT_LSPA_FLAG_L : 0x00); + + return LENGTH_4WORDS; +} + +uint16_t pcep_encode_obj_svec(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_svec *svec = (struct pcep_object_svec *)hdr; + obj_body_buf[3] = + ((svec->flag_srlg_diverse ? OBJECT_SVEC_FLAG_S : 0x00) + | (svec->flag_node_diverse ? OBJECT_SVEC_FLAG_N : 0x00) + | (svec->flag_link_diverse ? OBJECT_SVEC_FLAG_L : 0x00)); + + if (svec->request_id_list == NULL) { + return LENGTH_1WORD; + } + + int index = 1; + uint32_t *uint32_ptr = (uint32_t *)obj_body_buf; + double_linked_list_node *node = svec->request_id_list->head; + for (; node != NULL; node = node->next_node) { + uint32_ptr[index++] = htonl(*((uint32_t *)(node->data))); + } + + return LENGTH_1WORD + + (svec->request_id_list->num_entries * sizeof(uint32_t)); +} + +uint16_t pcep_encode_obj_error(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_error *error = (struct pcep_object_error *)hdr; + obj_body_buf[2] = error->error_type; + obj_body_buf[3] = error->error_value; + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_close(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_close *close = (struct pcep_object_close *)hdr; + obj_body_buf[3] = close->reason; + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_srp(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_srp *srp = (struct pcep_object_srp *)hdr; + obj_body_buf[3] = (srp->flag_lsp_remove ? OBJECT_SRP_FLAG_R : 0x00); + uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + 4); + *uint32_ptr = htonl(srp->srp_id_number); + + return LENGTH_2WORDS; +} + +uint16_t pcep_encode_obj_lsp(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)hdr; + uint32_t *uint32_ptr = (uint32_t *)obj_body_buf; + uint32_ptr[0] = htonl((lsp->plsp_id << 12) & 0xfffff000); + obj_body_buf[3] = ((lsp->flag_c ? OBJECT_LSP_FLAG_C : 0x00) + | ((lsp->operational_status << 4) & 0x70) + | (lsp->flag_a ? OBJECT_LSP_FLAG_A : 0x00) + | (lsp->flag_r ? OBJECT_LSP_FLAG_R : 0x00) + | (lsp->flag_s ? OBJECT_LSP_FLAG_S : 0x00) + | (lsp->flag_d ? OBJECT_LSP_FLAG_D : 0x00)); + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_vendor_info(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_vendor_info *obj = + (struct pcep_object_vendor_info *)hdr; + uint32_t *uint32_ptr = (uint32_t *)obj_body_buf; + uint32_ptr[0] = htonl(obj->enterprise_number); + uint32_ptr[1] = htonl(obj->enterprise_specific_info); + + return LENGTH_2WORDS; +} + +uint16_t pcep_encode_obj_inter_layer(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_inter_layer *obj = + (struct pcep_object_inter_layer *)hdr; + obj_body_buf[3] = ((obj->flag_i ? OBJECT_INTER_LAYER_FLAG_I : 0x00) + | (obj->flag_m ? OBJECT_INTER_LAYER_FLAG_M : 0x00) + | (obj->flag_t ? OBJECT_INTER_LAYER_FLAG_T : 0x00)); + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_switch_layer(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_switch_layer *obj = + (struct pcep_object_switch_layer *)hdr; + uint8_t buf_index = 0; + + double_linked_list_node *node = obj->switch_layer_rows->head; + while (node != NULL) { + struct pcep_object_switch_layer_row *row = node->data; + if (row == NULL) { + break; + } + + obj_body_buf[buf_index] = row->lsp_encoding_type; + obj_body_buf[buf_index + 1] = row->switching_type; + obj_body_buf[buf_index + 3] = + (row->flag_i ? OBJECT_SWITCH_LAYER_FLAG_I : 0x00); + + buf_index += LENGTH_1WORD; + } + + return buf_index; +} + +uint16_t pcep_encode_obj_req_adap_cap(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_req_adap_cap *obj = + (struct pcep_object_req_adap_cap *)hdr; + + obj_body_buf[0] = obj->switching_capability; + obj_body_buf[1] = obj->encoding; + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_server_ind(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_server_indication *obj = + (struct pcep_object_server_indication *)hdr; + + obj_body_buf[0] = obj->switching_capability; + obj_body_buf[1] = obj->encoding; + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_objective_function(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_objective_function *obj = + (struct pcep_object_objective_function *)hdr; + + uint16_t *uint16_ptr = (uint16_t *)obj_body_buf; + *uint16_ptr = htons(obj->of_code); + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_obj_ro(struct pcep_object_header *hdr, + struct pcep_versioning *versioning, + uint8_t *obj_body_buf) +{ + (void)versioning; + struct pcep_object_ro *ro = (struct pcep_object_ro *)hdr; + if (ro == NULL || ro->sub_objects == NULL) { + return 0; + } + + /* RO Subobject format + * + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+ + * |L| Type | Length | (Subobject contents) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+ + */ + + uint16_t index = 0; + double_linked_list_node *node = ro->sub_objects->head; + for (; node != NULL; node = node->next_node) { + struct pcep_object_ro_subobj *ro_subobj = node->data; + obj_body_buf[index++] = + ((ro_subobj->flag_subobj_loose_hop ? 0x80 : 0x00) + | (ro_subobj->ro_subobj_type)); + /* The length will be written below, depending on the subobj + * type */ + uint8_t *length_ptr = &(obj_body_buf[index++]); + uint32_t *uint32_ptr = (uint32_t *)(obj_body_buf + index); + + /* - The index has already been incremented past the header, + * and now points to the ro_subobj body. Below it just needs + * to be incremented past the body. + * + * - Each section below needs to write the total length, + * including the 2 byte subobj header. */ + + switch (ro_subobj->ro_subobj_type) { + case RO_SUBOBJ_TYPE_IPV4: { + struct pcep_ro_subobj_ipv4 *ipv4 = + (struct pcep_ro_subobj_ipv4 *)ro_subobj; + uint32_ptr[0] = ipv4->ip_addr.s_addr; + index += LENGTH_1WORD; + obj_body_buf[index++] = ipv4->prefix_length; + obj_body_buf[index++] = + (ipv4->flag_local_protection + ? OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT + : 0x00); + *length_ptr = LENGTH_2WORDS; + } break; + + case RO_SUBOBJ_TYPE_IPV6: { + struct pcep_ro_subobj_ipv6 *ipv6 = + (struct pcep_ro_subobj_ipv6 *)ro_subobj; + encode_ipv6(&ipv6->ip_addr, uint32_ptr); + index += LENGTH_4WORDS; + obj_body_buf[index++] = ipv6->prefix_length; + obj_body_buf[index++] = + (ipv6->flag_local_protection + ? OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT + : 0x00); + *length_ptr = LENGTH_5WORDS; + } break; + + case RO_SUBOBJ_TYPE_LABEL: { + struct pcep_ro_subobj_32label *label = + (struct pcep_ro_subobj_32label *)ro_subobj; + obj_body_buf[index++] = + (label->flag_global_label + ? OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL + : 0x00); + obj_body_buf[index++] = label->class_type; + uint32_ptr = (uint32_t *)(obj_body_buf + index); + *uint32_ptr = htonl(label->label); + *length_ptr = LENGTH_2WORDS; + index += LENGTH_1WORD; + } break; + + case RO_SUBOBJ_TYPE_UNNUM: { + struct pcep_ro_subobj_unnum *unum = + (struct pcep_ro_subobj_unnum *)ro_subobj; + index += 2; /* increment past 2 reserved bytes */ + uint32_ptr = (uint32_t *)(obj_body_buf + index); + uint32_ptr[0] = unum->router_id.s_addr; + uint32_ptr[1] = htonl(unum->interface_id); + *length_ptr = LENGTH_3WORDS; + index += LENGTH_2WORDS; + } break; + + case RO_SUBOBJ_TYPE_ASN: { + struct pcep_ro_subobj_asn *asn = + (struct pcep_ro_subobj_asn *)ro_subobj; + uint16_t *uint16_ptr = + (uint16_t *)(obj_body_buf + index); + *uint16_ptr = htons(asn->asn); + *length_ptr = LENGTH_1WORD; + index += 2; + } break; + + case RO_SUBOBJ_TYPE_SR: { + /* SR-ERO subobject format + * + * 0 1 2 3 0 1 2 3 4 + * 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |L| Type=36 | Length | NT | Flags + * |F|S|C|M| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SID (optional) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * // NAI (variable, optional) // + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + struct pcep_ro_subobj_sr *sr_subobj = + (struct pcep_ro_subobj_sr *)ro_subobj; + obj_body_buf[index++] = + ((sr_subobj->nai_type << 4) & 0xf0); + obj_body_buf[index++] = + ((sr_subobj->flag_f ? OBJECT_SUBOBJ_SR_FLAG_F + : 0x00) + | (sr_subobj->flag_s ? OBJECT_SUBOBJ_SR_FLAG_S + : 0x00) + | (sr_subobj->flag_c ? OBJECT_SUBOBJ_SR_FLAG_C + : 0x00) + | (sr_subobj->flag_m ? OBJECT_SUBOBJ_SR_FLAG_M + : 0x00)); + uint32_ptr = (uint32_t *)(obj_body_buf + index); + /* Start with LENGTH_1WORD for the SubObj HDR + NT + + * Flags */ + uint8_t sr_base_length = LENGTH_1WORD; + /* If the sid_absent flag is true, then dont convert the + * sid */ + if (sr_subobj->flag_s == false) { + uint32_ptr[0] = htonl(sr_subobj->sid); + index += LENGTH_1WORD; + uint32_ptr = (uint32_t *)(obj_body_buf + index); + sr_base_length += LENGTH_1WORD; + } + + /* The lengths below need to include: + * - sr_base_length: set above to include SR SubObj Hdr + * and the SID if present + * - Number of bytes written to the NAI + * The index will only be incremented below by the + * number of bytes written to the NAI, since the RO SR + * subobj header and the SID have already been written. + */ + + double_linked_list_node *nai_node = + (sr_subobj->nai_list == NULL + ? NULL + : sr_subobj->nai_list->head); + if (nai_node == NULL) { + if (sr_subobj->nai_type + == PCEP_SR_SUBOBJ_NAI_ABSENT) { + *length_ptr = sr_base_length; + continue; + } else { + return 0; + } + } + switch (sr_subobj->nai_type) { + case PCEP_SR_SUBOBJ_NAI_IPV4_NODE: + uint32_ptr[0] = + ((struct in_addr *)nai_node->data) + ->s_addr; + *length_ptr = sr_base_length + LENGTH_1WORD; + index += LENGTH_1WORD; + break; + + case PCEP_SR_SUBOBJ_NAI_IPV6_NODE: + encode_ipv6((struct in6_addr *)nai_node->data, + uint32_ptr); + *length_ptr = sr_base_length + LENGTH_4WORDS; + index += LENGTH_4WORDS; + break; + + case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: + uint32_ptr[0] = + ((struct in_addr *)nai_node->data) + ->s_addr; + nai_node = nai_node->next_node; + uint32_ptr[1] = + ((struct in_addr *)nai_node->data) + ->s_addr; + nai_node = nai_node->next_node; + uint32_ptr[2] = + ((struct in_addr *)nai_node->data) + ->s_addr; + nai_node = nai_node->next_node; + uint32_ptr[3] = + ((struct in_addr *)nai_node->data) + ->s_addr; + *length_ptr = sr_base_length + LENGTH_4WORDS; + index += LENGTH_4WORDS; + break; + + case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY: + uint32_ptr[0] = + ((struct in_addr *)nai_node->data) + ->s_addr; + nai_node = nai_node->next_node; + uint32_ptr[1] = + ((struct in_addr *)nai_node->data) + ->s_addr; + *length_ptr = sr_base_length + LENGTH_2WORDS; + index += LENGTH_2WORDS; + break; + + case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY: + encode_ipv6((struct in6_addr *)nai_node->data, + uint32_ptr); + nai_node = nai_node->next_node; + encode_ipv6((struct in6_addr *)nai_node->data, + uint32_ptr + 4); + *length_ptr = sr_base_length + LENGTH_8WORDS; + index += LENGTH_8WORDS; + break; + + case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY: + encode_ipv6((struct in6_addr *)nai_node->data, + uint32_ptr); + nai_node = nai_node->next_node; + uint32_ptr[4] = + ((struct in_addr *)nai_node->data) + ->s_addr; + nai_node = nai_node->next_node; + encode_ipv6((struct in6_addr *)nai_node->data, + uint32_ptr + 5); + nai_node = nai_node->next_node; + uint32_ptr[9] = + ((struct in_addr *)nai_node->data) + ->s_addr; + *length_ptr = sr_base_length + LENGTH_10WORDS; + index += LENGTH_10WORDS; + break; + + default: + break; + } + } break; + + default: + break; + } + } + + return index; +} + +void encode_ipv6(struct in6_addr *src_ipv6, uint32_t *dst) +{ + memcpy(dst, src_ipv6, sizeof(struct in6_addr)); +} + +/* + * Decoding functions. + */ + +void pcep_decode_object_hdr(const uint8_t *obj_buf, + struct pcep_object_header *obj_hdr) +{ + memset(obj_hdr, 0, sizeof(struct pcep_object_header)); + + obj_hdr->object_class = obj_buf[0]; + obj_hdr->object_type = (obj_buf[1] >> 4) & 0x0f; + obj_hdr->flag_p = (obj_buf[1] & OBJECT_HEADER_FLAG_P); + obj_hdr->flag_i = (obj_buf[1] & OBJECT_HEADER_FLAG_I); + uint16_t net_order_length; + memcpy(&net_order_length, obj_buf + 2, sizeof(net_order_length)); + obj_hdr->encoded_object_length = ntohs(net_order_length); + obj_hdr->encoded_object = obj_buf; +} + +uint16_t pcep_object_get_length(enum pcep_object_classes object_class, + enum pcep_object_types object_type) +{ + uint8_t object_length = pcep_object_class_lengths[object_class]; + if (object_length == 0) { + if (object_class == PCEP_OBJ_CLASS_ENDPOINTS) { + if (object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV4) { + return 12; + } else if (object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV6) { + return 36; + } + } + + return 0; + } + + return object_length; +} + +uint16_t pcep_object_get_length_by_hdr(struct pcep_object_header *object_hdr) +{ + return (pcep_object_get_length(object_hdr->object_class, + object_hdr->object_type)); +} + +bool pcep_object_has_tlvs(struct pcep_object_header *object_hdr) +{ + uint8_t object_length = pcep_object_get_length_by_hdr(object_hdr); + if (object_length == 0) { + return false; + } + + return (object_hdr->encoded_object_length - object_length) > 0; +} + +struct pcep_object_header *pcep_decode_object(const uint8_t *obj_buf) +{ + + struct pcep_object_header object_hdr; + /* Only initializes and decodes the Object Header: class, type, flags, + * and length */ + pcep_decode_object_hdr(obj_buf, &object_hdr); + + if (object_hdr.object_class >= MAX_OBJECT_ENCODER_INDEX) { + pcep_log(LOG_INFO, + "%s: Cannot decode unknown Object class [%d]", + __func__, object_hdr.object_class); + return NULL; + } + + object_decoder_funcptr obj_decoder = + object_decoders[object_hdr.object_class]; + if (obj_decoder == NULL) { + pcep_log(LOG_INFO, + "%s: No object decoder found for Object class [%d]", + __func__, object_hdr.object_class); + return NULL; + } + + /* The object decoders will start decoding the object body, if + * anything from the header is needed, they have the object_hdr */ + struct pcep_object_header *object = + obj_decoder(&object_hdr, obj_buf + OBJECT_HEADER_LENGTH); + if (object == NULL) { + pcep_log(LOG_INFO, "%s: Unable to decode Object class [%d].", + __func__, object_hdr.object_class); + return NULL; + } + + if (pcep_object_has_tlvs(&object_hdr)) { + object->tlv_list = dll_initialize(); + int num_iterations = 0; + uint16_t tlv_index = pcep_object_get_length_by_hdr(&object_hdr); + while ((object->encoded_object_length - tlv_index) > 0 + && num_iterations++ < MAX_ITERATIONS) { + struct pcep_object_tlv_header *tlv = + pcep_decode_tlv(obj_buf + tlv_index); + if (tlv == NULL) { + /* TODO should we do anything else here ? */ + return object; + } + + /* The TLV length does not include the TLV header */ + tlv_index += normalize_pcep_tlv_length( + tlv->encoded_tlv_length + TLV_HEADER_LENGTH); + dll_append(object->tlv_list, tlv); + } + } + + return object; +} + +static struct pcep_object_header * +common_object_create(struct pcep_object_header *hdr, uint16_t new_obj_length) +{ + struct pcep_object_header *new_object = + pceplib_malloc(PCEPLIB_MESSAGES, new_obj_length); + memset(new_object, 0, new_obj_length); + memcpy(new_object, hdr, sizeof(struct pcep_object_header)); + + return new_object; +} + +/* + * Decoders + */ + +struct pcep_object_header *pcep_decode_obj_open(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_open *obj = + (struct pcep_object_open *)common_object_create( + hdr, sizeof(struct pcep_object_open)); + + obj->open_version = (obj_buf[0] >> 5) & 0x07; + obj->open_keepalive = obj_buf[1]; + obj->open_deadtimer = obj_buf[2]; + obj->open_sid = obj_buf[3]; + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header *pcep_decode_obj_rp(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_rp *obj = + (struct pcep_object_rp *)common_object_create( + hdr, sizeof(struct pcep_object_rp)); + + obj->flag_reoptimization = (obj_buf[3] & OBJECT_RP_FLAG_R); + obj->flag_bidirectional = (obj_buf[3] & OBJECT_RP_FLAG_B); + obj->flag_strict = (obj_buf[3] & OBJECT_RP_FLAG_O); + obj->flag_of = (obj_buf[3] & OBJECT_RP_FLAG_OF); + obj->priority = (obj_buf[3] & 0x07); + obj->request_id = ntohl(*((uint32_t *)(obj_buf + 4))); + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header * +pcep_decode_obj_notify(struct pcep_object_header *hdr, const uint8_t *obj_buf) +{ + struct pcep_object_notify *obj = + (struct pcep_object_notify *)common_object_create( + hdr, sizeof(struct pcep_object_notify)); + + obj->notification_type = obj_buf[2]; + obj->notification_value = obj_buf[3]; + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header * +pcep_decode_obj_nopath(struct pcep_object_header *hdr, const uint8_t *obj_buf) +{ + struct pcep_object_nopath *obj = + (struct pcep_object_nopath *)common_object_create( + hdr, sizeof(struct pcep_object_nopath)); + + obj->ni = (obj_buf[0] >> 1); + obj->flag_c = (obj_buf[0] & OBJECT_NOPATH_FLAG_C); + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header * +pcep_decode_obj_association(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + uint16_t *uint16_ptr = (uint16_t *)obj_buf; + uint32_t *uint32_ptr = (uint32_t *)obj_buf; + + if (hdr->object_type == PCEP_OBJ_TYPE_ASSOCIATION_IPV4) { + struct pcep_object_association_ipv4 *obj = + (struct pcep_object_association_ipv4 *) + common_object_create( + hdr, + sizeof(struct + pcep_object_association_ipv4)); + obj->R_flag = (obj_buf[3] & OBJECT_ASSOCIATION_FLAG_R); + obj->association_type = ntohs(uint16_ptr[2]); + obj->association_id = ntohs(uint16_ptr[3]); + obj->src.s_addr = uint32_ptr[2]; + + return (struct pcep_object_header *)obj; + } else if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV6) { + struct pcep_object_association_ipv6 *obj = + (struct pcep_object_association_ipv6 *) + common_object_create( + hdr, + sizeof(struct + pcep_object_association_ipv6)); + + obj->R_flag = (obj_buf[3] & OBJECT_ASSOCIATION_FLAG_R); + obj->association_type = ntohs(uint16_ptr[2]); + obj->association_id = ntohs(uint16_ptr[3]); + memcpy(&obj->src, &uint32_ptr[2], sizeof(struct in6_addr)); + + return (struct pcep_object_header *)obj; + } + + return NULL; +} +struct pcep_object_header * +pcep_decode_obj_endpoints(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + uint32_t *uint32_ptr = (uint32_t *)obj_buf; + + if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV4) { + struct pcep_object_endpoints_ipv4 *obj = + (struct pcep_object_endpoints_ipv4 *) + common_object_create( + hdr, + sizeof(struct + pcep_object_endpoints_ipv4)); + obj->src_ipv4.s_addr = uint32_ptr[0]; + obj->dst_ipv4.s_addr = uint32_ptr[1]; + + return (struct pcep_object_header *)obj; + } else if (hdr->object_type == PCEP_OBJ_TYPE_ENDPOINT_IPV6) { + struct pcep_object_endpoints_ipv6 *obj = + (struct pcep_object_endpoints_ipv6 *) + common_object_create( + hdr, + sizeof(struct + pcep_object_endpoints_ipv6)); + + memcpy(&obj->src_ipv6, &uint32_ptr[0], sizeof(struct in6_addr)); + memcpy(&obj->dst_ipv6, &uint32_ptr[4], sizeof(struct in6_addr)); + + return (struct pcep_object_header *)obj; + } + + return NULL; +} + +struct pcep_object_header * +pcep_decode_obj_bandwidth(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_bandwidth *obj = + (struct pcep_object_bandwidth *)common_object_create( + hdr, sizeof(struct pcep_object_bandwidth)); + + uint32_t value = ntohl(*((uint32_t *)obj_buf)); + /* Seems like the compiler doesn't correctly copy to the float, so + * memcpy() it */ + memcpy(&obj->bandwidth, &value, sizeof(uint32_t)); + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header * +pcep_decode_obj_metric(struct pcep_object_header *hdr, const uint8_t *obj_buf) +{ + struct pcep_object_metric *obj = + (struct pcep_object_metric *)common_object_create( + hdr, sizeof(struct pcep_object_metric)); + obj->flag_b = (obj_buf[2] & OBJECT_METRIC_FLAC_B); + obj->flag_c = (obj_buf[2] & OBJECT_METRIC_FLAC_C); + obj->type = obj_buf[3]; + uint32_t value = ntohl(*((uint32_t *)(obj_buf + 4))); + /* Seems like the compiler doesn't correctly copy to the float, so + * memcpy() it */ + memcpy(&obj->value, &value, sizeof(uint32_t)); + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header *pcep_decode_obj_lspa(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_lspa *obj = + (struct pcep_object_lspa *)common_object_create( + hdr, sizeof(struct pcep_object_lspa)); + uint32_t *uint32_ptr = (uint32_t *)obj_buf; + + obj->lspa_exclude_any = ntohl(uint32_ptr[0]); + obj->lspa_include_any = ntohl(uint32_ptr[1]); + obj->lspa_include_all = ntohl(uint32_ptr[2]); + obj->setup_priority = obj_buf[12]; + obj->holding_priority = obj_buf[13]; + obj->flag_local_protection = (obj_buf[14] & OBJECT_LSPA_FLAG_L); + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header *pcep_decode_obj_svec(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_svec *obj = + (struct pcep_object_svec *)common_object_create( + hdr, sizeof(struct pcep_object_svec)); + + obj->flag_link_diverse = (obj_buf[3] & OBJECT_SVEC_FLAG_L); + obj->flag_node_diverse = (obj_buf[3] & OBJECT_SVEC_FLAG_N); + obj->flag_srlg_diverse = (obj_buf[3] & OBJECT_SVEC_FLAG_S); + + if (hdr->encoded_object_length > LENGTH_2WORDS) { + obj->request_id_list = dll_initialize(); + uint16_t index = 1; + uint32_t *uint32_ptr = (uint32_t *)obj_buf; + for (; + index < ((hdr->encoded_object_length - LENGTH_2WORDS) / 4); + index++) { + uint32_t *req_id_ptr = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(uint32_t)); + *req_id_ptr = uint32_ptr[index]; + dll_append(obj->request_id_list, req_id_ptr); + } + } + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header *pcep_decode_obj_error(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_error *obj = + (struct pcep_object_error *)common_object_create( + hdr, sizeof(struct pcep_object_error)); + + obj->error_type = obj_buf[2]; + obj->error_value = obj_buf[3]; + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header *pcep_decode_obj_close(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_close *obj = + (struct pcep_object_close *)common_object_create( + hdr, sizeof(struct pcep_object_close)); + + obj->reason = obj_buf[3]; + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header *pcep_decode_obj_srp(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_srp *obj = + (struct pcep_object_srp *)common_object_create( + hdr, sizeof(struct pcep_object_srp)); + + obj->flag_lsp_remove = (obj_buf[3] & OBJECT_SRP_FLAG_R); + obj->srp_id_number = ntohl(*((uint32_t *)(obj_buf + 4))); + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header *pcep_decode_obj_lsp(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_lsp *obj = + (struct pcep_object_lsp *)common_object_create( + hdr, sizeof(struct pcep_object_lsp)); + + obj->flag_d = (obj_buf[3] & OBJECT_LSP_FLAG_D); + obj->flag_s = (obj_buf[3] & OBJECT_LSP_FLAG_S); + obj->flag_r = (obj_buf[3] & OBJECT_LSP_FLAG_R); + obj->flag_a = (obj_buf[3] & OBJECT_LSP_FLAG_A); + obj->flag_c = (obj_buf[3] & OBJECT_LSP_FLAG_C); + obj->operational_status = ((obj_buf[3] >> 4) & 0x07); + obj->plsp_id = ((ntohl(*((uint32_t *)obj_buf)) >> 12) & 0x000fffff); + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header * +pcep_decode_obj_vendor_info(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_vendor_info *obj = + (struct pcep_object_vendor_info *)common_object_create( + hdr, sizeof(struct pcep_object_vendor_info)); + + obj->enterprise_number = ntohl(*((uint32_t *)(obj_buf))); + obj->enterprise_specific_info = ntohl(*((uint32_t *)(obj_buf + 4))); + if (obj->enterprise_number == ENTERPRISE_NUMBER_CISCO + && obj->enterprise_specific_info == ENTERPRISE_COLOR_CISCO) + obj->enterprise_specific_info1 = + ntohl(*((uint32_t *)(obj_buf + 8))); + else + obj->enterprise_specific_info1 = 0; + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header * +pcep_decode_obj_inter_layer(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_inter_layer *obj = + (struct pcep_object_inter_layer *)common_object_create( + hdr, sizeof(struct pcep_object_inter_layer)); + obj->flag_t = (obj_buf[3] & OBJECT_INTER_LAYER_FLAG_T); + obj->flag_m = (obj_buf[3] & OBJECT_INTER_LAYER_FLAG_M); + obj->flag_i = (obj_buf[3] & OBJECT_INTER_LAYER_FLAG_I); + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header * +pcep_decode_obj_switch_layer(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_switch_layer *obj = + (struct pcep_object_switch_layer *)common_object_create( + hdr, sizeof(struct pcep_object_switch_layer)); + obj->switch_layer_rows = dll_initialize(); + int num_rows = ((hdr->encoded_object_length - 4) / 4); + uint8_t buf_index = 0; + + int i = 0; + for (; i < num_rows; i++) { + struct pcep_object_switch_layer_row *row = pceplib_malloc( + PCEPLIB_MESSAGES, + sizeof(struct pcep_object_switch_layer_row)); + row->lsp_encoding_type = obj_buf[buf_index]; + row->switching_type = obj_buf[buf_index + 1]; + row->flag_i = + (obj_buf[buf_index + 3] & OBJECT_SWITCH_LAYER_FLAG_I); + dll_append(obj->switch_layer_rows, row); + + buf_index += LENGTH_1WORD; + } + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header * +pcep_decode_obj_req_adap_cap(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_req_adap_cap *obj = + (struct pcep_object_req_adap_cap *)common_object_create( + hdr, sizeof(struct pcep_object_req_adap_cap)); + + obj->switching_capability = obj_buf[0]; + obj->encoding = obj_buf[1]; + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header * +pcep_decode_obj_server_ind(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_server_indication *obj = + (struct pcep_object_server_indication *)common_object_create( + hdr, sizeof(struct pcep_object_server_indication)); + + obj->switching_capability = obj_buf[0]; + obj->encoding = obj_buf[1]; + + return (struct pcep_object_header *)obj; +} + +struct pcep_object_header * +pcep_decode_obj_objective_function(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_objective_function *obj = + (struct pcep_object_objective_function *)common_object_create( + hdr, sizeof(struct pcep_object_objective_function)); + + uint16_t *uint16_ptr = (uint16_t *)obj_buf; + obj->of_code = ntohs(*uint16_ptr); + + return (struct pcep_object_header *)obj; +} + +void set_ro_subobj_fields(struct pcep_object_ro_subobj *subobj, bool flag_l, + uint8_t subobj_type) +{ + subobj->flag_subobj_loose_hop = flag_l; + subobj->ro_subobj_type = subobj_type; +} + +void decode_ipv6(const uint32_t *src, struct in6_addr *dst_ipv6) +{ + memcpy(dst_ipv6, src, sizeof(struct in6_addr)); +} +struct pcep_object_header *pcep_decode_obj_ro(struct pcep_object_header *hdr, + const uint8_t *obj_buf) +{ + struct pcep_object_ro *obj = + (struct pcep_object_ro *)common_object_create( + hdr, sizeof(struct pcep_object_ro)); + obj->sub_objects = dll_initialize(); + + /* RO Subobject format + * + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+ + * |L| Type | Length | (Subobject contents) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------//----------------+ + */ + + uint16_t read_count = 0; + int num_sub_objects = 1; + uint32_t *uint32_ptr; + uint16_t obj_body_length = + hdr->encoded_object_length - OBJECT_HEADER_LENGTH; + + while ((obj_body_length - read_count) > OBJECT_RO_SUBOBJ_HEADER_LENGTH + && num_sub_objects < MAX_ITERATIONS) { + num_sub_objects++; + /* Read the Sub-Object Header */ + bool flag_l = (obj_buf[read_count] & 0x80); + uint8_t subobj_type = (obj_buf[read_count++] & 0x7f); + uint8_t subobj_length = obj_buf[read_count++]; + + if (subobj_length <= OBJECT_RO_SUBOBJ_HEADER_LENGTH) { + pcep_log(LOG_INFO, + "%s: Invalid ro subobj type [%d] length [%d]", + __func__, subobj_type, subobj_length); + pceplib_free(PCEPLIB_MESSAGES, obj); + return NULL; + } + + switch (subobj_type) { + case RO_SUBOBJ_TYPE_IPV4: { + struct pcep_ro_subobj_ipv4 *ipv4 = pceplib_malloc( + PCEPLIB_MESSAGES, + sizeof(struct pcep_ro_subobj_ipv4)); + ipv4->ro_subobj.flag_subobj_loose_hop = flag_l; + ipv4->ro_subobj.ro_subobj_type = subobj_type; + uint32_ptr = (uint32_t *)(obj_buf + read_count); + ipv4->ip_addr.s_addr = *uint32_ptr; + read_count += LENGTH_1WORD; + ipv4->prefix_length = obj_buf[read_count++]; + ipv4->flag_local_protection = + (obj_buf[read_count++] + & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT); + + dll_append(obj->sub_objects, ipv4); + } break; + + case RO_SUBOBJ_TYPE_IPV6: { + struct pcep_ro_subobj_ipv6 *ipv6 = pceplib_malloc( + PCEPLIB_MESSAGES, + sizeof(struct pcep_ro_subobj_ipv6)); + ipv6->ro_subobj.flag_subobj_loose_hop = flag_l; + ipv6->ro_subobj.ro_subobj_type = subobj_type; + decode_ipv6((uint32_t *)obj_buf, &ipv6->ip_addr); + read_count += LENGTH_4WORDS; + ipv6->prefix_length = obj_buf[read_count++]; + ipv6->flag_local_protection = + (obj_buf[read_count++] + & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT); + + dll_append(obj->sub_objects, ipv6); + } break; + + case RO_SUBOBJ_TYPE_LABEL: { + struct pcep_ro_subobj_32label *label = pceplib_malloc( + PCEPLIB_MESSAGES, + sizeof(struct pcep_ro_subobj_32label)); + label->ro_subobj.flag_subobj_loose_hop = flag_l; + label->ro_subobj.ro_subobj_type = subobj_type; + label->flag_global_label = + (obj_buf[read_count++] + & OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL); + label->class_type = obj_buf[read_count++]; + label->label = ntohl(obj_buf[read_count]); + read_count += LENGTH_1WORD; + + dll_append(obj->sub_objects, label); + } break; + + case RO_SUBOBJ_TYPE_UNNUM: { + struct pcep_ro_subobj_unnum *unum = pceplib_malloc( + PCEPLIB_MESSAGES, + sizeof(struct pcep_ro_subobj_unnum)); + unum->ro_subobj.flag_subobj_loose_hop = flag_l; + unum->ro_subobj.ro_subobj_type = subobj_type; + set_ro_subobj_fields( + (struct pcep_object_ro_subobj *)unum, flag_l, + subobj_type); + uint32_ptr = (uint32_t *)(obj_buf + read_count); + unum->interface_id = ntohl(uint32_ptr[0]); + unum->router_id.s_addr = uint32_ptr[1]; + read_count += 2; + + dll_append(obj->sub_objects, unum); + } break; + + case RO_SUBOBJ_TYPE_ASN: { + struct pcep_ro_subobj_asn *asn = pceplib_malloc( + PCEPLIB_MESSAGES, + sizeof(struct pcep_ro_subobj_asn)); + asn->ro_subobj.flag_subobj_loose_hop = flag_l; + asn->ro_subobj.ro_subobj_type = subobj_type; + uint16_t *uint16_ptr = + (uint16_t *)(obj_buf + read_count); + asn->asn = ntohs(*uint16_ptr); + read_count += 2; + + dll_append(obj->sub_objects, asn); + } break; + + case RO_SUBOBJ_TYPE_SR: { + /* SR-ERO subobject format + * + * 0 1 2 3 0 1 2 3 4 + * 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |L| Type=36 | Length | NT | Flags + * |F|S|C|M| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | SID (optional) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * // NAI (variable, optional) // + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + struct pcep_ro_subobj_sr *sr_subobj = pceplib_malloc( + PCEPLIB_MESSAGES, + sizeof(struct pcep_ro_subobj_sr)); + sr_subobj->ro_subobj.flag_subobj_loose_hop = flag_l; + sr_subobj->ro_subobj.ro_subobj_type = subobj_type; + dll_append(obj->sub_objects, sr_subobj); + + sr_subobj->nai_list = dll_initialize(); + sr_subobj->nai_type = + ((obj_buf[read_count++] >> 4) & 0x0f); + sr_subobj->flag_f = + (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_F); + sr_subobj->flag_s = + (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_S); + sr_subobj->flag_c = + (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_C); + sr_subobj->flag_m = + (obj_buf[read_count] & OBJECT_SUBOBJ_SR_FLAG_M); + read_count++; + + /* If the sid_absent flag is true, then dont decode the + * sid */ + uint32_ptr = (uint32_t *)(obj_buf + read_count); + if (sr_subobj->flag_s == false) { + sr_subobj->sid = ntohl(*uint32_ptr); + read_count += LENGTH_1WORD; + uint32_ptr += 1; + } + + switch (sr_subobj->nai_type) { + case PCEP_SR_SUBOBJ_NAI_IPV4_NODE: { + struct in_addr *ipv4 = + pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in_addr)); + ipv4->s_addr = *uint32_ptr; + dll_append(sr_subobj->nai_list, ipv4); + read_count += LENGTH_1WORD; + } break; + + case PCEP_SR_SUBOBJ_NAI_IPV6_NODE: { + struct in6_addr *ipv6 = + pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in6_addr)); + decode_ipv6(uint32_ptr, ipv6); + dll_append(sr_subobj->nai_list, ipv6); + read_count += LENGTH_4WORDS; + } break; + + case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: { + struct in_addr *ipv4 = + pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in_addr)); + ipv4->s_addr = uint32_ptr[0]; + dll_append(sr_subobj->nai_list, ipv4); + + ipv4 = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in_addr)); + ipv4->s_addr = uint32_ptr[1]; + dll_append(sr_subobj->nai_list, ipv4); + + ipv4 = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in_addr)); + ipv4->s_addr = uint32_ptr[2]; + dll_append(sr_subobj->nai_list, ipv4); + + ipv4 = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in_addr)); + ipv4->s_addr = uint32_ptr[3]; + dll_append(sr_subobj->nai_list, ipv4); + + read_count += LENGTH_4WORDS; + } break; + + case PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY: { + struct in_addr *ipv4 = + pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in_addr)); + ipv4->s_addr = uint32_ptr[0]; + dll_append(sr_subobj->nai_list, ipv4); + + ipv4 = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in_addr)); + ipv4->s_addr = uint32_ptr[1]; + dll_append(sr_subobj->nai_list, ipv4); + + read_count += LENGTH_2WORDS; + } break; + + case PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY: { + struct in6_addr *ipv6 = + pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in6_addr)); + decode_ipv6(uint32_ptr, ipv6); + dll_append(sr_subobj->nai_list, ipv6); + + ipv6 = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in6_addr)); + decode_ipv6(uint32_ptr + 4, ipv6); + dll_append(sr_subobj->nai_list, ipv6); + + read_count += LENGTH_8WORDS; + } break; + + case PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY: { + struct in6_addr *ipv6 = + pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in6_addr)); + decode_ipv6(uint32_ptr, ipv6); + dll_append(sr_subobj->nai_list, ipv6); + + struct in_addr *ipv4 = + pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in_addr)); + ipv4->s_addr = uint32_ptr[4]; + dll_append(sr_subobj->nai_list, ipv4); + + ipv6 = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in6_addr)); + decode_ipv6(uint32_ptr + 5, ipv6); + dll_append(sr_subobj->nai_list, ipv6); + + ipv4 = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct in_addr)); + ipv4->s_addr = uint32_ptr[9]; + dll_append(sr_subobj->nai_list, ipv4); + + read_count += LENGTH_10WORDS; + } break; + + case PCEP_SR_SUBOBJ_NAI_ABSENT: + default: + break; + } + } break; + + default: + pcep_log( + LOG_INFO, + "%s: pcep_decode_obj_ro skipping unrecognized sub-object type [%d]", + __func__, subobj_type); + read_count += subobj_length; + break; + } + } + + return (struct pcep_object_header *)obj; +} diff --git a/pceplib/pcep_msg_tlvs.c b/pceplib/pcep_msg_tlvs.c new file mode 100644 index 0000000..6298ed4 --- /dev/null +++ b/pceplib/pcep_msg_tlvs.c @@ -0,0 +1,468 @@ +/* + * 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> + * + */ + + +/* + * This is the implementation of a High Level PCEP message object TLV API. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +#include "pcep_msg_tlvs.h" +#include "pcep_msg_encoding.h" +#include "pcep_utils_memory.h" + +static struct pcep_object_tlv_header * +pcep_tlv_common_create(enum pcep_object_tlv_types type, uint16_t size) +{ + struct pcep_object_tlv_header *tlv = + pceplib_malloc(PCEPLIB_MESSAGES, size); + memset(tlv, 0, size); + tlv->type = type; + + return tlv; +} + +/* + * Open Object TLVs + */ + +struct pcep_object_tlv_stateful_pce_capability * +pcep_tlv_create_stateful_pce_capability( + bool flag_u_lsp_update_capability, bool flag_s_include_db_version, + bool flag_i_lsp_instantiation_capability, bool flag_t_triggered_resync, + bool flag_d_delta_lsp_sync, bool flag_f_triggered_initial_sync) +{ + struct pcep_object_tlv_stateful_pce_capability *tlv = + (struct pcep_object_tlv_stateful_pce_capability *) + pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY, + sizeof(struct + pcep_object_tlv_stateful_pce_capability)); + tlv->flag_u_lsp_update_capability = flag_u_lsp_update_capability; + tlv->flag_s_include_db_version = flag_s_include_db_version; + tlv->flag_i_lsp_instantiation_capability = + flag_i_lsp_instantiation_capability; + tlv->flag_t_triggered_resync = flag_t_triggered_resync; + tlv->flag_d_delta_lsp_sync = flag_d_delta_lsp_sync; + tlv->flag_f_triggered_initial_sync = flag_f_triggered_initial_sync; + + return tlv; +} + +struct pcep_object_tlv_lsp_db_version * +pcep_tlv_create_lsp_db_version(uint64_t lsp_db_version) +{ + struct pcep_object_tlv_lsp_db_version *tlv = + (struct pcep_object_tlv_lsp_db_version *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION, + sizeof(struct pcep_object_tlv_lsp_db_version)); + tlv->lsp_db_version = lsp_db_version; + + return tlv; +} + +struct pcep_object_tlv_speaker_entity_identifier * +pcep_tlv_create_speaker_entity_id(double_linked_list *speaker_entity_id_list) +{ + if (speaker_entity_id_list == NULL) { + return NULL; + } + + if (speaker_entity_id_list->num_entries == 0) { + return NULL; + } + + struct pcep_object_tlv_speaker_entity_identifier *tlv = + (struct pcep_object_tlv_speaker_entity_identifier *) + pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID, + sizeof(struct + pcep_object_tlv_speaker_entity_identifier)); + tlv->speaker_entity_id_list = speaker_entity_id_list; + + return tlv; +} + +struct pcep_object_tlv_path_setup_type * +pcep_tlv_create_path_setup_type(uint8_t pst) +{ + struct pcep_object_tlv_path_setup_type *tlv = + (struct pcep_object_tlv_path_setup_type *) + pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE, + sizeof(struct pcep_object_tlv_path_setup_type)); + tlv->path_setup_type = pst; + + return tlv; +} + +struct pcep_object_tlv_path_setup_type_capability * +pcep_tlv_create_path_setup_type_capability(double_linked_list *pst_list, + double_linked_list *sub_tlv_list) +{ + if (pst_list == NULL) { + return NULL; + } + + if (pst_list->num_entries == 0) { + return NULL; + } + + struct pcep_object_tlv_path_setup_type_capability *tlv = + (struct pcep_object_tlv_path_setup_type_capability *) + pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY, + sizeof(struct + pcep_object_tlv_path_setup_type_capability)); + + tlv->pst_list = pst_list; + tlv->sub_tlv_list = sub_tlv_list; + + return tlv; +} + +struct pcep_object_tlv_sr_pce_capability * +pcep_tlv_create_sr_pce_capability(bool flag_n, bool flag_x, + uint8_t max_sid_depth) +{ + struct pcep_object_tlv_sr_pce_capability *tlv = + (struct pcep_object_tlv_sr_pce_capability *) + pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY, + sizeof(struct + pcep_object_tlv_sr_pce_capability)); + tlv->flag_n = flag_n; + tlv->flag_x = flag_x; + tlv->max_sid_depth = max_sid_depth; + + return tlv; +} + +struct pcep_object_tlv_of_list * +pcep_tlv_create_of_list(double_linked_list *of_list) +{ + if (of_list == NULL) { + return NULL; + } + + struct pcep_object_tlv_of_list *tlv = + (struct pcep_object_tlv_of_list *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST, + sizeof(struct pcep_object_tlv_of_list)); + + tlv->of_list = of_list; + + return tlv; +} + +/* + * LSP Object TLVs + */ + +struct pcep_object_tlv_ipv4_lsp_identifier * +pcep_tlv_create_ipv4_lsp_identifiers(struct in_addr *ipv4_tunnel_sender, + struct in_addr *ipv4_tunnel_endpoint, + uint16_t lsp_id, uint16_t tunnel_id, + struct in_addr *extended_tunnel_id) +{ + if (ipv4_tunnel_sender == NULL || ipv4_tunnel_endpoint == NULL) { + return NULL; + } + + struct pcep_object_tlv_ipv4_lsp_identifier *tlv = + (struct pcep_object_tlv_ipv4_lsp_identifier *) + pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS, + sizeof(struct + pcep_object_tlv_ipv4_lsp_identifier)); + tlv->ipv4_tunnel_sender.s_addr = ipv4_tunnel_sender->s_addr; + tlv->ipv4_tunnel_endpoint.s_addr = ipv4_tunnel_endpoint->s_addr; + tlv->lsp_id = lsp_id; + tlv->tunnel_id = tunnel_id; + tlv->extended_tunnel_id.s_addr = + (extended_tunnel_id == NULL ? INADDR_ANY + : extended_tunnel_id->s_addr); + + return tlv; +} + +struct pcep_object_tlv_ipv6_lsp_identifier * +pcep_tlv_create_ipv6_lsp_identifiers(struct in6_addr *ipv6_tunnel_sender, + struct in6_addr *ipv6_tunnel_endpoint, + uint16_t lsp_id, uint16_t tunnel_id, + struct in6_addr *extended_tunnel_id) +{ + if (ipv6_tunnel_sender == NULL || ipv6_tunnel_endpoint == NULL) { + return NULL; + } + + struct pcep_object_tlv_ipv6_lsp_identifier *tlv = + (struct pcep_object_tlv_ipv6_lsp_identifier *) + pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS, + sizeof(struct + pcep_object_tlv_ipv6_lsp_identifier)); + + memcpy(&tlv->ipv6_tunnel_sender, ipv6_tunnel_sender, + sizeof(struct in6_addr)); + + tlv->tunnel_id = tunnel_id; + tlv->lsp_id = lsp_id; + + memcpy(&tlv->extended_tunnel_id, extended_tunnel_id, + sizeof(struct in6_addr)); + + memcpy(&tlv->ipv6_tunnel_endpoint, ipv6_tunnel_endpoint, + sizeof(struct in6_addr)); + + return tlv; +} + +struct pcep_object_tlv_symbolic_path_name * +pcep_tlv_create_symbolic_path_name(const char *symbolic_path_name, + uint16_t symbolic_path_name_length) +{ + /* symbolic_path_name_length should NOT include the null terminator and + * cannot be zero */ + if (symbolic_path_name == NULL || symbolic_path_name_length == 0) { + return NULL; + } + + struct pcep_object_tlv_symbolic_path_name *tlv = + (struct pcep_object_tlv_symbolic_path_name *) + pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME, + sizeof(struct + pcep_object_tlv_symbolic_path_name)); + + uint16_t length = (symbolic_path_name_length > MAX_SYMBOLIC_PATH_NAME) + ? MAX_SYMBOLIC_PATH_NAME + : symbolic_path_name_length; + memcpy(tlv->symbolic_path_name, symbolic_path_name, length); + tlv->symbolic_path_name_length = length; + + return tlv; +} + +struct pcep_object_tlv_lsp_error_code * +pcep_tlv_create_lsp_error_code(enum pcep_tlv_lsp_error_codes lsp_error_code) +{ + struct pcep_object_tlv_lsp_error_code *tlv = + (struct pcep_object_tlv_lsp_error_code *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE, + sizeof(struct pcep_object_tlv_lsp_error_code)); + tlv->lsp_error_code = lsp_error_code; + + return tlv; +} + +struct pcep_object_tlv_rsvp_error_spec * +pcep_tlv_create_rsvp_ipv4_error_spec(struct in_addr *error_node_ip, + uint8_t error_code, uint16_t error_value) +{ + if (error_node_ip == NULL) { + return NULL; + } + + struct pcep_object_tlv_rsvp_error_spec *tlv = + (struct pcep_object_tlv_rsvp_error_spec *) + pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC, + sizeof(struct pcep_object_tlv_rsvp_error_spec)); + + tlv->c_type = RSVP_ERROR_SPEC_IPV4_CTYPE; + tlv->class_num = RSVP_ERROR_SPEC_CLASS_NUM; + tlv->error_code = error_code; + tlv->error_value = error_value; + tlv->error_spec_ip.ipv4_error_node_address.s_addr = + error_node_ip->s_addr; + + return tlv; +} + +struct pcep_object_tlv_rsvp_error_spec * +pcep_tlv_create_rsvp_ipv6_error_spec(struct in6_addr *error_node_ip, + uint8_t error_code, uint16_t error_value) +{ + if (error_node_ip == NULL) { + return NULL; + } + + struct pcep_object_tlv_rsvp_error_spec *tlv = + (struct pcep_object_tlv_rsvp_error_spec *) + pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC, + sizeof(struct pcep_object_tlv_rsvp_error_spec)); + + tlv->c_type = RSVP_ERROR_SPEC_IPV6_CTYPE; + tlv->class_num = RSVP_ERROR_SPEC_CLASS_NUM; + tlv->error_code = error_code; + tlv->error_value = error_value; + memcpy(&tlv->error_spec_ip, error_node_ip, sizeof(struct in6_addr)); + + return tlv; +} + +struct pcep_object_tlv_nopath_vector * +pcep_tlv_create_nopath_vector(uint32_t error_code) +{ + struct pcep_object_tlv_nopath_vector *tlv = + (struct pcep_object_tlv_nopath_vector *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR, + sizeof(struct pcep_object_tlv_nopath_vector)); + + tlv->error_code = error_code; + + return tlv; +} + +struct pcep_object_tlv_vendor_info * +pcep_tlv_create_vendor_info(uint32_t enterprise_number, + uint32_t enterprise_specific_info) +{ + struct pcep_object_tlv_vendor_info *tlv = + (struct pcep_object_tlv_vendor_info *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_VENDOR_INFO, + sizeof(struct pcep_object_tlv_vendor_info)); + + tlv->enterprise_number = enterprise_number; + tlv->enterprise_specific_info = enterprise_specific_info; + + return tlv; +} + +/* + * SRPAG (SR Association Group) TLVs + */ + +struct pcep_object_tlv_srpag_pol_id * +pcep_tlv_create_srpag_pol_id_ipv4(uint32_t color, struct in_addr *ipv4) +{ + struct pcep_object_tlv_srpag_pol_id *tlv = + (struct pcep_object_tlv_srpag_pol_id *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID, + sizeof(struct pcep_object_tlv_srpag_pol_id)); + tlv->color = color; + tlv->is_ipv4 = true; + memcpy(&tlv->end_point.ipv4.s_addr, ipv4, sizeof(struct in_addr)); + + return tlv; +} + +struct pcep_object_tlv_srpag_pol_id * +pcep_tlv_create_srpag_pol_id_ipv6(uint32_t color, struct in6_addr *ipv6) +{ + struct pcep_object_tlv_srpag_pol_id *tlv = + (struct pcep_object_tlv_srpag_pol_id *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID, + sizeof(struct pcep_object_tlv_srpag_pol_id)); + tlv->color = color; + tlv->is_ipv4 = false; + memcpy(&tlv->end_point.ipv6, ipv6, sizeof(struct in6_addr)); + + return tlv; +} + + +struct pcep_object_tlv_srpag_pol_name * +pcep_tlv_create_srpag_pol_name(const char *pol_name, uint16_t pol_name_length) +{ + if (pol_name == NULL) { + return NULL; + } + struct pcep_object_tlv_srpag_pol_name *tlv = + (struct pcep_object_tlv_srpag_pol_name *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME, + sizeof(struct pcep_object_tlv_srpag_pol_name)); + uint16_t length = + (normalize_pcep_tlv_length(pol_name_length) > MAX_POLICY_NAME) + ? MAX_POLICY_NAME + : pol_name_length; + memcpy(tlv->name, pol_name, length); + tlv->name_length = length; + + return tlv; +} +struct pcep_object_tlv_srpag_cp_id * +pcep_tlv_create_srpag_cp_id(uint8_t proto_origin, uint32_t asn, + struct in6_addr *in6_addr_with_mapped_ipv4, + uint32_t discriminator) +{ + if (!in6_addr_with_mapped_ipv4) { + return NULL; + } + + struct pcep_object_tlv_srpag_cp_id *tlv = + (struct pcep_object_tlv_srpag_cp_id *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID, + sizeof(struct pcep_object_tlv_srpag_cp_id)); + tlv->proto = proto_origin; + tlv->orig_asn = asn; + memcpy(&(tlv->orig_addres), in6_addr_with_mapped_ipv4, + sizeof(*in6_addr_with_mapped_ipv4)); + tlv->discriminator = discriminator; + + return tlv; +} +struct pcep_object_tlv_srpag_cp_pref * +pcep_tlv_create_srpag_cp_pref(uint32_t pref) +{ + + struct pcep_object_tlv_srpag_cp_pref *tlv = + (struct pcep_object_tlv_srpag_cp_pref *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE, + sizeof(struct pcep_object_tlv_srpag_cp_pref)); + tlv->preference = pref; + + return tlv; +} + +struct pcep_object_tlv_arbitrary * +pcep_tlv_create_tlv_arbitrary(const char *data, uint16_t data_length, + int tlv_id) +{ + if (data == NULL || data_length == 0) { + return NULL; + } + + struct pcep_object_tlv_arbitrary *tlv = + (struct pcep_object_tlv_arbitrary *)pcep_tlv_common_create( + PCEP_OBJ_TLV_TYPE_ARBITRARY, + sizeof(struct pcep_object_tlv_arbitrary)); + + uint16_t length = (data_length > MAX_ARBITRARY_SIZE) + ? MAX_ARBITRARY_SIZE + : data_length; + memcpy(tlv->data, data, length); + tlv->data_length = length; + tlv->arbitraty_type = tlv_id; + + return tlv; +} diff --git a/pceplib/pcep_msg_tlvs.h b/pceplib/pcep_msg_tlvs.h new file mode 100644 index 0000000..6dd2b56 --- /dev/null +++ b/pceplib/pcep_msg_tlvs.h @@ -0,0 +1,382 @@ +/* + * 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> + */ + + +/* + * This is a High Level PCEP message object TLV API. + */ + +#ifndef PCEP_TLVS_H_ +#define PCEP_TLVS_H_ + +#include <arpa/inet.h> +#include <stdint.h> + +#include "pcep.h" +#include "pcep_utils_double_linked_list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Regarding memory usage: + * When creating TLVs, any TLVs passed into messages or objects with these APIs + * will be free'd when the the enclosing pcep_message is free'd. That includes + * the double_linked_list's. So, just create the objects and TLVs, put them in + * their double_linked_list's, and everything will be managed internally. The + * enclosing message will be deleted by pcep_msg_free_message() or + * pcep_msg_free_message_list() which, * in turn will call one of: + * pcep_obj_free_object() and pcep_obj_free_tlv(). + * For received messages, call pcep_msg_free_message() to free them. + */ + +/* These numbers can be found here: + * https://www.iana.org/assignments/pcep/pcep.xhtml */ +enum pcep_object_tlv_types { + PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR = 1, + PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST = 4, /* RFC 5541 */ + PCEP_OBJ_TLV_TYPE_VENDOR_INFO = 7, /* RFC 7470 */ + PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY = 16, /* RFC 8231 */ + PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME = 17, /* RFC 8232 */ + PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS = 18, /* RFC 8231 */ + PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS = 19, /* RFC 8231 */ + PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE = 20, /* RFC 8232 */ + PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC = 21, /* RFC 8232 */ + PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION = 23, /* RFC 8232 */ + PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID = 24, /* RFC 8232 */ + PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY = + 26, /* draft-ietf-pce-segment-routing-16 */ + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE = 28, /* RFC 8408 */ + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY = + 34, /* RFC 8408, draft-ietf-pce-segment-routing-16 */ + PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID = + 60, /*TDB2 draft-barth-pce-segment-routing-policy-cp-04 */ + PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME = + 61, /*TDB3 draft-barth-pce-segment-routing-policy-cp-04 */ + PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID = + 62, /*TDB4 draft-barth-pce-segment-routing-policy-cp-04 */ + PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE = + 63, /*TDB5 draft-barth-pce-segment-routing-policy-cp-04 */ + PCEP_OBJ_TLV_TYPE_UNKNOWN = 128, + PCEP_OBJ_TYPE_CISCO_BSID = 65505, + /* Max IANA To write arbitrary data */ + PCEP_OBJ_TLV_TYPE_ARBITRARY = 65533 +}; + + +struct pcep_object_tlv_header { + enum pcep_object_tlv_types type; + /* Pointer into encoded_message field from the pcep_message */ + const uint8_t *encoded_tlv; + uint16_t encoded_tlv_length; +}; + +/* STATEFUL-PCE-CAPABILITY TLV, Used in Open Object. RFCs: 8231, 8232, 8281 */ +#define TLV_STATEFUL_PCE_CAP_FLAG_U 0x01 +#define TLV_STATEFUL_PCE_CAP_FLAG_S 0x02 +#define TLV_STATEFUL_PCE_CAP_FLAG_I 0x04 +#define TLV_STATEFUL_PCE_CAP_FLAG_T 0x08 +#define TLV_STATEFUL_PCE_CAP_FLAG_D 0x10 +#define TLV_STATEFUL_PCE_CAP_FLAG_F 0x20 + +struct pcep_object_tlv_stateful_pce_capability { + struct pcep_object_tlv_header header; + bool flag_u_lsp_update_capability; /* RFC 8231 */ + bool flag_s_include_db_version; /* RFC 8232 */ + bool flag_i_lsp_instantiation_capability; /* RFC 8281 */ + bool flag_t_triggered_resync; /* RFC 8232 */ + bool flag_d_delta_lsp_sync; /* RFC 8232 */ + bool flag_f_triggered_initial_sync; /* RFC 8232 */ +}; + +/* NOPATH-VECTOR TLV, Used in the Reply NoPath Object. */ +struct pcep_object_tlv_nopath_vector { + struct pcep_object_tlv_header header; + uint32_t error_code; +}; + +/* STATEFUL-PCE-CAPABILITY TLV, Used in Open Object. RFCs: 8232 */ +struct pcep_object_tlv_lsp_db_version { + struct pcep_object_tlv_header header; + uint64_t lsp_db_version; +}; + +/* Speaker Entity Identifier TLV, Used in Open Object. RFCs: 8232 */ +struct pcep_object_tlv_speaker_entity_identifier { + struct pcep_object_tlv_header header; + double_linked_list *speaker_entity_id_list; /* list of uint32_t speaker + entity ids */ +}; + +/* Ipv4 LSP Identifier TLV, Used in LSP Object. RFCs: 8231 */ +struct pcep_object_tlv_ipv4_lsp_identifier { + struct pcep_object_tlv_header header; + struct in_addr ipv4_tunnel_sender; + uint16_t lsp_id; + uint16_t tunnel_id; + struct in_addr extended_tunnel_id; + struct in_addr ipv4_tunnel_endpoint; +}; + +/* Ipv6 LSP Identifier TLV, Used in LSP Object. RFCs: 8231 */ +struct pcep_object_tlv_ipv6_lsp_identifier { + struct pcep_object_tlv_header header; + struct in6_addr ipv6_tunnel_sender; + uint16_t lsp_id; + uint16_t tunnel_id; + struct in6_addr extended_tunnel_id; + struct in6_addr ipv6_tunnel_endpoint; +}; + +/* Symbolic Path Name TLV, Used in LSP Object. RFCs: 8231 */ +#define MAX_SYMBOLIC_PATH_NAME 256 + +struct pcep_object_tlv_symbolic_path_name { + struct pcep_object_tlv_header header; + uint16_t symbolic_path_name_length; + char symbolic_path_name[MAX_SYMBOLIC_PATH_NAME]; +}; + +/* LSP Error Code TLV, Used in LSP Object. RFCs: 8231 */ +enum pcep_tlv_lsp_error_codes { + PCEP_TLV_LSP_ERROR_CODE_UNKNOWN = 1, + PCEP_TLV_LSP_ERROR_CODE_LSP_LIMIT_REACHED = 2, + PCEP_TLV_LSP_ERROR_CODE_TOO_MANY_PENDING_LSP_UPDATES = 3, + PCEP_TLV_LSP_ERROR_CODE_UNACCEPTABLE_PARAMS = 4, + PCEP_TLV_LSP_ERROR_CODE_INTERNAL_ERROR = 5, + PCEP_TLV_LSP_ERROR_CODE_LSP_BROUGHT_DOWN = 6, + PCEP_TLV_LSP_ERROR_CODE_LSP_PREEMPTED = 7, + PCEP_TLV_LSP_ERROR_CODE_RSVP_SIGNALING_ERROR = 8, +}; + +struct pcep_object_tlv_lsp_error_code { + struct pcep_object_tlv_header header; + enum pcep_tlv_lsp_error_codes lsp_error_code; +}; + +/* Path Setup Type TLV, Used in RP and SRP Object. RFCs: 8408, + * draft-ietf-pce-segment-routing-16 */ +#define SR_TE_PST 1 + +struct pcep_object_tlv_path_setup_type { + struct pcep_object_tlv_header header; + uint8_t path_setup_type; +}; + +/* Path Setup Type Capability TLV, Used in Open Object. RFCs: 8408, + * draft-ietf-pce-segment-routing-16 */ +struct pcep_object_tlv_path_setup_type_capability { + struct pcep_object_tlv_header header; + double_linked_list *pst_list; /* list of uint8_t PSTs */ + double_linked_list *sub_tlv_list; /* list of sub_tlvs */ +}; + +/* SR PCE Capability sub-TLV, Used in Open Object. RFCs: + * draft-ietf-pce-segment-routing-16 */ +#define TLV_SR_PCE_CAP_FLAG_X 0x01 +#define TLV_SR_PCE_CAP_FLAG_N 0x02 + +struct pcep_object_tlv_sr_pce_capability { + struct pcep_object_tlv_header header; + bool flag_n; + bool flag_x; + uint8_t max_sid_depth; +}; + + +/* RSVP Error Spec TLV, Used in LSP Object. RFCs: 8231, 2205 */ +#define RSVP_ERROR_SPEC_IPV4_CTYPE 1 +#define RSVP_ERROR_SPEC_IPV6_CTYPE 2 +#define RSVP_ERROR_SPEC_CLASS_NUM 6 + +struct pcep_object_tlv_rsvp_error_spec { + struct pcep_object_tlv_header header; + uint8_t class_num; + uint8_t c_type; + uint8_t error_code; + uint16_t error_value; + /* Use the c_type to determine which union entry to use */ + union error_spec_ip { + struct in_addr ipv4_error_node_address; + struct in6_addr ipv6_error_node_address; + } error_spec_ip; +}; + +/* SR Policy Identifier TLV Used in Association Object. + * draft-barth-pce-segment-routing-policy-cp-04*/ +struct pcep_object_tlv_srpag_pol_id { + struct pcep_object_tlv_header header; + uint32_t color; + bool is_ipv4; + union end_point_ { + struct in_addr ipv4; + struct in6_addr ipv6; + } end_point; +}; + +/*draft-ietf-spring-segment-routing-policy-06*/ +#define MAX_POLICY_NAME 256 + +/* SR Policy Name TLV Used in Association Object. + * draft-barth-pce-segment-routing-policy-cp-04*/ +struct pcep_object_tlv_srpag_pol_name { + struct pcep_object_tlv_header header; + uint16_t name_length; + char name[MAX_POLICY_NAME]; +}; + +/* SR Candidate Path Id TLV Used in Association Object. + * draft-barth-pce-segment-routing-policy-cp-04*/ +struct pcep_object_tlv_srpag_cp_id { + struct pcep_object_tlv_header header; + uint8_t proto; + uint32_t orig_asn; + struct in6_addr orig_addres; /*With ipv4 embedded*/ + uint32_t discriminator; +}; + +/* SR Candidate Preference TLV Used in Association Object. + * draft-barth-pce-segment-routing-policy-cp-04*/ +struct pcep_object_tlv_srpag_cp_pref { + struct pcep_object_tlv_header header; + uint32_t preference; +}; + +struct pcep_object_tlv_vendor_info { + struct pcep_object_tlv_header header; + uint32_t enterprise_number; + uint32_t enterprise_specific_info; +}; + +/* arbitrary TLV 65535 */ +#define MAX_ARBITRARY_SIZE 256 +struct pcep_object_tlv_arbitrary { + struct pcep_object_tlv_header header; + enum pcep_object_tlv_types arbitraty_type; + uint16_t data_length; + char data[MAX_ARBITRARY_SIZE]; +}; + +/* Objective Functions List RFC 5541 + * At least the following 6 OF codes must be supported */ +enum objective_function_codes { + PCEP_OF_CODE_MINIMUM_COST_PATH = 1, /* MCP */ + PCEP_OF_CODE_MINIMUM_LOAD_PATH = 2, /* MLP */ + PCEP_OF_CODE_MAXIMUM_BW_PATH = 3, /* MBP */ + PCEP_OF_CODE_MINIMIZE_AGGR_BW_CONSUMPTION = 4, /* MBC */ + PCEP_OF_CODE_MINIMIZE_MOST_LOADED_LINK = 5, /* MLL */ + PCEP_OF_CODE_MINIMIZE_CUMULATIVE_COST_PATHS = 6, /* MCC */ +}; + +struct pcep_object_tlv_of_list { + struct pcep_object_tlv_header header; + double_linked_list *of_list; /* list of uint16_t OF code points */ +}; + +/* + * TLV creation functions + */ + +/* + * Open Object TLVs + */ + +struct pcep_object_tlv_stateful_pce_capability * +pcep_tlv_create_stateful_pce_capability( + bool flag_u_lsp_update_capability, bool flag_s_include_db_version, + bool flag_i_lsp_instantiation_capability, bool flag_t_triggered_resync, + bool flag_d_delta_lsp_sync, bool flag_f_triggered_initial_sync); +struct pcep_object_tlv_lsp_db_version * +pcep_tlv_create_lsp_db_version(uint64_t lsp_db_version); +struct pcep_object_tlv_speaker_entity_identifier * +pcep_tlv_create_speaker_entity_id(double_linked_list *speaker_entity_id_list); +struct pcep_object_tlv_path_setup_type * +pcep_tlv_create_path_setup_type(uint8_t pst); +struct pcep_object_tlv_path_setup_type_capability * +pcep_tlv_create_path_setup_type_capability(double_linked_list *pst_list, + double_linked_list *sub_tlv_list); +struct pcep_object_tlv_sr_pce_capability * +pcep_tlv_create_sr_pce_capability(bool flag_n, bool flag_x, + uint8_t max_sid_depth); +struct pcep_object_tlv_of_list * +pcep_tlv_create_of_list(double_linked_list *of_list); + +/* + * LSP Object TLVs + */ + +struct pcep_object_tlv_ipv4_lsp_identifier * +pcep_tlv_create_ipv4_lsp_identifiers(struct in_addr *ipv4_tunnel_sender, + struct in_addr *ipv4_tunnel_endpoint, + uint16_t lsp_id, uint16_t tunnel_id, + struct in_addr *extended_tunnel_id); +struct pcep_object_tlv_ipv6_lsp_identifier * +pcep_tlv_create_ipv6_lsp_identifiers(struct in6_addr *ipv6_tunnel_sender, + struct in6_addr *extended_tunnel_id, + uint16_t lsp_id, uint16_t tunnel_id, + struct in6_addr *ipv6_tunnel_endpoint); +/* symbolic_path_name_length should NOT include the null terminator and cannot + * be zero */ +struct pcep_object_tlv_symbolic_path_name * +pcep_tlv_create_symbolic_path_name(const char *symbolic_path_name, + uint16_t symbolic_path_name_length); +struct pcep_object_tlv_lsp_error_code * +pcep_tlv_create_lsp_error_code(enum pcep_tlv_lsp_error_codes lsp_error_code); +struct pcep_object_tlv_rsvp_error_spec * +pcep_tlv_create_rsvp_ipv4_error_spec(struct in_addr *error_node_ip, + uint8_t error_code, uint16_t error_value); +struct pcep_object_tlv_rsvp_error_spec * +pcep_tlv_create_rsvp_ipv6_error_spec(struct in6_addr *error_node_ip, + uint8_t error_code, uint16_t error_value); + +struct pcep_object_tlv_nopath_vector * +pcep_tlv_create_nopath_vector(uint32_t error_code); +struct pcep_object_tlv_vendor_info * +pcep_tlv_create_vendor_info(uint32_t enterprise_number, + uint32_t enterprise_specific_info); + +struct pcep_object_tlv_arbitrary * +pcep_tlv_create_tlv_arbitrary(const char *data, uint16_t data_length, + int tlv_id); +/* + * SRPAG (SR Association Group) TLVs + */ + +struct pcep_object_tlv_srpag_pol_id * +pcep_tlv_create_srpag_pol_id_ipv4(uint32_t color, struct in_addr *ipv4); +struct pcep_object_tlv_srpag_pol_id * +pcep_tlv_create_srpag_pol_id_ipv6(uint32_t color, struct in6_addr *ipv6); +struct pcep_object_tlv_srpag_pol_name * +pcep_tlv_create_srpag_pol_name(const char *pol_name, uint16_t pol_name_length); +struct pcep_object_tlv_srpag_cp_id * +pcep_tlv_create_srpag_cp_id(uint8_t proto_origin, uint32_t asn, + struct in6_addr *in6_addr_with_mapped_ipv4, + uint32_t discriminator); +struct pcep_object_tlv_srpag_cp_pref * +pcep_tlv_create_srpag_cp_pref(uint32_t pref); + + +#ifdef __cplusplus +} +#endif + +#endif /* PCEP_TLVS_H_ */ diff --git a/pceplib/pcep_msg_tlvs_encoding.c b/pceplib/pcep_msg_tlvs_encoding.c new file mode 100644 index 0000000..b5a65d4 --- /dev/null +++ b/pceplib/pcep_msg_tlvs_encoding.c @@ -0,0 +1,1298 @@ +/* + * 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> + * + */ + + +/* + * Encoding and decoding for PCEP Object TLVs. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __FreeBSD__ +#include <sys/endian.h> +#else +#include <endian.h> +#endif /* __FreeBSD__ */ +#include <stdlib.h> +#include <string.h> + +#include "pcep.h" +#include "pcep_msg_encoding.h" +#include "pcep_msg_tlvs.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +void write_tlv_header(struct pcep_object_tlv_header *tlv_hdr, + uint16_t tlv_length, struct pcep_versioning *versioning, + uint8_t *buf); +void pcep_decode_tlv_hdr(const uint8_t *tlv_buf, + struct pcep_object_tlv_header *tlv_hdr); + +/* + * forward declarations for initialize_tlv_encoders() + */ +uint16_t pcep_encode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t +pcep_encode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t +pcep_encode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t +pcep_encode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t +pcep_encode_tlv_path_setup_type_capability(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_pol_id(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_pol_name(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_cpath_id(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_vendor_info(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_arbitrary(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +uint16_t pcep_encode_tlv_of_list(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); +typedef uint16_t (*tlv_encoder_funcptr)(struct pcep_object_tlv_header *, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf); + +#define MAX_TLV_ENCODER_INDEX 65533 + 1 // 65 + +#define PCEP_TLV_ENCODERS_ARGS \ + struct pcep_object_tlv_header *, struct pcep_versioning *versioning, \ + uint8_t *tlv_body_buf +uint16_t (*const tlv_encoders[MAX_TLV_ENCODER_INDEX])( + PCEP_TLV_ENCODERS_ARGS) = { + [PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] = pcep_encode_tlv_no_path_vector, + [PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] = + pcep_encode_tlv_stateful_pce_capability, + [PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] = + pcep_encode_tlv_symbolic_path_name, + [PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] = + pcep_encode_tlv_ipv4_lsp_identifiers, + [PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] = + pcep_encode_tlv_ipv6_lsp_identifiers, + [PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] = pcep_encode_tlv_lsp_error_code, + [PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] = pcep_encode_tlv_rsvp_error_spec, + [PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] = pcep_encode_tlv_lsp_db_version, + [PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] = + pcep_encode_tlv_speaker_entity_id, + [PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] = + pcep_encode_tlv_sr_pce_capability, + [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] = pcep_encode_tlv_path_setup_type, + [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] = + pcep_encode_tlv_path_setup_type_capability, + [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] = pcep_encode_tlv_pol_id, + [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] = pcep_encode_tlv_pol_name, + [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] = pcep_encode_tlv_cpath_id, + [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] = + pcep_encode_tlv_cpath_preference, + [PCEP_OBJ_TLV_TYPE_VENDOR_INFO] = pcep_encode_tlv_vendor_info, + [PCEP_OBJ_TLV_TYPE_ARBITRARY] = pcep_encode_tlv_arbitrary, + [PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] = pcep_encode_tlv_of_list, +}; +/* + * forward declarations for initialize_tlv_decoders() + */ +struct pcep_object_tlv_header * +pcep_decode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header *pcep_decode_tlv_path_setup_type_capability( + struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_pol_id(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_pol_name(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_cpath_id(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_vendor_info(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_arbitrary(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +struct pcep_object_tlv_header * +pcep_decode_tlv_of_list(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf); +typedef struct pcep_object_tlv_header *(*tlv_decoder_funcptr)( + struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf); + +// tlv_decoder_funcptr tlv_decoders[MAX_TLV_ENCODER_INDEX]; + +#define PCEP_TLV_DECODERS_ARGS \ + struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf + +struct pcep_object_tlv_header *(*const tlv_decoders[MAX_TLV_ENCODER_INDEX])( + PCEP_TLV_DECODERS_ARGS) = { + [PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] = pcep_decode_tlv_no_path_vector, + [PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] = + pcep_decode_tlv_stateful_pce_capability, + [PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] = + pcep_decode_tlv_symbolic_path_name, + [PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] = + pcep_decode_tlv_ipv4_lsp_identifiers, + [PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] = + pcep_decode_tlv_ipv6_lsp_identifiers, + [PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] = pcep_decode_tlv_lsp_error_code, + [PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] = pcep_decode_tlv_rsvp_error_spec, + [PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] = pcep_decode_tlv_lsp_db_version, + [PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] = + pcep_decode_tlv_speaker_entity_id, + [PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] = + pcep_decode_tlv_sr_pce_capability, + [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] = pcep_decode_tlv_path_setup_type, + [PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] = + pcep_decode_tlv_path_setup_type_capability, + [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] = pcep_decode_tlv_pol_id, + [PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] = pcep_decode_tlv_pol_name, + [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] = pcep_decode_tlv_cpath_id, + [PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] = + pcep_decode_tlv_cpath_preference, + [PCEP_OBJ_TLV_TYPE_VENDOR_INFO] = pcep_decode_tlv_vendor_info, + [PCEP_OBJ_TLV_TYPE_ARBITRARY] = pcep_decode_tlv_arbitrary, + [PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] = pcep_decode_tlv_of_list, +}; + +static void initialize_tlv_coders(void) +{ + static bool initialized = false; + + if (initialized == true) { + return; + } + + initialized = true; + + /* Encoders */ + /* + memset(tlv_encoders, 0, sizeof(tlv_encoder_funcptr) * + MAX_TLV_ENCODER_INDEX); tlv_encoders[PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] = + pcep_encode_tlv_no_path_vector; + tlv_encoders[PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] = + pcep_encode_tlv_stateful_pce_capability; + tlv_encoders[PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] = + pcep_encode_tlv_symbolic_path_name; + tlv_encoders[PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] = + pcep_encode_tlv_ipv4_lsp_identifiers; + tlv_encoders[PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] = + pcep_encode_tlv_ipv6_lsp_identifiers; + tlv_encoders[PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] = + pcep_encode_tlv_lsp_error_code; + tlv_encoders[PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] = + pcep_encode_tlv_rsvp_error_spec; + tlv_encoders[PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] = + pcep_encode_tlv_lsp_db_version; + tlv_encoders[PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] = + pcep_encode_tlv_speaker_entity_id; + tlv_encoders[PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] = + pcep_encode_tlv_sr_pce_capability; + tlv_encoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] = + pcep_encode_tlv_path_setup_type; + tlv_encoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] = + pcep_encode_tlv_path_setup_type_capability; + tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] = + pcep_encode_tlv_pol_id; + tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] = + pcep_encode_tlv_pol_name; + tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] = + pcep_encode_tlv_cpath_id; + tlv_encoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] = + pcep_encode_tlv_cpath_preference; + tlv_encoders[PCEP_OBJ_TLV_TYPE_VENDOR_INFO] = + pcep_encode_tlv_vendor_info; tlv_encoders[PCEP_OBJ_TLV_TYPE_ARBITRARY] = + pcep_encode_tlv_arbitrary; + tlv_encoders[PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] = + pcep_encode_tlv_of_list; + */ + + /* Decoders */ + /* + memset(tlv_decoders, 0, sizeof(tlv_decoder_funcptr) * + MAX_TLV_ENCODER_INDEX); tlv_decoders[PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR] = + pcep_decode_tlv_no_path_vector; + tlv_decoders[PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY] = + pcep_decode_tlv_stateful_pce_capability; + tlv_decoders[PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME] = + pcep_decode_tlv_symbolic_path_name; + tlv_decoders[PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS] = + pcep_decode_tlv_ipv4_lsp_identifiers; + tlv_decoders[PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS] = + pcep_decode_tlv_ipv6_lsp_identifiers; + tlv_decoders[PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE] = + pcep_decode_tlv_lsp_error_code; + tlv_decoders[PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC] = + pcep_decode_tlv_rsvp_error_spec; + tlv_decoders[PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION] = + pcep_decode_tlv_lsp_db_version; + tlv_decoders[PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID] = + pcep_decode_tlv_speaker_entity_id; + tlv_decoders[PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY] = + pcep_decode_tlv_sr_pce_capability; + tlv_decoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE] = + pcep_decode_tlv_path_setup_type; + tlv_decoders[PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY] = + pcep_decode_tlv_path_setup_type_capability; + tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID] = + pcep_decode_tlv_pol_id; + tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME] = + pcep_decode_tlv_pol_name; + tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID] = + pcep_decode_tlv_cpath_id; + tlv_decoders[PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE] = + pcep_decode_tlv_cpath_preference; + tlv_decoders[PCEP_OBJ_TLV_TYPE_VENDOR_INFO] = + pcep_decode_tlv_vendor_info; tlv_decoders[PCEP_OBJ_TLV_TYPE_ARBITRARY] = + pcep_decode_tlv_arbitrary; + tlv_decoders[PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST] = + pcep_decode_tlv_of_list; + */ +} + +uint16_t pcep_encode_tlv(struct pcep_object_tlv_header *tlv_hdr, + struct pcep_versioning *versioning, uint8_t *buf) +{ + initialize_tlv_coders(); + + if (tlv_hdr->type >= MAX_TLV_ENCODER_INDEX) { + pcep_log(LOG_INFO, + "%s: Cannot encode unknown Object class [%d]", + __func__, tlv_hdr->type); + return 0; + } + + tlv_encoder_funcptr tlv_encoder = tlv_encoders[tlv_hdr->type]; + if (tlv_encoder == NULL) { + pcep_log(LOG_INFO, + "%s: No object encoder found for Object class [%d]", + __func__, tlv_hdr->type); + return 0; + } + + /* Notice: The length in the TLV header does not include the TLV header, + * so the length returned from the tlv_encoder() is only the TLV body. + */ + uint16_t tlv_length = + tlv_encoder(tlv_hdr, versioning, buf + TLV_HEADER_LENGTH); + write_tlv_header(tlv_hdr, tlv_length, versioning, buf); + tlv_hdr->encoded_tlv = buf; + tlv_hdr->encoded_tlv_length = tlv_length; + + return normalize_pcep_tlv_length(tlv_length + TLV_HEADER_LENGTH); +} + +/* TLV Header format + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type (2 bytes) | Length (2 bytes) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Value (Variable) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +void write_tlv_header(struct pcep_object_tlv_header *tlv_hdr, + uint16_t tlv_length, struct pcep_versioning *versioning, + uint8_t *buf) +{ + (void)versioning; + uint16_t *uint16_ptr = (uint16_t *)buf; + uint16_ptr[0] = htons(tlv_hdr->type); + uint16_ptr[1] = htons(tlv_length); +} + +/* + * Functions to encode TLVs + */ + +uint16_t pcep_encode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_nopath_vector *nopath_tlv = + (struct pcep_object_tlv_nopath_vector *)tlv; + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + *uint32_ptr = htonl(nopath_tlv->error_code); + + return LENGTH_1WORD; +} + +uint16_t +pcep_encode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_stateful_pce_capability *spc_tlv = + (struct pcep_object_tlv_stateful_pce_capability *)tlv; + tlv_body_buf[3] = + ((spc_tlv->flag_f_triggered_initial_sync == true + ? TLV_STATEFUL_PCE_CAP_FLAG_F + : 0x00) + | (spc_tlv->flag_d_delta_lsp_sync == true + ? TLV_STATEFUL_PCE_CAP_FLAG_D + : 0x00) + | (spc_tlv->flag_t_triggered_resync == true + ? TLV_STATEFUL_PCE_CAP_FLAG_T + : 0x00) + | (spc_tlv->flag_i_lsp_instantiation_capability == true + ? TLV_STATEFUL_PCE_CAP_FLAG_I + : 0x00) + | (spc_tlv->flag_s_include_db_version == true + ? TLV_STATEFUL_PCE_CAP_FLAG_S + : 0x00) + | (spc_tlv->flag_u_lsp_update_capability == true + ? TLV_STATEFUL_PCE_CAP_FLAG_U + : 0x00)); + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_symbolic_path_name *spn_tlv = + (struct pcep_object_tlv_symbolic_path_name *)tlv; + memcpy(tlv_body_buf, spn_tlv->symbolic_path_name, + spn_tlv->symbolic_path_name_length); + + return spn_tlv->symbolic_path_name_length; +} + +uint16_t +pcep_encode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_ipv4_lsp_identifier *ipv4_lsp = + (struct pcep_object_tlv_ipv4_lsp_identifier *)tlv; + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + uint32_ptr[0] = ipv4_lsp->ipv4_tunnel_sender.s_addr; + /* uint32_t[1] is lsp_id and tunnel_id, below */ + uint32_ptr[2] = ipv4_lsp->extended_tunnel_id.s_addr; + uint32_ptr[3] = ipv4_lsp->ipv4_tunnel_endpoint.s_addr; + + uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_1WORD); + uint16_ptr[0] = htons(ipv4_lsp->lsp_id); + uint16_ptr[1] = htons(ipv4_lsp->tunnel_id); + + return LENGTH_4WORDS; +} + +uint16_t +pcep_encode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_ipv6_lsp_identifier *ipv6_lsp = + (struct pcep_object_tlv_ipv6_lsp_identifier *)tlv; + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + encode_ipv6(&ipv6_lsp->ipv6_tunnel_sender, uint32_ptr); + encode_ipv6(&ipv6_lsp->extended_tunnel_id, uint32_ptr + 5); + encode_ipv6(&ipv6_lsp->ipv6_tunnel_endpoint, uint32_ptr + 9); + + uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_4WORDS); + uint16_ptr[0] = htons(ipv6_lsp->lsp_id); + uint16_ptr[1] = htons(ipv6_lsp->tunnel_id); + + return LENGTH_13WORDS; +} + +uint16_t pcep_encode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_lsp_error_code *lsp_error_tlv = + (struct pcep_object_tlv_lsp_error_code *)tlv; + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + *uint32_ptr = htonl(lsp_error_tlv->lsp_error_code); + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + /* Same decode tlv function for both types: + pcep_create_tlv_rsvp_ipv4_error_spec(tlv); + pcep_create_tlv_rsvp_ipv6_error_spec(tlv); */ + + /* RSVP Object Header + * + * 0 1 2 3 + * +-------------+-------------+-------------+-------------+ + * | Length (bytes) | Class-Num | C-Type | + * +-------------+-------------+-------------+-------------+ + * | | + * // (Object contents) // + * | | + * +-------------+-------------+-------------+-------------+ + * + * IPv4 ERROR_SPEC object: Class = 6, C-Type = 1 + * +-------------+-------------+-------------+-------------+ + * | IPv4 Error Node Address (4 bytes) | + * +-------------+-------------+-------------+-------------+ + * | Flags | Error Code | Error Value | + * +-------------+-------------+-------------+-------------+ + * + * IPv6 ERROR_SPEC object: Class = 6, C-Type = 2 + * +-------------+-------------+-------------+-------------+ + * | IPv6 Error Node Address (16 bytes) | + * +-------------+-------------+-------------+-------------+ + * | Flags | Error Code | Error Value | + * +-------------+-------------+-------------+-------------+ + */ + + (void)versioning; + struct pcep_object_tlv_rsvp_error_spec *rsvp_hdr = + (struct pcep_object_tlv_rsvp_error_spec *)tlv; + tlv_body_buf[2] = rsvp_hdr->class_num; + tlv_body_buf[3] = rsvp_hdr->c_type; + + uint16_t *length_ptr = (uint16_t *)tlv_body_buf; + uint32_t *uint32_ptr = (uint32_t *)(tlv_body_buf + LENGTH_1WORD); + if (rsvp_hdr->c_type == RSVP_ERROR_SPEC_IPV4_CTYPE) { + *length_ptr = htons(LENGTH_3WORDS); + *uint32_ptr = + rsvp_hdr->error_spec_ip.ipv4_error_node_address.s_addr; + tlv_body_buf[LENGTH_2WORDS + 1] = rsvp_hdr->error_code; + uint16_t *uint16_ptr = + (uint16_t *)(tlv_body_buf + LENGTH_2WORDS + 2); + *uint16_ptr = htons(rsvp_hdr->error_value); + + return LENGTH_3WORDS; + } else if (rsvp_hdr->c_type == RSVP_ERROR_SPEC_IPV6_CTYPE) { + *length_ptr = htons(LENGTH_6WORDS); + encode_ipv6(&rsvp_hdr->error_spec_ip.ipv6_error_node_address, + uint32_ptr); + tlv_body_buf[LENGTH_5WORDS + 1] = rsvp_hdr->error_code; + uint16_t *uint16_ptr = + (uint16_t *)(tlv_body_buf + LENGTH_5WORDS + 2); + *uint16_ptr = htons(rsvp_hdr->error_value); + + return LENGTH_6WORDS; + } + + return 0; +} + +uint16_t pcep_encode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_lsp_db_version *lsp_db_ver = + (struct pcep_object_tlv_lsp_db_version *)tlv; + *((uint64_t *)tlv_body_buf) = htobe64(lsp_db_ver->lsp_db_version); + + return LENGTH_2WORDS; +} + +uint16_t pcep_encode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_speaker_entity_identifier *speaker_id = + (struct pcep_object_tlv_speaker_entity_identifier *)tlv; + if (speaker_id->speaker_entity_id_list == NULL) { + return 0; + } + + int index = 0; + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + double_linked_list_node *node = + speaker_id->speaker_entity_id_list->head; + for (; node != NULL; node = node->next_node) { + uint32_ptr[index++] = htonl(*((uint32_t *)node->data)); + } + + return speaker_id->speaker_entity_id_list->num_entries * LENGTH_1WORD; +} + +uint16_t pcep_encode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_sr_pce_capability *sr_pce_cap = + (struct pcep_object_tlv_sr_pce_capability *)tlv; + tlv_body_buf[2] = + ((sr_pce_cap->flag_n == true ? TLV_SR_PCE_CAP_FLAG_N : 0x00) + | (sr_pce_cap->flag_x == true ? TLV_SR_PCE_CAP_FLAG_X : 0x00)); + tlv_body_buf[3] = sr_pce_cap->max_sid_depth; + + return LENGTH_1WORD; +} + +uint16_t pcep_encode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_path_setup_type *pst = + (struct pcep_object_tlv_path_setup_type *)tlv; + tlv_body_buf[3] = pst->path_setup_type; + + return LENGTH_1WORD; +} + +uint16_t +pcep_encode_tlv_path_setup_type_capability(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_path_setup_type_capability *pst_cap = + (struct pcep_object_tlv_path_setup_type_capability *)tlv; + if (pst_cap->pst_list == NULL) { + return 0; + } + + tlv_body_buf[3] = pst_cap->pst_list->num_entries; + + /* Index past the reserved and NumPSTs fields */ + int index = 4; + double_linked_list_node *node = pst_cap->pst_list->head; + for (; node != NULL; node = node->next_node) { + tlv_body_buf[index++] = *((uint8_t *)node->data); + } + + uint16_t pst_length = normalize_pcep_tlv_length( + LENGTH_1WORD + pst_cap->pst_list->num_entries); + if (pst_cap->sub_tlv_list == NULL) { + return pst_length; + } + + /* Any padding used for the PSTs should not be included in the tlv + * header length */ + index = normalize_pcep_tlv_length(index); + uint16_t sub_tlvs_length = 0; + node = pst_cap->sub_tlv_list->head; + for (; node != NULL; node = node->next_node) { + struct pcep_object_tlv_header *sub_tlv = + (struct pcep_object_tlv_header *)node->data; + uint16_t sub_tlv_length = pcep_encode_tlv(sub_tlv, versioning, + tlv_body_buf + index); + index += sub_tlv_length; + sub_tlvs_length += sub_tlv_length; + } + + return sub_tlvs_length + pst_length; +} +uint16_t pcep_encode_tlv_pol_id(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + struct pcep_object_tlv_srpag_pol_id *ipv4 = + (struct pcep_object_tlv_srpag_pol_id *)tlv; + if (ipv4->is_ipv4) { + uint32_ptr[0] = htonl(ipv4->color); + uint32_ptr[1] = ipv4->end_point.ipv4.s_addr; + return LENGTH_2WORDS; + } else { + struct pcep_object_tlv_srpag_pol_id *ipv6 = + (struct pcep_object_tlv_srpag_pol_id *)tlv; + uint32_ptr[0] = htonl(ipv6->color); + encode_ipv6(&ipv6->end_point.ipv6, &uint32_ptr[1]); + return LENGTH_5WORDS; + } +} + +uint16_t pcep_encode_tlv_pol_name(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_srpag_pol_name *pol_name_tlv = + (struct pcep_object_tlv_srpag_pol_name *)tlv; + memcpy(tlv_body_buf, pol_name_tlv->name, pol_name_tlv->name_length); + + return normalize_pcep_tlv_length(pol_name_tlv->name_length); +} + +uint16_t pcep_encode_tlv_cpath_id(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_srpag_cp_id *cpath_id_tlv = + (struct pcep_object_tlv_srpag_cp_id *)tlv; + + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + tlv_body_buf[0] = cpath_id_tlv->proto; + uint32_ptr[1] = htonl(cpath_id_tlv->orig_asn); + encode_ipv6(&cpath_id_tlv->orig_addres, &uint32_ptr[2]); + uint32_ptr[6] = htonl(cpath_id_tlv->discriminator); + + return sizeof(cpath_id_tlv->proto) + sizeof(cpath_id_tlv->orig_asn) + + sizeof(cpath_id_tlv->orig_addres) + + sizeof(cpath_id_tlv->discriminator); +} + +uint16_t pcep_encode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_srpag_cp_pref *cpath_pref_tlv = + (struct pcep_object_tlv_srpag_cp_pref *)tlv; + + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + uint32_ptr[0] = htonl(cpath_pref_tlv->preference); + + return sizeof(cpath_pref_tlv->preference); +} + +uint16_t pcep_encode_tlv_vendor_info(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_vendor_info *vendor_info = + (struct pcep_object_tlv_vendor_info *)tlv; + + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + uint32_ptr[0] = htonl(vendor_info->enterprise_number); + uint32_ptr[1] = htonl(vendor_info->enterprise_specific_info); + + return LENGTH_2WORDS; +} + +uint16_t pcep_encode_tlv_arbitrary(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_arbitrary *tlv_arbitrary = + (struct pcep_object_tlv_arbitrary *)tlv; + memcpy(tlv_body_buf, tlv_arbitrary->data, tlv_arbitrary->data_length); + tlv->type = tlv_arbitrary->arbitraty_type; + + return tlv_arbitrary->data_length; +} + +uint16_t pcep_encode_tlv_of_list(struct pcep_object_tlv_header *tlv, + struct pcep_versioning *versioning, + uint8_t *tlv_body_buf) +{ + (void)versioning; + struct pcep_object_tlv_of_list *of_list = + (struct pcep_object_tlv_of_list *)tlv; + + if (of_list->of_list == NULL) { + return 0; + } + + int index = 0; + double_linked_list_node *node = of_list->of_list->head; + while (node != NULL) { + uint16_t *of_code = (uint16_t *)node->data; + if (of_code == NULL) { + return 0; + } + + uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + index); + *uint16_ptr = *of_code; + index += 2; + + node = node->next_node; + } + + return of_list->of_list->num_entries * 2; +} + +/* + * Decoding functions + */ + +void pcep_decode_tlv_hdr(const uint8_t *tlv_buf, + struct pcep_object_tlv_header *tlv_hdr) +{ + memset(tlv_hdr, 0, sizeof(struct pcep_object_tlv_header)); + + uint16_t *uint16_ptr = (uint16_t *)tlv_buf; + tlv_hdr->type = ntohs(uint16_ptr[0]); + tlv_hdr->encoded_tlv_length = ntohs(uint16_ptr[1]); + tlv_hdr->encoded_tlv = tlv_buf; +} + +struct pcep_object_tlv_header *pcep_decode_tlv(const uint8_t *tlv_buf) +{ + initialize_tlv_coders(); + + struct pcep_object_tlv_header tlv_hdr; + /* Only initializes and decodes the Object Header: class, type, flags, + * and length */ + pcep_decode_tlv_hdr(tlv_buf, &tlv_hdr); + + if (tlv_hdr.type >= MAX_TLV_ENCODER_INDEX) { + pcep_log(LOG_INFO, "%s: Cannot decode unknown TLV type [%d]", + __func__, tlv_hdr.type); + return NULL; + } + + tlv_decoder_funcptr tlv_decoder = NULL; + if (tlv_hdr.type == PCEP_OBJ_TYPE_CISCO_BSID) { + pcep_log(LOG_INFO, + "%s: Cisco BSID TLV decoder found for TLV type [%d]", + __func__, tlv_hdr.type); + tlv_decoder = tlv_decoders[PCEP_OBJ_TLV_TYPE_ARBITRARY]; + } else { + tlv_decoder = tlv_decoders[tlv_hdr.type]; + } + if (tlv_decoder == NULL) { + pcep_log(LOG_INFO, "%s: No TLV decoder found for TLV type [%d]", + __func__, tlv_hdr.type); + return NULL; + } + + return tlv_decoder(&tlv_hdr, tlv_buf + LENGTH_1WORD); +} + +static struct pcep_object_tlv_header * +common_tlv_create(struct pcep_object_tlv_header *hdr, uint16_t new_tlv_length) +{ + struct pcep_object_tlv_header *new_tlv = + pceplib_malloc(PCEPLIB_MESSAGES, new_tlv_length); + memset(new_tlv, 0, new_tlv_length); + memcpy(new_tlv, hdr, sizeof(struct pcep_object_tlv_header)); + + return new_tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_no_path_vector(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_nopath_vector *tlv = + (struct pcep_object_tlv_nopath_vector *)common_tlv_create( + tlv_hdr, sizeof(struct pcep_object_tlv_nopath_vector)); + + tlv->error_code = ntohl(*((uint32_t *)tlv_body_buf)); + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_stateful_pce_capability(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_stateful_pce_capability *tlv = + (struct pcep_object_tlv_stateful_pce_capability *) + common_tlv_create( + tlv_hdr, + sizeof(struct + pcep_object_tlv_stateful_pce_capability)); + + tlv->flag_f_triggered_initial_sync = + (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_F); + tlv->flag_d_delta_lsp_sync = + (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_D); + tlv->flag_t_triggered_resync = + (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_T); + tlv->flag_i_lsp_instantiation_capability = + (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_I); + tlv->flag_s_include_db_version = + (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_S); + tlv->flag_u_lsp_update_capability = + (tlv_body_buf[3] & TLV_STATEFUL_PCE_CAP_FLAG_U); + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_symbolic_path_name(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_symbolic_path_name *tlv = + (struct pcep_object_tlv_symbolic_path_name *)common_tlv_create( + tlv_hdr, + sizeof(struct pcep_object_tlv_symbolic_path_name)); + + uint16_t length = tlv_hdr->encoded_tlv_length; + if (length > MAX_SYMBOLIC_PATH_NAME) { + /* TODO should we also reset the tlv_hdr->encoded_tlv_length ? + */ + length = MAX_SYMBOLIC_PATH_NAME; + pcep_log( + LOG_INFO, + "%s: Decoding Symbolic Path Name TLV, truncate path name from [%d] to [%d].\",", + __func__, tlv_hdr->encoded_tlv_length, + MAX_SYMBOLIC_PATH_NAME); + } + + tlv->symbolic_path_name_length = length; + memcpy(tlv->symbolic_path_name, tlv_body_buf, length); + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_ipv4_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_ipv4_lsp_identifier *tlv = + (struct pcep_object_tlv_ipv4_lsp_identifier *)common_tlv_create( + tlv_hdr, + sizeof(struct pcep_object_tlv_ipv4_lsp_identifier)); + + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + tlv->ipv4_tunnel_sender.s_addr = uint32_ptr[0]; + /* uint32_t[1] is lsp_id and tunnel_id, below */ + tlv->extended_tunnel_id.s_addr = uint32_ptr[2]; + tlv->ipv4_tunnel_endpoint.s_addr = uint32_ptr[3]; + + uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_1WORD); + tlv->lsp_id = ntohs(uint16_ptr[0]); + tlv->tunnel_id = ntohs(uint16_ptr[1]); + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_ipv6_lsp_identifiers(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_ipv6_lsp_identifier *tlv = + (struct pcep_object_tlv_ipv6_lsp_identifier *)common_tlv_create( + tlv_hdr, + sizeof(struct pcep_object_tlv_ipv6_lsp_identifier)); + + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + decode_ipv6(uint32_ptr, &tlv->ipv6_tunnel_sender); + decode_ipv6(uint32_ptr + 5, &tlv->extended_tunnel_id); + decode_ipv6(uint32_ptr + 9, &tlv->ipv6_tunnel_endpoint); + + uint16_t *uint16_ptr = (uint16_t *)(tlv_body_buf + LENGTH_4WORDS); + tlv->lsp_id = htons(uint16_ptr[0]); + tlv->tunnel_id = htons(uint16_ptr[1]); + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_lsp_error_code(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_lsp_error_code *tlv = + (struct pcep_object_tlv_lsp_error_code *)common_tlv_create( + tlv_hdr, sizeof(struct pcep_object_tlv_lsp_error_code)); + + tlv->lsp_error_code = ntohl(*((uint32_t *)tlv_body_buf)); + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_rsvp_error_spec(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + uint8_t class_num = tlv_body_buf[2]; + uint8_t ctype = tlv_body_buf[3]; + + if (class_num != RSVP_ERROR_SPEC_CLASS_NUM) { + pcep_log( + LOG_INFO, + "%s: Decoding RSVP Error Spec TLV, unknown class num [%d]", + __func__, class_num); + return NULL; + } + + if (ctype != RSVP_ERROR_SPEC_IPV4_CTYPE + && ctype != RSVP_ERROR_SPEC_IPV6_CTYPE) { + pcep_log(LOG_INFO, + "%s: Decoding RSVP Error Spec TLV, unknown ctype [%d]", + __func__, ctype); + return NULL; + } + + struct pcep_object_tlv_rsvp_error_spec *tlv = + (struct pcep_object_tlv_rsvp_error_spec *)common_tlv_create( + tlv_hdr, + sizeof(struct pcep_object_tlv_rsvp_error_spec)); + + tlv->class_num = class_num; + tlv->c_type = ctype; + + uint32_t *uint32_ptr = (uint32_t *)(tlv_body_buf + LENGTH_1WORD); + if (ctype == RSVP_ERROR_SPEC_IPV4_CTYPE) { + tlv->error_spec_ip.ipv4_error_node_address.s_addr = *uint32_ptr; + tlv->error_code = tlv_body_buf[LENGTH_2WORDS + 1]; + tlv->error_value = ntohs( + *((uint16_t *)(tlv_body_buf + LENGTH_2WORDS + 2))); + } else /* RSVP_ERROR_SPEC_IPV6_CTYPE */ + { + decode_ipv6(uint32_ptr, + &tlv->error_spec_ip.ipv6_error_node_address); + tlv->error_code = tlv_body_buf[LENGTH_5WORDS + 1]; + tlv->error_value = ntohs( + *((uint16_t *)(tlv_body_buf + LENGTH_5WORDS + 2))); + } + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_lsp_db_version(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_lsp_db_version *tlv = + (struct pcep_object_tlv_lsp_db_version *)common_tlv_create( + tlv_hdr, sizeof(struct pcep_object_tlv_lsp_db_version)); + + tlv->lsp_db_version = be64toh(*((uint64_t *)tlv_body_buf)); + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_speaker_entity_id(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_speaker_entity_identifier *tlv = + (struct pcep_object_tlv_speaker_entity_identifier *) + common_tlv_create( + tlv_hdr, + sizeof(struct + pcep_object_tlv_speaker_entity_identifier)); + + uint8_t num_entity_ids = tlv_hdr->encoded_tlv_length / LENGTH_1WORD; + if (num_entity_ids > MAX_ITERATIONS) { + num_entity_ids = MAX_ITERATIONS; + pcep_log( + LOG_INFO, + "%s: Decode Speaker Entity ID, truncating num entities from [%d] to [%d].", + __func__, num_entity_ids, MAX_ITERATIONS); + } + + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + tlv->speaker_entity_id_list = dll_initialize(); + int i; + for (i = 0; i < num_entity_ids; i++) { + uint32_t *entity_id = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t)); + *entity_id = ntohl(uint32_ptr[i]); + dll_append(tlv->speaker_entity_id_list, entity_id); + } + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_sr_pce_capability(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_sr_pce_capability *tlv = + (struct pcep_object_tlv_sr_pce_capability *)common_tlv_create( + tlv_hdr, + sizeof(struct pcep_object_tlv_sr_pce_capability)); + + tlv->flag_n = (tlv_body_buf[2] & TLV_SR_PCE_CAP_FLAG_N); + tlv->flag_x = (tlv_body_buf[2] & TLV_SR_PCE_CAP_FLAG_X); + tlv->max_sid_depth = tlv_body_buf[3]; + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_path_setup_type(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_path_setup_type *tlv = + (struct pcep_object_tlv_path_setup_type *)common_tlv_create( + tlv_hdr, + sizeof(struct pcep_object_tlv_path_setup_type)); + + tlv->path_setup_type = tlv_body_buf[3]; + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header *pcep_decode_tlv_path_setup_type_capability( + struct pcep_object_tlv_header *tlv_hdr, const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_path_setup_type_capability *tlv = + (struct pcep_object_tlv_path_setup_type_capability *) + common_tlv_create( + tlv_hdr, + sizeof(struct + pcep_object_tlv_path_setup_type_capability)); + + uint8_t num_psts = tlv_body_buf[3]; + if (num_psts > MAX_ITERATIONS) { + pcep_log( + LOG_INFO, + "%s: Decode Path Setup Type Capability num PSTs [%d] exceeds MAX [%d] continuing anyways", + __func__, num_psts, MAX_ITERATIONS); + } + + int i; + tlv->pst_list = dll_initialize(); + for (i = 0; i < num_psts; i++) { + uint8_t *pst = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint8_t)); + *pst = tlv_body_buf[i + LENGTH_1WORD]; + dll_append(tlv->pst_list, pst); + } + + if (tlv->header.encoded_tlv_length + == (TLV_HEADER_LENGTH + LENGTH_1WORD + num_psts)) { + return (struct pcep_object_tlv_header *)tlv; + } + + uint8_t num_iterations = 0; + tlv->sub_tlv_list = dll_initialize(); + uint16_t buf_index = normalize_pcep_tlv_length( + TLV_HEADER_LENGTH + LENGTH_1WORD + num_psts); + while ((tlv->header.encoded_tlv_length - buf_index) > TLV_HEADER_LENGTH + && num_iterations++ < MAX_ITERATIONS) { + struct pcep_object_tlv_header *sub_tlv = + pcep_decode_tlv(tlv_body_buf + buf_index); + if (sub_tlv == NULL) { + pcep_log( + LOG_INFO, + "%s: Decode PathSetupType Capability sub-TLV decode returned NULL", + __func__); + return (struct pcep_object_tlv_header *)tlv; + } + + buf_index += + normalize_pcep_tlv_length(sub_tlv->encoded_tlv_length); + dll_append(tlv->sub_tlv_list, sub_tlv); + } + + return (struct pcep_object_tlv_header *)tlv; +} +struct pcep_object_tlv_header * +pcep_decode_tlv_pol_id(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + struct pcep_object_tlv_srpag_pol_id *ipv4 = + (struct pcep_object_tlv_srpag_pol_id *)common_tlv_create( + tlv_hdr, sizeof(struct pcep_object_tlv_srpag_pol_id)); + if (tlv_hdr->encoded_tlv_length == 8) { + ipv4->is_ipv4 = true; + ipv4->color = ntohl(uint32_ptr[0]); + ipv4->end_point.ipv4.s_addr = uint32_ptr[1]; + return (struct pcep_object_tlv_header *)ipv4; + } else { + ipv4->is_ipv4 = false; + struct pcep_object_tlv_srpag_pol_id *ipv6 = ipv4; + ipv6->color = ntohl(uint32_ptr[0]); + decode_ipv6(&uint32_ptr[1], &ipv6->end_point.ipv6); + return (struct pcep_object_tlv_header *)ipv6; + } +} +struct pcep_object_tlv_header * +pcep_decode_tlv_pol_name(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_srpag_pol_name *tlv = + (struct pcep_object_tlv_srpag_pol_name *)common_tlv_create( + tlv_hdr, sizeof(struct pcep_object_tlv_srpag_pol_name)); + + memcpy(tlv->name, tlv_body_buf, tlv->header.encoded_tlv_length); + + return (struct pcep_object_tlv_header *)tlv; +} +struct pcep_object_tlv_header * +pcep_decode_tlv_cpath_id(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + struct pcep_object_tlv_srpag_cp_id *tlv = + (struct pcep_object_tlv_srpag_cp_id *)common_tlv_create( + tlv_hdr, sizeof(struct pcep_object_tlv_srpag_cp_id)); + + tlv->proto = tlv_body_buf[0]; + tlv->orig_asn = ntohl(uint32_ptr[1]); + decode_ipv6(&uint32_ptr[2], &tlv->orig_addres); + tlv->discriminator = ntohl(uint32_ptr[6]); + + return (struct pcep_object_tlv_header *)tlv; +} +struct pcep_object_tlv_header * +pcep_decode_tlv_cpath_preference(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + struct pcep_object_tlv_srpag_cp_pref *tlv = + (struct pcep_object_tlv_srpag_cp_pref *)common_tlv_create( + tlv_hdr, sizeof(struct pcep_object_tlv_srpag_cp_pref)); + + tlv->preference = ntohl(uint32_ptr[0]); + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_vendor_info(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_vendor_info *tlv = + (struct pcep_object_tlv_vendor_info *)common_tlv_create( + tlv_hdr, sizeof(struct pcep_object_tlv_vendor_info)); + + uint32_t *uint32_ptr = (uint32_t *)tlv_body_buf; + tlv->enterprise_number = ntohl(uint32_ptr[0]); + tlv->enterprise_specific_info = ntohl(uint32_ptr[1]); + + return (struct pcep_object_tlv_header *)tlv; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_arbitrary(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_arbitrary *tlv_arbitrary = + (struct pcep_object_tlv_arbitrary *)common_tlv_create( + tlv_hdr, sizeof(struct pcep_object_tlv_arbitrary)); + + uint16_t length = tlv_hdr->encoded_tlv_length; + if (length > MAX_ARBITRARY_SIZE) { + /* TODO should we also reset the tlv_hdr->encoded_tlv_length ? + */ + length = MAX_ARBITRARY_SIZE; + pcep_log( + LOG_INFO, + "%s: Decoding Arbitrary TLV , truncate path name from [%d] to [%d].\",", + __func__, tlv_hdr->encoded_tlv_length, + MAX_ARBITRARY_SIZE); + } + + tlv_arbitrary->data_length = length; + tlv_arbitrary->arbitraty_type = tlv_hdr->type; + tlv_hdr->type = PCEP_OBJ_TLV_TYPE_ARBITRARY; + memcpy(tlv_arbitrary->data, tlv_body_buf, length); + + return (struct pcep_object_tlv_header *)tlv_arbitrary; +} + +struct pcep_object_tlv_header * +pcep_decode_tlv_of_list(struct pcep_object_tlv_header *tlv_hdr, + const uint8_t *tlv_body_buf) +{ + struct pcep_object_tlv_of_list *of_tlv = + (struct pcep_object_tlv_of_list *)common_tlv_create( + tlv_hdr, sizeof(struct pcep_object_tlv_of_list)); + + of_tlv->of_list = dll_initialize(); + uint16_t *uint16_ptr = (uint16_t *)tlv_body_buf; + int i = 0; + for (; i < tlv_hdr->encoded_tlv_length && i < MAX_ITERATIONS; i++) { + uint16_t *of_code_ptr = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint16_t)); + *of_code_ptr = ntohs(uint16_ptr[i]); + dll_append(of_tlv->of_list, of_code_ptr); + } + + return (struct pcep_object_tlv_header *)of_tlv; +} diff --git a/pceplib/pcep_msg_tools.c b/pceplib/pcep_msg_tools.c new file mode 100644 index 0000000..f7c25f4 --- /dev/null +++ b/pceplib/pcep_msg_tools.c @@ -0,0 +1,484 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "pcep_msg_tools.h" +#include "pcep_msg_encoding.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +static const char *message_type_strs[] = {"NOT_IMPLEMENTED0", + "OPEN", + "KEEPALIVE", + "PCREQ", + "PCREP", + "PCNOTF", + "ERROR", + "CLOSE", + "NOT_IMPLEMENTED8", + "NOT_IMPLEMENTED9", + "REPORT", + "UPDATE", + "INITIATE", + "UNKOWN_MESSAGE_TYPE"}; + +static const char *object_class_strs[] = {"NOT_IMPLEMENTED0", + "OPEN", + "RP", + "NOPATH", + "ENDPOINTS", + "BANDWIDTH", + "METRIC", + "ERO", + "RRO", + "LSPA", + "IRO", + "SVEC", + "NOTF", + "ERROR", + "NOT_IMPLEMENTED14", + "CLOSE", + "NOT_IMPLEMENTED16", + "NOT_IMPLEMENTED17", + "NOT_IMPLEMENTED18", + "NOT_IMPLEMENTED19", + "NOT_IMPLEMENTED20", + "OBJECTIVE_FUNCTION", + "NOT_IMPLEMENTED22", + "NOT_IMPLEMENTED23", + "NOT_IMPLEMENTED24", + "NOT_IMPLEMENTED25", + "NOT_IMPLEMENTED26", + "NOT_IMPLEMENTED27", + "NOT_IMPLEMENTED28", + "NOT_IMPLEMENTED29", + "NOT_IMPLEMENTED30", + "NOT_IMPLEMENTED31", + "LSP", + "SRP", + "VENDOR_INFO", + "NOT_IMPLEMENTED35", + "INTER_LAYER", + "SWITCH_LAYER", + "REQ_ADAP_CAP", + "SERVER_IND", + "ASSOCIATION", /* 40 */ + "UNKNOWN_MESSAGE_TYPE"}; + + +double_linked_list *pcep_msg_read(int sock_fd) +{ + int ret; + uint8_t buffer[PCEP_MESSAGE_LENGTH] = {0}; + uint16_t buffer_read = 0; + + + ret = read(sock_fd, &buffer, PCEP_MESSAGE_LENGTH); + + if (ret < 0) { + pcep_log( + LOG_INFO, + "%s: pcep_msg_read: Failed to read from socket fd [%d] errno [%d %s]", + __func__, sock_fd, errno, strerror(errno)); + return NULL; + } else if (ret == 0) { + pcep_log(LOG_INFO, "%s: pcep_msg_read: Remote shutdown fd [%d]", + __func__, sock_fd); + return NULL; + } + + double_linked_list *msg_list = dll_initialize(); + struct pcep_message *msg = NULL; + + while (((uint16_t)ret - buffer_read) >= MESSAGE_HEADER_LENGTH) { + + /* Get the Message header, validate it, and return the msg + * length */ + int32_t msg_length = + pcep_decode_validate_msg_header(buffer + buffer_read); + if (msg_length < 0 || msg_length > PCEP_MESSAGE_LENGTH) { + /* If the message header is invalid, we cant keep + * reading since the length may be invalid */ + pcep_log( + LOG_INFO, + "%s: pcep_msg_read: Received an invalid message fd [%d]", + __func__, sock_fd); + return msg_list; + } + + /* Check if the msg_length is longer than what was read, + * in which case, we need to read the rest of the message. */ + if ((ret - buffer_read) < msg_length) { + int read_len = (msg_length - (ret - buffer_read)); + int read_ret = 0; + pcep_log( + LOG_INFO, + "%s: pcep_msg_read: Message not fully read! Trying to read %d bytes more, fd [%d]", + __func__, read_len, sock_fd); + + if (PCEP_MESSAGE_LENGTH - ret - buffer_read >= read_len) + read_ret = + read(sock_fd, &buffer[ret], read_len); + else { + pcep_log( + LOG_ERR, + "%s: Trying to read size (%d) offset (%d) in a buff of size (%d)", + __func__, read_len, ret, + PCEP_MESSAGE_LENGTH); + return msg_list; + } + + if (read_ret != read_len) { + pcep_log( + LOG_INFO, + "%s: pcep_msg_read: Did not manage to read enough data (%d != %d) fd [%d]", + __func__, read_ret, read_len, sock_fd); + return msg_list; + } + } + + msg = pcep_decode_message(buffer + buffer_read); + buffer_read += msg_length; + + if (msg == NULL) { + return msg_list; + } else { + dll_append(msg_list, msg); + } + } + + return msg_list; +} + +struct pcep_message *pcep_msg_get(double_linked_list *msg_list, uint8_t type) +{ + if (msg_list == NULL) { + return NULL; + } + + double_linked_list_node *node; + for (node = msg_list->head; node != NULL; node = node->next_node) { + if (((struct pcep_message *)node->data)->msg_header->type + == type) { + return (struct pcep_message *)node->data; + } + } + + return NULL; +} + +struct pcep_message *pcep_msg_get_next(double_linked_list *list, + struct pcep_message *current, + uint8_t type) +{ + if (list == NULL || current == NULL) { + return NULL; + } + + if (list->head == NULL) { + return NULL; + } + + double_linked_list_node *node; + for (node = list->head; node != NULL; node = node->next_node) { + if (node->data == current) { + continue; + } + + if (((struct pcep_message *)node->data)->msg_header->type + == type) { + return (struct pcep_message *)node->data; + } + } + + return NULL; +} + +struct pcep_object_header *pcep_obj_get(double_linked_list *list, + uint8_t object_class) +{ + if (list == NULL) { + return NULL; + } + + if (list->head == NULL) { + return NULL; + } + + double_linked_list_node *obj_item; + for (obj_item = list->head; obj_item != NULL; + obj_item = obj_item->next_node) { + if (((struct pcep_object_header *)obj_item->data)->object_class + == object_class) { + return (struct pcep_object_header *)obj_item->data; + } + } + + return NULL; +} + +struct pcep_object_header *pcep_obj_get_next(double_linked_list *list, + struct pcep_object_header *current, + uint8_t object_class) +{ + if (list == NULL || current == NULL) { + return NULL; + } + + if (list->head == NULL) { + return NULL; + } + + double_linked_list_node *node; + for (node = list->head; node != NULL; node = node->next_node) { + if (node->data == current) { + continue; + } + + if (((struct pcep_object_header *)node->data)->object_class + == object_class) { + return (struct pcep_object_header *)node->data; + } + } + + return NULL; +} + +void pcep_obj_free_tlv(struct pcep_object_tlv_header *tlv) +{ + /* Specific TLV freeing */ + switch (tlv->type) { + case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID: + if (((struct pcep_object_tlv_speaker_entity_identifier *)tlv) + ->speaker_entity_id_list + != NULL) { + dll_destroy_with_data_memtype( + ((struct + pcep_object_tlv_speaker_entity_identifier *) + tlv) + ->speaker_entity_id_list, + PCEPLIB_MESSAGES); + } + break; + + case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY: + if (((struct pcep_object_tlv_path_setup_type_capability *)tlv) + ->pst_list + != NULL) { + dll_destroy_with_data_memtype( + ((struct + pcep_object_tlv_path_setup_type_capability *) + tlv) + ->pst_list, + PCEPLIB_MESSAGES); + } + + if (((struct pcep_object_tlv_path_setup_type_capability *)tlv) + ->sub_tlv_list + != NULL) { + dll_destroy_with_data_memtype( + ((struct + pcep_object_tlv_path_setup_type_capability *) + tlv) + ->sub_tlv_list, + PCEPLIB_MESSAGES); + } + break; + + default: + break; + } + + pceplib_free(PCEPLIB_MESSAGES, tlv); +} + +void pcep_obj_free_object(struct pcep_object_header *obj) +{ + /* Iterate the TLVs and free each one */ + if (obj->tlv_list != NULL) { + struct pcep_object_tlv_header *tlv; + while ((tlv = (struct pcep_object_tlv_header *) + dll_delete_first_node(obj->tlv_list)) + != NULL) { + pcep_obj_free_tlv(tlv); + } + + dll_destroy(obj->tlv_list); + } + + /* Specific object freeing */ + switch (obj->object_class) { + case PCEP_OBJ_CLASS_ERO: + case PCEP_OBJ_CLASS_IRO: + case PCEP_OBJ_CLASS_RRO: { + if (((struct pcep_object_ro *)obj)->sub_objects != NULL) { + double_linked_list_node *node = + ((struct pcep_object_ro *)obj) + ->sub_objects->head; + for (; node != NULL; node = node->next_node) { + struct pcep_object_ro_subobj *ro_subobj = + (struct pcep_object_ro_subobj *) + node->data; + if (ro_subobj->ro_subobj_type + == RO_SUBOBJ_TYPE_SR) { + if (((struct pcep_ro_subobj_sr *) + ro_subobj) + ->nai_list + != NULL) { + dll_destroy_with_data_memtype( + ((struct + pcep_ro_subobj_sr *) + ro_subobj) + ->nai_list, + PCEPLIB_MESSAGES); + } + } + } + dll_destroy_with_data_memtype( + ((struct pcep_object_ro *)obj)->sub_objects, + PCEPLIB_MESSAGES); + } + } break; + + case PCEP_OBJ_CLASS_SVEC: + if (((struct pcep_object_svec *)obj)->request_id_list != NULL) { + dll_destroy_with_data_memtype( + ((struct pcep_object_svec *)obj) + ->request_id_list, + PCEPLIB_MESSAGES); + } + break; + + case PCEP_OBJ_CLASS_SWITCH_LAYER: + if (((struct pcep_object_switch_layer *)obj)->switch_layer_rows + != NULL) { + dll_destroy_with_data_memtype( + ((struct pcep_object_switch_layer *)obj) + ->switch_layer_rows, + PCEPLIB_MESSAGES); + } + break; + + default: + break; + } + + pceplib_free(PCEPLIB_MESSAGES, obj); +} + +void pcep_msg_free_message(struct pcep_message *message) +{ + /* Iterate the objects and free each one */ + if (message->obj_list != NULL) { + struct pcep_object_header *obj; + while ((obj = (struct pcep_object_header *) + dll_delete_first_node(message->obj_list)) + != NULL) { + pcep_obj_free_object(obj); + } + + dll_destroy(message->obj_list); + } + + if (message->msg_header != NULL) { + pceplib_free(PCEPLIB_MESSAGES, message->msg_header); + } + + if (message->encoded_message != NULL) { + pceplib_free(PCEPLIB_MESSAGES, message->encoded_message); + } + + pceplib_free(PCEPLIB_MESSAGES, message); +} + +void pcep_msg_free_message_list(double_linked_list *list) +{ + /* Iterate the messages and free each one */ + struct pcep_message *msg; + while ((msg = (struct pcep_message *)dll_delete_first_node(list)) + != NULL) { + pcep_msg_free_message(msg); + } + + dll_destroy(list); +} + +const char *get_message_type_str(uint8_t type) +{ + uint8_t msg_type = + (type > PCEP_TYPE_INITIATE) ? PCEP_TYPE_INITIATE + 1 : type; + + return message_type_strs[msg_type]; +} + +const char *get_object_class_str(uint8_t class) +{ + uint8_t object_class = + (class > PCEP_OBJ_CLASS_SRP) ? PCEP_OBJ_CLASS_SRP + 1 : class; + + return object_class_strs[object_class]; +} + +/* Expecting a list of struct pcep_message pointers */ +void pcep_msg_print(double_linked_list *msg_list) +{ + double_linked_list_node *node; + for (node = msg_list->head; node != NULL; node = node->next_node) { + struct pcep_message *msg = (struct pcep_message *)node->data; + pcep_log(LOG_INFO, "%s: PCEP_MSG %s", __func__, + get_message_type_str(msg->msg_header->type)); + + double_linked_list_node *obj_node = + (msg->obj_list == NULL ? NULL : msg->obj_list->head); + for (; obj_node != NULL; obj_node = obj_node->next_node) { + struct pcep_object_header *obj_header = + ((struct pcep_object_header *)obj_node->data); + pcep_log( + LOG_INFO, "%s: PCEP_OBJ %s", __func__, + get_object_class_str(obj_header->object_class)); + } + } +} + +int pcep_msg_send(int sock_fd, struct pcep_message *msg) +{ + if (msg == NULL) { + return 0; + } + int msg_length = ntohs(msg->encoded_message_length); + if (msg_length > PCEP_MESSAGE_LENGTH) { + pcep_log(LOG_ERR, "%s: Not sended, size(% d) exceed max(% d) ", + __func__, msg_length, PCEP_MESSAGE_LENGTH); + return 0; + } + + return write(sock_fd, msg->encoded_message, msg_length); +} diff --git a/pceplib/pcep_msg_tools.h b/pceplib/pcep_msg_tools.h new file mode 100644 index 0000000..b62bdde --- /dev/null +++ b/pceplib/pcep_msg_tools.h @@ -0,0 +1,71 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + */ + +#ifndef PCEP_TOOLS_H +#define PCEP_TOOLS_H + +#include <stdint.h> +#include <netinet/in.h> // struct in_addr + +#include "pcep_utils_double_linked_list.h" +#include "pcep_msg_messages.h" +#include "pcep_msg_objects.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PCEP_MAX_SIZE 6000 + +/* Returns a double linked list of PCEP messages */ +double_linked_list *pcep_msg_read(int sock_fd); +/* Given a double linked list of PCEP messages, return the first node that has + * the same message type */ +struct pcep_message *pcep_msg_get(double_linked_list *msg_list, uint8_t type); +/* Given a double linked list of PCEP messages, return the next node after + * current node that has the same message type */ +struct pcep_message *pcep_msg_get_next(double_linked_list *msg_list, + struct pcep_message *current, + uint8_t type); +struct pcep_object_header *pcep_obj_get(double_linked_list *list, + uint8_t object_class); +struct pcep_object_header *pcep_obj_get_next(double_linked_list *list, + struct pcep_object_header *current, + uint8_t object_class); +struct pcep_object_tlv_header *pcep_tlv_get(double_linked_list *list, + uint16_t type); +struct pcep_object_tlv_header * +pcep_tlv_get_next(double_linked_list *list, + struct pcep_object_tlv_header *current, uint16_t type); +void pcep_obj_free_tlv(struct pcep_object_tlv_header *tlv); +void pcep_obj_free_object(struct pcep_object_header *obj); +void pcep_msg_free_message(struct pcep_message *message); +void pcep_msg_free_message_list(double_linked_list *list); +void pcep_msg_print(double_linked_list *list); +const char *get_message_type_str(uint8_t type); +const char *get_object_class_str(uint8_t class); +int pcep_msg_send(int sock_fd, struct pcep_message *hdr); + +#ifdef __cplusplus +} +#endif + +#endif 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; +} diff --git a/pceplib/pcep_pcc_api.c b/pceplib/pcep_pcc_api.c new file mode 100644 index 0000000..b7813c5 --- /dev/null +++ b/pceplib/pcep_pcc_api.c @@ -0,0 +1,392 @@ +/* + * 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> + * + */ + + +/* + * Public PCEPlib PCC API implementation + */ + +#include <zebra.h> + +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include "pcep_msg_messages.h" +#include "pcep_pcc_api.h" +#include "pcep_utils_counters.h" +#include "pcep_utils_logging.h" + +/* Not using an array here since the enum pcep_event_type indeces go into the + * 100's */ +const char MESSAGE_RECEIVED_STR[] = "MESSAGE_RECEIVED"; +const char PCE_CLOSED_SOCKET_STR[] = "PCE_CLOSED_SOCKET"; +const char PCE_SENT_PCEP_CLOSE_STR[] = "PCE_SENT_PCEP_CLOSE"; +const char PCE_DEAD_TIMER_EXPIRED_STR[] = "PCE_DEAD_TIMER_EXPIRED"; +const char PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED_STR[] = + "PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED"; +const char PCC_CONNECTED_TO_PCE_STR[] = "PCC_CONNECTED_TO_PCE"; +const char PCC_PCEP_SESSION_CLOSED_STR[] = "PCC_PCEP_SESSION_CLOSED"; +const char PCC_RCVD_INVALID_OPEN_STR[] = "PCC_RCVD_INVALID_OPEN"; +const char PCC_RCVD_MAX_INVALID_MSGS_STR[] = "PCC_RCVD_MAX_INVALID_MSGS"; +const char PCC_RCVD_MAX_UNKOWN_MSGS_STR[] = "PCC_RCVD_MAX_UNKOWN_MSGS"; +const char UNKNOWN_EVENT_STR[] = "UNKNOWN Event Type"; + +/* Session Logic Handle managed in pcep_session_logic.c */ +extern pcep_event_queue *session_logic_event_queue_; + +bool initialize_pcc() +{ + if (!run_session_logic()) { + pcep_log(LOG_ERR, "%s: Error initializing PCC session logic.", + __func__); + return false; + } + + return true; +} + + +bool initialize_pcc_infra(struct pceplib_infra_config *infra_config) +{ + if (infra_config == NULL) { + return initialize_pcc(); + } + + if (!run_session_logic_with_infra(infra_config)) { + pcep_log(LOG_ERR, + "%s: Error initializing PCC session logic with infra.", + __func__); + return false; + } + + return true; +} + + +/* this function is blocking */ +bool initialize_pcc_wait_for_completion() +{ + return run_session_logic_wait_for_completion(); +} + + +bool destroy_pcc() +{ + if (!stop_session_logic()) { + pcep_log(LOG_WARNING, "%s: Error stopping PCC session logic.", + __func__); + return false; + } + + return true; +} + + +pcep_configuration *create_default_pcep_configuration() +{ + pcep_configuration *config = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_configuration)); + memset(config, 0, sizeof(pcep_configuration)); + + config->keep_alive_seconds = DEFAULT_CONFIG_KEEP_ALIVE; + /* This value will possibly be overwritten later with PCE config data */ + config->keep_alive_pce_negotiated_timer_seconds = + DEFAULT_CONFIG_KEEP_ALIVE; + config->min_keep_alive_seconds = DEFAULT_MIN_CONFIG_KEEP_ALIVE; + config->max_keep_alive_seconds = DEFAULT_MAX_CONFIG_KEEP_ALIVE; + + config->dead_timer_seconds = DEFAULT_CONFIG_DEAD_TIMER; + /* This value will be overwritten later with PCE config data */ + config->dead_timer_pce_negotiated_seconds = DEFAULT_CONFIG_DEAD_TIMER; + config->min_dead_timer_seconds = DEFAULT_MIN_CONFIG_DEAD_TIMER; + config->max_dead_timer_seconds = DEFAULT_MAX_CONFIG_DEAD_TIMER; + + config->request_time_seconds = DEFAULT_CONFIG_REQUEST_TIME; + config->max_unknown_messages = DEFAULT_CONFIG_MAX_UNKNOWN_MESSAGES; + config->max_unknown_requests = DEFAULT_CONFIG_MAX_UNKNOWN_REQUESTS; + + config->socket_connect_timeout_millis = + DEFAULT_TCP_CONNECT_TIMEOUT_MILLIS; + config->support_stateful_pce_lsp_update = true; + config->support_pce_lsp_instantiation = true; + config->support_include_db_version = true; + config->lsp_db_version = 0; + config->support_lsp_triggered_resync = true; + config->support_lsp_delta_sync = true; + config->support_pce_triggered_initial_sync = true; + config->support_sr_te_pst = true; + config->pcc_can_resolve_nai_to_sid = true; + config->max_sid_depth = 0; + config->dst_pcep_port = 0; + config->src_pcep_port = 0; + config->src_ip.src_ipv4.s_addr = INADDR_ANY; + config->is_src_ipv6 = false; + config->pcep_msg_versioning = create_default_pcep_versioning(); + config->tcp_authentication_str[0] = '\0'; + config->is_tcp_auth_md5 = true; + + return config; +} + +void destroy_pcep_configuration(pcep_configuration *config) +{ + destroy_pcep_versioning(config->pcep_msg_versioning); + pceplib_free(PCEPLIB_INFRA, config); +} + +pcep_session *connect_pce(pcep_configuration *config, struct in_addr *pce_ip) +{ + return create_pcep_session(config, pce_ip); +} + +pcep_session *connect_pce_ipv6(pcep_configuration *config, + struct in6_addr *pce_ip) +{ + return create_pcep_session_ipv6(config, pce_ip); +} + +void disconnect_pce(pcep_session *session) +{ + if (session_exists(session) == false) { + pcep_log( + LOG_WARNING, + "%s: disconnect_pce session [%p] has already been deleted", + __func__, session); + return; + } + + if (session->socket_comm_session == NULL + || session->socket_comm_session->socket_fd < 0) { + /* If the socket has already been closed, just destroy the + * session */ + destroy_pcep_session(session); + } else { + /* This will cause the session to be destroyed AFTER the close + * message is sent */ + session->destroy_session_after_write = true; + + /* Send a PCEP close message */ + close_pcep_session(session); + } +} + +void send_message(pcep_session *session, struct pcep_message *msg, + bool free_after_send) +{ + if (session == NULL || msg == NULL) { + pcep_log(LOG_DEBUG, + "%s: send_message NULL params session [%p] msg [%p]", + __func__, session, msg); + + return; + } + + if (session_exists(session) == false) { + pcep_log( + LOG_WARNING, + "%s: send_message session [%p] has already been deleted", + __func__, session); + return; + } + + pcep_encode_message(msg, session->pcc_config.pcep_msg_versioning); + socket_comm_session_send_message( + session->socket_comm_session, (char *)msg->encoded_message, + msg->encoded_message_length, free_after_send); + + increment_message_tx_counters(session, msg); + + if (free_after_send == true) { + /* The encoded_message will be deleted once sent, so everything + * else in the message will be freed */ + msg->encoded_message = NULL; + pcep_msg_free_message(msg); + } +} + +/* Returns true if the queue is empty, false otherwise */ +bool event_queue_is_empty() +{ + if (session_logic_event_queue_ == NULL) { + pcep_log( + LOG_WARNING, + "%s: event_queue_is_empty Session Logic is not initialized yet", + __func__); + return false; + } + + pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex); + bool is_empty = + (session_logic_event_queue_->event_queue->num_entries == 0); + pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex); + + return is_empty; +} + + +/* Return the number of events on the queue, 0 if empty */ +uint32_t event_queue_num_events_available() +{ + if (session_logic_event_queue_ == NULL) { + pcep_log( + LOG_WARNING, + "%s: event_queue_num_events_available Session Logic is not initialized yet", + __func__); + return 0; + } + + pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex); + uint32_t num_events = + session_logic_event_queue_->event_queue->num_entries; + pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex); + + return num_events; +} + + +/* Return the next event on the queue, NULL if empty */ +struct pcep_event *event_queue_get_event() +{ + if (session_logic_event_queue_ == NULL) { + pcep_log( + LOG_WARNING, + "%s: event_queue_get_event Session Logic is not initialized yet", + __func__); + return NULL; + } + + pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex); + struct pcep_event *event = (struct pcep_event *)queue_dequeue( + session_logic_event_queue_->event_queue); + pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex); + + return event; +} + + +/* Free the PCEP Event resources, including the PCEP message */ +void destroy_pcep_event(struct pcep_event *event) +{ + if (event == NULL) { + pcep_log(LOG_WARNING, + "%s: destroy_pcep_event cannot destroy NULL event", + __func__); + return; + } + + if (event->event_type == MESSAGE_RECEIVED && event->message != NULL) { + pcep_msg_free_message(event->message); + } + + pceplib_free(PCEPLIB_INFRA, event); +} + +const char *get_event_type_str(int event_type) +{ + switch (event_type) { + case MESSAGE_RECEIVED: + return MESSAGE_RECEIVED_STR; + break; + case PCE_CLOSED_SOCKET: + return PCE_CLOSED_SOCKET_STR; + break; + case PCE_SENT_PCEP_CLOSE: + return PCE_SENT_PCEP_CLOSE_STR; + break; + case PCE_DEAD_TIMER_EXPIRED: + return PCE_DEAD_TIMER_EXPIRED_STR; + break; + case PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED: + return PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED_STR; + break; + case PCC_CONNECTED_TO_PCE: + return PCC_CONNECTED_TO_PCE_STR; + break; + case PCC_PCEP_SESSION_CLOSED: + return PCC_PCEP_SESSION_CLOSED_STR; + break; + case PCC_RCVD_INVALID_OPEN: + return PCC_RCVD_INVALID_OPEN_STR; + break; + case PCC_RCVD_MAX_INVALID_MSGS: + return PCC_RCVD_MAX_INVALID_MSGS_STR; + break; + case PCC_RCVD_MAX_UNKOWN_MSGS: + return PCC_RCVD_MAX_UNKOWN_MSGS_STR; + break; + default: + return UNKNOWN_EVENT_STR; + break; + } +} + +void dump_pcep_session_counters(pcep_session *session) +{ + if (session_exists(session) == false) { + pcep_log( + LOG_WARNING, + "%s: dump_pcep_session_counters session [%p] has already been deleted", + __func__, session); + return; + } + + /* Update the counters group name so that the PCE session connected time + * is accurate */ + time_t now = time(NULL); + char counters_name[MAX_COUNTER_STR_LENGTH] = {0}; + char ip_str[40] = {0}; + if (session->socket_comm_session->is_ipv6) { + inet_ntop(AF_INET6, + &session->socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv6.sin6_addr, + ip_str, 40); + } else { + inet_ntop(AF_INET, + &session->socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv4.sin_addr, + ip_str, 40); + } + snprintf(counters_name, MAX_COUNTER_STR_LENGTH, + "PCEP Session [%d], connected to [%s] for [%u seconds]", + session->session_id, ip_str, + (uint32_t)(now - session->time_connected)); + strlcpy(session->pcep_session_counters->counters_group_name, + counters_name, + sizeof(session->pcep_session_counters->counters_group_name)); + + dump_counters_group_to_log(session->pcep_session_counters); +} + +void reset_pcep_session_counters(pcep_session *session) +{ + if (session_exists(session) == false) { + pcep_log( + LOG_WARNING, + "%s: reset_pcep_session_counters session [%p] has already been deleted", + session); + return; + } + + reset_group_counters(session->pcep_session_counters); +} diff --git a/pceplib/pcep_pcc_api.h b/pceplib/pcep_pcc_api.h new file mode 100644 index 0000000..5756e23 --- /dev/null +++ b/pceplib/pcep_pcc_api.h @@ -0,0 +1,103 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +/* + * Public PCEPlib PCC API + */ + +#ifndef PCEPPCC_INCLUDE_PCEPPCCAPI_H_ +#define PCEPPCC_INCLUDE_PCEPPCCAPI_H_ + +#include <stdbool.h> + +#include "pcep_session_logic.h" +#include "pcep_timers.h" + +#define DEFAULT_PCEP_TCP_PORT 4189 +#define DEFAULT_CONFIG_KEEP_ALIVE 30 +#define DEFAULT_CONFIG_DEAD_TIMER DEFAULT_CONFIG_KEEP_ALIVE * 4 +#define DEFAULT_CONFIG_REQUEST_TIME 30 +#define DEFAULT_CONFIG_MAX_UNKNOWN_REQUESTS 5 +#define DEFAULT_CONFIG_MAX_UNKNOWN_MESSAGES 5 +#define DEFAULT_TCP_CONNECT_TIMEOUT_MILLIS 250 + +/* Acceptable MIN and MAX values used in deciding if the PCEP + * Open received from a PCE should be accepted or rejected. */ +#define DEFAULT_MIN_CONFIG_KEEP_ALIVE 5 +#define DEFAULT_MAX_CONFIG_KEEP_ALIVE 120 +#define DEFAULT_MIN_CONFIG_DEAD_TIMER DEFAULT_MIN_CONFIG_KEEP_ALIVE * 4 +#define DEFAULT_MAX_CONFIG_DEAD_TIMER DEFAULT_MAX_CONFIG_KEEP_ALIVE * 4 + +/* + * PCEP PCC library initialization/teardown functions + */ + +/* Later when this is integrated with FRR pathd, it will be changed + * to just initialize_pcc(struct pceplib_infra_config *infra_config) */ +bool initialize_pcc(void); +bool initialize_pcc_infra(struct pceplib_infra_config *infra_config); +/* this function is blocking */ +bool initialize_pcc_wait_for_completion(void); +bool destroy_pcc(void); + + +/* + * PCEP session functions + */ + +pcep_configuration *create_default_pcep_configuration(void); +void destroy_pcep_configuration(pcep_configuration *config); + +/* Uses the standard PCEP TCP src and dest port = 4189. + * To use a specific dest or src port, set them other than 0 in the + * pcep_configuration. If src_ip is not set, INADDR_ANY will be used. */ +pcep_session *connect_pce(pcep_configuration *config, struct in_addr *pce_ip); +pcep_session *connect_pce_ipv6(pcep_configuration *config, + struct in6_addr *pce_ip); +void disconnect_pce(pcep_session *session); +void send_message(pcep_session *session, struct pcep_message *msg, + bool free_after_send); + +void dump_pcep_session_counters(pcep_session *session); +void reset_pcep_session_counters(pcep_session *session); + +/* + * Event Queue functions + */ + +/* Returns true if the queue is empty, false otherwise */ +bool event_queue_is_empty(void); + +/* Return the number of events on the queue, 0 if empty */ +uint32_t event_queue_num_events_available(void); + +/* Return the next event on the queue, NULL if empty */ +struct pcep_event *event_queue_get_event(void); + +/* Free the PCEP Event resources, including the PCEP message */ +void destroy_pcep_event(struct pcep_event *event); + +const char *get_event_type_str(int event_type); + + +#endif /* PCEPPCC_INCLUDE_PCEPPCCAPI_H_ */ diff --git a/pceplib/pcep_session_logic.c b/pceplib/pcep_session_logic.c new file mode 100644 index 0000000..78d1072 --- /dev/null +++ b/pceplib/pcep_session_logic.c @@ -0,0 +1,689 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <limits.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "pcep_msg_encoding.h" +#include "pcep_session_logic.h" +#include "pcep_session_logic_internals.h" +#include "pcep_timers.h" +#include "pcep_utils_counters.h" +#include "pcep_utils_ordered_list.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +/* + * public API function implementations for the session_logic + */ + +pcep_session_logic_handle *session_logic_handle_ = NULL; +pcep_event_queue *session_logic_event_queue_ = NULL; +int session_id_ = 0; + +void send_pcep_open(pcep_session *session); /* forward decl */ + +static bool run_session_logic_common(void) +{ + if (session_logic_handle_ != NULL) { + pcep_log(LOG_WARNING, + "%s: Session Logic is already initialized.", __func__); + return false; + } + + session_logic_handle_ = pceplib_malloc( + PCEPLIB_INFRA, sizeof(pcep_session_logic_handle)); + memset(session_logic_handle_, 0, sizeof(pcep_session_logic_handle)); + + session_logic_handle_->active = true; + session_logic_handle_->session_list = + ordered_list_initialize(pointer_compare_function); + session_logic_handle_->session_event_queue = queue_initialize(); + + /* Initialize the event queue */ + session_logic_event_queue_ = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event_queue)); + session_logic_event_queue_->event_queue = queue_initialize(); + if (pthread_mutex_init(&(session_logic_event_queue_->event_queue_mutex), + NULL) + != 0) { + pcep_log( + LOG_ERR, + "%s: Cannot initialize session_logic event queue mutex.", + __func__); + return false; + } + + pthread_cond_init(&(session_logic_handle_->session_logic_cond_var), + NULL); + + if (pthread_mutex_init(&(session_logic_handle_->session_logic_mutex), + NULL) + != 0) { + pcep_log(LOG_ERR, "%s: Cannot initialize session_logic mutex.", + __func__); + return false; + } + + pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex)); + session_logic_handle_->session_logic_condition = true; + pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var)); + pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex)); + + if (pthread_mutex_init(&(session_logic_handle_->session_list_mutex), + NULL) + != 0) { + pcep_log(LOG_ERR, "%s: Cannot initialize session_list mutex.", + __func__); + return false; + } + + return true; +} + + +bool run_session_logic() +{ + if (!run_session_logic_common()) { + return false; + } + + if (pthread_create(&(session_logic_handle_->session_logic_thread), NULL, + session_logic_loop, session_logic_handle_)) { + pcep_log(LOG_ERR, "%s: Cannot initialize session_logic thread.", + __func__); + return false; + } + + if (!initialize_timers(session_logic_timer_expire_handler)) { + pcep_log(LOG_ERR, "%s: Cannot initialize session_logic timers.", + __func__); + return false; + } + + /* No need to call initialize_socket_comm_loop() since it will be + * called internally when the first socket_comm_session is created. */ + + return true; +} + + +bool run_session_logic_with_infra(pceplib_infra_config *infra_config) +{ + if (infra_config == NULL) { + return run_session_logic(); + } + + /* Initialize the memory infrastructure before anything gets allocated + */ + if (infra_config->pceplib_infra_mt != NULL + && infra_config->pceplib_messages_mt != NULL) { + pceplib_memory_initialize( + infra_config->pceplib_infra_mt, + infra_config->pceplib_messages_mt, + infra_config->malloc_func, infra_config->calloc_func, + infra_config->realloc_func, infra_config->strdup_func, + infra_config->free_func); + } + + if (!run_session_logic_common()) { + return false; + } + + /* Create the pcep_session_logic pthread so it can be managed externally + */ + if (infra_config->pthread_create_func != NULL) { + if (infra_config->pthread_create_func( + &(session_logic_handle_->session_logic_thread), + NULL, session_logic_loop, session_logic_handle_, + "pcep_session_logic")) { + pcep_log( + LOG_ERR, + "%s: Cannot initialize external session_logic thread.", + __func__); + return false; + } + } else { + if (pthread_create( + &(session_logic_handle_->session_logic_thread), + NULL, session_logic_loop, session_logic_handle_)) { + pcep_log(LOG_ERR, + "%s: Cannot initialize session_logic thread.", + __func__); + return false; + } + } + + session_logic_event_queue_->event_callback = + infra_config->pcep_event_func; + session_logic_event_queue_->event_callback_data = + infra_config->external_infra_data; + + if (!initialize_timers_external_infra( + session_logic_timer_expire_handler, + infra_config->external_infra_data, + infra_config->timer_create_func, + infra_config->timer_cancel_func, + infra_config->pthread_create_func)) { + pcep_log( + LOG_ERR, + "%s: Cannot initialize session_logic timers with infra.", + __func__); + return false; + } + + /* We found a problem with the FRR sockets, where not all the KeepAlive + * messages were received, so if the pthread_create_func is set, the + * internal PCEPlib socket infrastructure will be used. */ + + /* For the SocketComm, the socket_read/write_func and the + * pthread_create_func are mutually exclusive. */ + if (infra_config->pthread_create_func != NULL) { + if (!initialize_socket_comm_external_infra( + infra_config->external_infra_data, NULL, NULL, + infra_config->pthread_create_func)) { + pcep_log( + LOG_ERR, + "%s: Cannot initialize session_logic socket comm with infra.", + __func__); + return false; + } + } else if (infra_config->socket_read_func != NULL + && infra_config->socket_write_func != NULL) { + if (!initialize_socket_comm_external_infra( + infra_config->external_infra_data, + infra_config->socket_read_func, + infra_config->socket_write_func, NULL)) { + pcep_log( + LOG_ERR, + "%s: Cannot initialize session_logic socket comm with infra.", + __func__); + return false; + } + } + + return true; +} + +bool run_session_logic_wait_for_completion() +{ + if (!run_session_logic()) { + return false; + } + + /* Blocking call, waits for session logic thread to complete */ + pthread_join(session_logic_handle_->session_logic_thread, NULL); + + return true; +} + + +bool stop_session_logic() +{ + if (session_logic_handle_ == NULL) { + pcep_log(LOG_WARNING, "%s: Session logic already stopped", + __func__); + return false; + } + + session_logic_handle_->active = false; + teardown_timers(); + + pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex)); + session_logic_handle_->session_logic_condition = true; + pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var)); + pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex)); + pthread_join(session_logic_handle_->session_logic_thread, NULL); + + pthread_mutex_destroy(&(session_logic_handle_->session_logic_mutex)); + pthread_mutex_destroy(&(session_logic_handle_->session_list_mutex)); + ordered_list_destroy(session_logic_handle_->session_list); + queue_destroy(session_logic_handle_->session_event_queue); + + /* destroy the event_queue */ + pthread_mutex_destroy(&(session_logic_event_queue_->event_queue_mutex)); + queue_destroy(session_logic_event_queue_->event_queue); + pceplib_free(PCEPLIB_INFRA, session_logic_event_queue_); + + /* Explicitly stop the socket comm loop started by the pcep_sessions */ + destroy_socket_comm_loop(); + + pceplib_free(PCEPLIB_INFRA, session_logic_handle_); + session_logic_handle_ = NULL; + + return true; +} + + +void close_pcep_session(pcep_session *session) +{ + close_pcep_session_with_reason(session, PCEP_CLOSE_REASON_NO); +} + +void close_pcep_session_with_reason(pcep_session *session, + enum pcep_close_reason reason) +{ + struct pcep_message *close_msg = pcep_msg_create_close(reason); + + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic send pcep_close message for session [%d]", + __func__, time(NULL), pthread_self(), session->session_id); + + session_send_message(session, close_msg); + socket_comm_session_close_tcp_after_write(session->socket_comm_session); + session->session_state = SESSION_STATE_INITIALIZED; +} + + +void destroy_pcep_session(pcep_session *session) +{ + if (session == NULL) { + pcep_log(LOG_WARNING, "%s: Cannot destroy NULL session", + __func__); + return; + } + + /* Remove the session from the session_list and synchronize session + * destroy with the session_logic_loop, so that no in-flight events + * will be handled now that the session is destroyed. */ + pthread_mutex_lock(&(session_logic_handle_->session_list_mutex)); + ordered_list_remove_first_node_equals( + session_logic_handle_->session_list, session); + pcep_log(LOG_DEBUG, + "%s: destroy_pcep_session delete session_list sessionPtr %p", + __func__, session); + + pcep_session_cancel_timers(session); + delete_counters_group(session->pcep_session_counters); + queue_destroy_with_data(session->num_unknown_messages_time_queue); + socket_comm_session_teardown(session->socket_comm_session); + + if (session->pcc_config.pcep_msg_versioning != NULL) { + pceplib_free(PCEPLIB_INFRA, + session->pcc_config.pcep_msg_versioning); + } + + if (session->pce_config.pcep_msg_versioning != NULL) { + pceplib_free(PCEPLIB_INFRA, + session->pce_config.pcep_msg_versioning); + } + + int session_id = session->session_id; + pceplib_free(PCEPLIB_INFRA, session); + pcep_log(LOG_INFO, "%s: [%ld-%ld] session [%d] destroyed", __func__, + time(NULL), pthread_self(), session_id); + pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex)); +} + +void pcep_session_cancel_timers(pcep_session *session) +{ + if (session == NULL) { + return; + } + + if (session->timer_id_dead_timer != TIMER_ID_NOT_SET) { + cancel_timer(session->timer_id_dead_timer); + } + + if (session->timer_id_keep_alive != TIMER_ID_NOT_SET) { + cancel_timer(session->timer_id_keep_alive); + } + + if (session->timer_id_open_keep_wait != TIMER_ID_NOT_SET) { + cancel_timer(session->timer_id_open_keep_wait); + } + + if (session->timer_id_open_keep_alive != TIMER_ID_NOT_SET) { + cancel_timer(session->timer_id_open_keep_alive); + } +} + +/* Internal util function */ +static int get_next_session_id(void) +{ + if (session_id_ == INT_MAX) { + session_id_ = 0; + } + + return session_id_++; +} + +/* Internal util function */ +static pcep_session *create_pcep_session_pre_setup(pcep_configuration *config) +{ + if (config == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot create pcep session with NULL config", + __func__); + return NULL; + } + + pcep_session *session = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_session)); + memset(session, 0, sizeof(pcep_session)); + session->session_id = get_next_session_id(); + session->session_state = SESSION_STATE_INITIALIZED; + session->timer_id_open_keep_wait = TIMER_ID_NOT_SET; + session->timer_id_open_keep_alive = TIMER_ID_NOT_SET; + session->timer_id_dead_timer = TIMER_ID_NOT_SET; + session->timer_id_keep_alive = TIMER_ID_NOT_SET; + session->stateful_pce = false; + session->num_unknown_messages_time_queue = queue_initialize(); + session->pce_open_received = false; + session->pce_open_rejected = false; + session->pce_open_keep_alive_sent = false; + session->pcc_open_rejected = false; + session->pce_open_accepted = false; + session->pcc_open_accepted = false; + session->destroy_session_after_write = false; + session->lsp_db_version = config->lsp_db_version; + memcpy(&(session->pcc_config), config, sizeof(pcep_configuration)); + /* copy the pcc_config to the pce_config until we receive the open + * keep_alive response */ + memcpy(&(session->pce_config), config, sizeof(pcep_configuration)); + if (config->pcep_msg_versioning != NULL) { + session->pcc_config.pcep_msg_versioning = pceplib_malloc( + PCEPLIB_INFRA, sizeof(struct pcep_versioning)); + memcpy(session->pcc_config.pcep_msg_versioning, + config->pcep_msg_versioning, + sizeof(struct pcep_versioning)); + session->pce_config.pcep_msg_versioning = pceplib_malloc( + PCEPLIB_INFRA, sizeof(struct pcep_versioning)); + memcpy(session->pce_config.pcep_msg_versioning, + config->pcep_msg_versioning, + sizeof(struct pcep_versioning)); + } + + pthread_mutex_lock(&(session_logic_handle_->session_list_mutex)); + ordered_list_add_node(session_logic_handle_->session_list, session); + pcep_log( + LOG_DEBUG, + "%s: create_pcep_session_pre_setup add session_list sessionPtr %p", + __func__, session); + pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex)); + + return session; +} + +/* Internal util function */ +static bool create_pcep_session_post_setup(pcep_session *session) +{ + if (!socket_comm_session_connect_tcp(session->socket_comm_session)) { + pcep_log(LOG_WARNING, "%s: Cannot establish TCP socket.", + __func__); + destroy_pcep_session(session); + + return false; + } + + session->time_connected = time(NULL); + create_session_counters(session); + + send_pcep_open(session); + + session->session_state = SESSION_STATE_PCEP_CONNECTING; + session->timer_id_open_keep_wait = + create_timer(session->pcc_config.keep_alive_seconds, session); + // session->session_state = SESSION_STATE_OPENED; + + return true; +} + +pcep_session *create_pcep_session(pcep_configuration *config, + struct in_addr *pce_ip) +{ + if (pce_ip == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot create pcep session with NULL pce_ip", + __func__); + return NULL; + } + + pcep_session *session = create_pcep_session_pre_setup(config); + if (session == NULL) { + return NULL; + } + + session->socket_comm_session = socket_comm_session_initialize_with_src( + NULL, session_logic_msg_ready_handler, + session_logic_message_sent_handler, + session_logic_conn_except_notifier, &(config->src_ip.src_ipv4), + ((config->src_pcep_port == 0) ? PCEP_TCP_PORT + : config->src_pcep_port), + pce_ip, + ((config->dst_pcep_port == 0) ? PCEP_TCP_PORT + : config->dst_pcep_port), + config->socket_connect_timeout_millis, + config->tcp_authentication_str, config->is_tcp_auth_md5, + session); + if (session->socket_comm_session == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot establish socket_comm_session.", __func__); + destroy_pcep_session(session); + + return NULL; + } + + if (create_pcep_session_post_setup(session) == false) { + return NULL; + } + + return session; +} + +pcep_session *create_pcep_session_ipv6(pcep_configuration *config, + struct in6_addr *pce_ip) +{ + if (pce_ip == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot create pcep session with NULL pce_ip", + __func__); + return NULL; + } + + pcep_session *session = create_pcep_session_pre_setup(config); + if (session == NULL) { + return NULL; + } + + session->socket_comm_session = + socket_comm_session_initialize_with_src_ipv6( + NULL, session_logic_msg_ready_handler, + session_logic_message_sent_handler, + session_logic_conn_except_notifier, + &(config->src_ip.src_ipv6), + ((config->src_pcep_port == 0) ? PCEP_TCP_PORT + : config->src_pcep_port), + pce_ip, + ((config->dst_pcep_port == 0) ? PCEP_TCP_PORT + : config->dst_pcep_port), + config->socket_connect_timeout_millis, + config->tcp_authentication_str, config->is_tcp_auth_md5, + session); + if (session->socket_comm_session == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot establish ipv6 socket_comm_session.", + __func__); + destroy_pcep_session(session); + + return NULL; + } + + if (create_pcep_session_post_setup(session) == false) { + return NULL; + } + + return session; +} + + +void session_send_message(pcep_session *session, struct pcep_message *message) +{ + pcep_encode_message(message, session->pcc_config.pcep_msg_versioning); + socket_comm_session_send_message(session->socket_comm_session, + (char *)message->encoded_message, + message->encoded_message_length, true); + + increment_message_tx_counters(session, message); + + /* The message->encoded_message will be freed in + * socket_comm_session_send_message() once sent. + * Setting to NULL here so pcep_msg_free_message() does not free it */ + message->encoded_message = NULL; + pcep_msg_free_message(message); +} + + +/* This function is also used in pcep_session_logic_states.c */ +struct pcep_message *create_pcep_open(pcep_session *session) +{ + /* create and send PCEP open + * with PCEP, the PCC sends the config the PCE should use in the open + * message, + * and the PCE will send an open with the config the PCC should use. */ + double_linked_list *tlv_list = dll_initialize(); + if (session->pcc_config.support_stateful_pce_lsp_update + || session->pcc_config.support_pce_lsp_instantiation + || session->pcc_config.support_include_db_version + || session->pcc_config.support_lsp_triggered_resync + || session->pcc_config.support_lsp_delta_sync + || session->pcc_config.support_pce_triggered_initial_sync) { + /* Prepend this TLV as the first in the list */ + dll_append( + tlv_list, + pcep_tlv_create_stateful_pce_capability( + /* U flag */ + session->pcc_config + .support_stateful_pce_lsp_update, + /* S flag */ + session->pcc_config.support_include_db_version, + /* I flag */ + session->pcc_config + .support_pce_lsp_instantiation, + /* T flag */ + session->pcc_config + .support_lsp_triggered_resync, + /* D flag */ + session->pcc_config.support_lsp_delta_sync, + /* F flag */ + session->pcc_config + .support_pce_triggered_initial_sync)); + } + + if (session->pcc_config.support_include_db_version) { + if (session->pcc_config.lsp_db_version != 0) { + dll_append(tlv_list, + pcep_tlv_create_lsp_db_version( + session->pcc_config.lsp_db_version)); + } + } + + if (session->pcc_config.support_sr_te_pst) { + bool flag_n = false; + bool flag_x = false; + if (session->pcc_config.pcep_msg_versioning + ->draft_ietf_pce_segment_routing_07 + == false) { + flag_n = session->pcc_config.pcc_can_resolve_nai_to_sid; + flag_x = (session->pcc_config.max_sid_depth == 0); + } + + struct pcep_object_tlv_sr_pce_capability *sr_pce_cap_tlv = + pcep_tlv_create_sr_pce_capability( + flag_n, flag_x, + session->pcc_config.max_sid_depth); + + double_linked_list *sub_tlv_list = NULL; + if (session->pcc_config.pcep_msg_versioning + ->draft_ietf_pce_segment_routing_07 + == true) { + /* With draft07, send the sr_pce_cap_tlv as a normal TLV + */ + dll_append(tlv_list, sr_pce_cap_tlv); + } else { + /* With draft16, send the sr_pce_cap_tlv as a sub-TLV in + * the path_setup_type_capability TLV */ + sub_tlv_list = dll_initialize(); + dll_append(sub_tlv_list, sr_pce_cap_tlv); + } + + uint8_t *pst = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint8_t)); + *pst = SR_TE_PST; + double_linked_list *pst_list = dll_initialize(); + dll_append(pst_list, pst); + dll_append(tlv_list, pcep_tlv_create_path_setup_type_capability( + pst_list, sub_tlv_list)); + } + + struct pcep_message *open_msg = pcep_msg_create_open_with_tlvs( + session->pcc_config.keep_alive_seconds, + session->pcc_config.dead_timer_seconds, session->session_id, + tlv_list); + + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic create open message: TLVs [%d] for session [%d]", + __func__, time(NULL), pthread_self(), tlv_list->num_entries, + session->session_id); + + return (open_msg); +} + + +void send_pcep_open(pcep_session *session) +{ + session_send_message(session, create_pcep_open(session)); +} + +/* This is a blocking call, since it is synchronized with destroy_pcep_session() + * and session_logic_loop(). It may be possible that the session has been + * deleted but API users havent been informed yet. + */ +bool session_exists(pcep_session *session) +{ + if (session_logic_handle_ == NULL) { + pcep_log(LOG_DEBUG, + "%s: session_exists session_logic_handle_ is NULL", + __func__); + return false; + } + + pthread_mutex_lock(&(session_logic_handle_->session_list_mutex)); + bool retval = + (ordered_list_find(session_logic_handle_->session_list, session) + != NULL); + pthread_mutex_unlock(&(session_logic_handle_->session_list_mutex)); + + return retval; +} diff --git a/pceplib/pcep_session_logic.h b/pceplib/pcep_session_logic.h new file mode 100644 index 0000000..9cdec52 --- /dev/null +++ b/pceplib/pcep_session_logic.h @@ -0,0 +1,290 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +#ifndef INCLUDE_PCEPSESSIONLOGIC_H_ +#define INCLUDE_PCEPSESSIONLOGIC_H_ + +#include <stdbool.h> +#include <netinet/tcp.h> + +#include "pcep_msg_encoding.h" +#include "pcep_socket_comm.h" +#include "pcep_msg_objects.h" +#include "pcep_msg_tools.h" +#include "pcep_timers.h" +#include "pcep_utils_queue.h" +#include "pcep_utils_memory.h" + +#define PCEP_TCP_PORT 4189 + +typedef struct pcep_configuration_ { + /* These are the configuration values that will + * be sent to the PCE in the PCEP Open message */ + int keep_alive_seconds; + int dead_timer_seconds; + int dead_timer_pce_negotiated_seconds; /* Config data negotiated with + PCE */ + int keep_alive_pce_negotiated_timer_seconds; /* Config data negotiated + with PCE */ + int request_time_seconds; + + /* These are the acceptable ranges of values received by + * the PCE in the initial PCEP Open Message. If a value is + * received outside of these ranges, then the Open message + * will be rejected. */ + int min_keep_alive_seconds; + int max_keep_alive_seconds; + int min_dead_timer_seconds; + int max_dead_timer_seconds; + + /* If more than this many unknown messages/requests are received + * per minute, then the session will be closed. */ + int max_unknown_messages; + int max_unknown_requests; + + /* Maximum amount of time to wait to connect to the + * PCE TCP socket before failing, in milliseconds. */ + uint32_t socket_connect_timeout_millis; + + /* Set if the PCE/PCC will support stateful PCE LSP Updates + * according to RCF8231, section 7.1.1, defaults to true. + * Will cause an additional TLV to be sent from the PCC in + * the PCEP Open */ + bool support_stateful_pce_lsp_update; + + /* RFC 8281: I-bit, the PCC allows instantiation of an LSP by a PCE */ + bool support_pce_lsp_instantiation; + + /* RFC 8232: S-bit, the PCC will include the LSP-DB-VERSION + * TLV in each LSP object */ + bool support_include_db_version; + + /* Only set if support_include_db_version is true and if the LSP-DB + * survived a restart and is available. If this has a value other than + * 0, then a LSP-DB-VERSION TLV will be sent in the OPEN object. This + * value will be copied over to the pcep_session upon init. */ + uint64_t lsp_db_version; + + /* RFC 8232: T-bit, the PCE can trigger resynchronization of + * LSPs at any point in the life of the session */ + bool support_lsp_triggered_resync; + + /* RFC 8232: D-bit, the PCEP speaker allows incremental (delta) + * State Synchronization */ + bool support_lsp_delta_sync; + + /* RFC 8232: F-bit, the PCE SHOULD trigger initial (first) + * State Synchronization */ + bool support_pce_triggered_initial_sync; + + /* draft-ietf-pce-segment-routing-16: Send a SR PCE Capability + * sub-TLV in a Path Setup Type Capability TLV with a PST = 1, + * Path is setup using SR TE. */ + bool support_sr_te_pst; + /* Used in the SR PCE Capability sub-TLV */ + bool pcc_can_resolve_nai_to_sid; + /* Used in the SR TE Capability sub-TLV, 0 means there are no max sid + * limits */ + uint8_t max_sid_depth; + + /* If set to 0, then the default 4189 PCEP port will be used */ + uint16_t dst_pcep_port; + + /* If set to 0, then the default 4189 PCEP port will be used. + * This is according to the RFC5440, Section 5 */ + uint16_t src_pcep_port; + + union src_ip { + struct in_addr src_ipv4; + struct in6_addr src_ipv6; + } src_ip; + bool is_src_ipv6; + + struct pcep_versioning *pcep_msg_versioning; + + char tcp_authentication_str[PCEP_MD5SIG_MAXKEYLEN]; + bool is_tcp_auth_md5; /* true: RFC 2385, false: RFC 5925 */ + +} pcep_configuration; + + +typedef enum pcep_session_state_ { + SESSION_STATE_UNKNOWN = 0, + SESSION_STATE_INITIALIZED = 1, + SESSION_STATE_PCEP_CONNECTING = 2, + SESSION_STATE_PCEP_CONNECTED = 3 + +} pcep_session_state; + + +typedef struct pcep_session_ { + int session_id; + pcep_session_state session_state; + int timer_id_open_keep_wait; + int timer_id_open_keep_alive; + int timer_id_dead_timer; + int timer_id_keep_alive; + bool pce_open_received; + bool pce_open_rejected; + bool pce_open_accepted; + bool pce_open_keep_alive_sent; + bool pcc_open_rejected; + bool pcc_open_accepted; + bool stateful_pce; + time_t time_connected; + uint64_t lsp_db_version; + queue_handle *num_unknown_messages_time_queue; + /* set this flag when finalizing the session */ + bool destroy_session_after_write; + pcep_socket_comm_session *socket_comm_session; + /* Configuration sent from the PCC to the PCE */ + pcep_configuration pcc_config; + /* Configuration received from the PCE, to be used in the PCC */ + pcep_configuration pce_config; + struct counters_group *pcep_session_counters; + +} pcep_session; + + +typedef enum pcep_event_type { + MESSAGE_RECEIVED = 0, + PCE_CLOSED_SOCKET = 1, + PCE_SENT_PCEP_CLOSE = 2, + PCE_DEAD_TIMER_EXPIRED = 3, + PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED = 4, + PCC_CONNECTED_TO_PCE = 100, + PCC_CONNECTION_FAILURE = 101, + PCC_PCEP_SESSION_CLOSED = 102, + PCC_RCVD_INVALID_OPEN = 103, + PCC_SENT_INVALID_OPEN = 104, + PCC_RCVD_MAX_INVALID_MSGS = 105, + PCC_RCVD_MAX_UNKOWN_MSGS = 106 + +} pcep_event_type; + + +typedef struct pcep_event { + enum pcep_event_type event_type; + time_t event_time; + struct pcep_message *message; + pcep_session *session; + +} pcep_event; + +typedef void (*pceplib_pcep_event_callback)(void *cb_data, pcep_event *); +typedef int (*pthread_create_callback)(pthread_t *pthread_id, + const pthread_attr_t *attr, + void *(*start_routine)(void *), + void *data, const char *thread_name); + + +typedef struct pcep_event_queue { + /* The event_queue and event_callback are mutually exclusive. + * If the event_callback is configured, then the event_queue + * will not be used. */ + queue_handle *event_queue; + pthread_mutex_t event_queue_mutex; + pceplib_pcep_event_callback event_callback; + void *event_callback_data; + +} pcep_event_queue; + + +typedef struct pceplib_infra_config { + /* Memory infrastructure */ + void *pceplib_infra_mt; + void *pceplib_messages_mt; + pceplib_malloc_func malloc_func; + pceplib_calloc_func calloc_func; + pceplib_realloc_func realloc_func; + pceplib_strdup_func strdup_func; + pceplib_free_func free_func; + + /* External Timer and Socket infrastructure */ + void *external_infra_data; + ext_timer_create timer_create_func; + ext_timer_cancel timer_cancel_func; + ext_socket_write socket_write_func; + ext_socket_read socket_read_func; + + /* External pcep_event infrastructure */ + pceplib_pcep_event_callback pcep_event_func; + + /* Callback to create pthreads */ + pthread_create_callback pthread_create_func; + +} pceplib_infra_config; + +/* + * Counters Sub-groups definitions + */ +typedef enum pcep_session_counters_subgroup_ids { + COUNTER_SUBGROUP_ID_RX_MSG = 0, + COUNTER_SUBGROUP_ID_TX_MSG = 1, + COUNTER_SUBGROUP_ID_RX_OBJ = 2, + COUNTER_SUBGROUP_ID_TX_OBJ = 3, + COUNTER_SUBGROUP_ID_RX_SUBOBJ = 4, + COUNTER_SUBGROUP_ID_TX_SUBOBJ = 5, + COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ = 6, + COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ = 7, + COUNTER_SUBGROUP_ID_RX_TLV = 8, + COUNTER_SUBGROUP_ID_TX_TLV = 9, + COUNTER_SUBGROUP_ID_EVENT = 10 + +} pcep_session_counters_subgroup_ids; + +bool run_session_logic(void); +bool run_session_logic_with_infra(pceplib_infra_config *infra_config); + +bool run_session_logic_wait_for_completion(void); + +bool stop_session_logic(void); + +/* Uses the standard PCEP TCP dest port = 4189 and an ephemeral src port. + * To use a specific dest or src port, set them other than 0 in the + * pcep_configuration. */ +pcep_session *create_pcep_session(pcep_configuration *config, + struct in_addr *pce_ip); +pcep_session *create_pcep_session_ipv6(pcep_configuration *config, + struct in6_addr *pce_ip); + +/* Send a PCEP close for this pcep_session */ +void close_pcep_session(pcep_session *session); +void close_pcep_session_with_reason(pcep_session *session, + enum pcep_close_reason); + +/* Destroy the PCEP session, a PCEP close should have + * already been sent with close_pcep_session() */ +void destroy_pcep_session(pcep_session *session); + +void pcep_session_cancel_timers(pcep_session *session); + +/* Increments transmitted message counters, additionally counters for the + * objects, sub-objects, and TLVs in the message will be incremented. Received + * counters are incremented internally. */ +void increment_message_tx_counters(pcep_session *session, + struct pcep_message *message); + +bool session_exists(pcep_session *session); + +#endif /* INCLUDE_PCEPSESSIONLOGIC_H_ */ diff --git a/pceplib/pcep_session_logic_counters.c b/pceplib/pcep_session_logic_counters.c new file mode 100644 index 0000000..fceb15a --- /dev/null +++ b/pceplib/pcep_session_logic_counters.c @@ -0,0 +1,454 @@ +/* + * 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> + * + */ + + +/* + * PCEP session logic counters configuration. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <time.h> + +#include "pcep_session_logic.h" +#include "pcep_session_logic_internals.h" +#include "pcep_utils_counters.h" +#include "pcep_utils_logging.h" + +void increment_message_counters(pcep_session *session, + struct pcep_message *message, bool is_rx); + +void create_session_counters(pcep_session *session) +{ + /* + * Message RX and TX counters + */ + struct counters_subgroup *rx_msg_subgroup = create_counters_subgroup( + "RX Message counters", COUNTER_SUBGROUP_ID_RX_MSG, + PCEP_TYPE_MAX + 1); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_OPEN, + "Message Open"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_KEEPALIVE, + "Message KeepAlive"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCREQ, + "Message PcReq"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCREP, + "Message PcRep"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_PCNOTF, + "Message Notify"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_ERROR, + "Message Error"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_CLOSE, + "Message Close"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_REPORT, + "Message Report"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_UPDATE, + "Message Update"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_INITIATE, + "Message Initiate"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_START_TLS, + "Message StartTls"); + create_subgroup_counter(rx_msg_subgroup, PCEP_TYPE_MAX, + "Message Erroneous"); + + struct counters_subgroup *tx_msg_subgroup = + clone_counters_subgroup(rx_msg_subgroup, "TX Message counters", + COUNTER_SUBGROUP_ID_TX_MSG); + + /* + * Object RX and TX counters + */ + + /* For the Endpoints, the ID will be either 64 or 65, so setting + * num_counters to 100 */ + struct counters_subgroup *rx_obj_subgroup = create_counters_subgroup( + "RX Object counters", COUNTER_SUBGROUP_ID_RX_OBJ, 100); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_OPEN, + "Object Open"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_RP, + "Object RP"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_NOPATH, + "Object Nopath"); + create_subgroup_counter( + rx_obj_subgroup, + ((PCEP_OBJ_CLASS_ENDPOINTS << 4) | PCEP_OBJ_TYPE_ENDPOINT_IPV4), + "Object Endpoint IPv4"); + create_subgroup_counter( + rx_obj_subgroup, + ((PCEP_OBJ_CLASS_ENDPOINTS << 4) | PCEP_OBJ_TYPE_ENDPOINT_IPV6), + "Object Endpoint IPv6"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_BANDWIDTH, + "Object Bandwidth"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_METRIC, + "Object Metric"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ERO, + "Object ERO"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_RRO, + "Object RRO"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_LSPA, + "Object LSPA"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_IRO, + "Object IRO"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SVEC, + "Object SVEC"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_NOTF, + "Object Notify"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ERROR, + "Object Error"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_CLOSE, + "Object Close"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_LSP, + "Object LSP"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SRP, + "Object SRP"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_VENDOR_INFO, + "Object Vendor Info"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_INTER_LAYER, + "Object Inter-Layer"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SWITCH_LAYER, + "Object Switch-Layer"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_REQ_ADAP_CAP, + "Object Requested Adap-Cap"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_SERVER_IND, + "Object Server-Indication"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_ASSOCIATION, + "Object Association"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_MAX, + "Object Unknown"); + create_subgroup_counter(rx_obj_subgroup, PCEP_OBJ_CLASS_MAX + 1, + "Object Erroneous"); + + struct counters_subgroup *tx_obj_subgroup = + clone_counters_subgroup(rx_obj_subgroup, "TX Object counters", + COUNTER_SUBGROUP_ID_TX_OBJ); + + /* + * Sub-Object RX and TX counters + */ + struct counters_subgroup *rx_subobj_subgroup = create_counters_subgroup( + "RX RO Sub-Object counters", COUNTER_SUBGROUP_ID_RX_SUBOBJ, + RO_SUBOBJ_UNKNOWN + 2); + create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_IPV4, + "RO Sub-Object IPv4"); + create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_IPV6, + "RO Sub-Object IPv6"); + create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_LABEL, + "RO Sub-Object Label"); + create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_UNNUM, + "RO Sub-Object Unnum"); + create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_ASN, + "RO Sub-Object ASN"); + create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_TYPE_SR, + "RO Sub-Object SR"); + create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_UNKNOWN, + "RO Sub-Object Unknown"); + create_subgroup_counter(rx_subobj_subgroup, RO_SUBOBJ_UNKNOWN + 1, + "RO Sub-Object Erroneous"); + + struct counters_subgroup *tx_subobj_subgroup = clone_counters_subgroup( + rx_subobj_subgroup, "TX RO Sub-Object counters", + COUNTER_SUBGROUP_ID_TX_SUBOBJ); + + /* + * RO SR Sub-Object RX and TX counters + */ + struct counters_subgroup *rx_subobj_sr_nai_subgroup = + create_counters_subgroup("RX RO SR NAI Sub-Object counters", + COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ, + PCEP_SR_SUBOBJ_NAI_UNKNOWN + 1); + create_subgroup_counter(rx_subobj_sr_nai_subgroup, + PCEP_SR_SUBOBJ_NAI_ABSENT, + "RO Sub-Object SR NAI absent"); + create_subgroup_counter(rx_subobj_sr_nai_subgroup, + PCEP_SR_SUBOBJ_NAI_IPV4_NODE, + "RO Sub-Object SR NAI IPv4 Node"); + create_subgroup_counter(rx_subobj_sr_nai_subgroup, + PCEP_SR_SUBOBJ_NAI_IPV6_NODE, + "RO Sub-Object SR NAI IPv6 Node"); + create_subgroup_counter(rx_subobj_sr_nai_subgroup, + PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, + "RO Sub-Object SR NAI IPv4 Adj"); + create_subgroup_counter(rx_subobj_sr_nai_subgroup, + PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, + "RO Sub-Object SR NAI IPv6 Adj"); + create_subgroup_counter(rx_subobj_sr_nai_subgroup, + PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, + "RO Sub-Object SR NAI Unnumbered IPv4 Adj"); + create_subgroup_counter(rx_subobj_sr_nai_subgroup, + PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, + "RO Sub-Object SR NAI Link Local IPv6 Adj"); + create_subgroup_counter(rx_subobj_sr_nai_subgroup, + PCEP_SR_SUBOBJ_NAI_UNKNOWN, + "RO Sub-Object SR NAI Unknown"); + + struct counters_subgroup *tx_subobj_sr_nai_subgroup = + clone_counters_subgroup(rx_subobj_sr_nai_subgroup, + "TX RO SR NAI Sub-Object counters", + COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ); + + /* + * TLV RX and TX counters + */ + struct counters_subgroup *rx_tlv_subgroup = create_counters_subgroup( + "RX TLV counters", COUNTER_SUBGROUP_ID_RX_TLV, + PCEP_OBJ_TLV_TYPE_UNKNOWN + 1); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR, + "TLV No Path Vector"); + create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_VENDOR_INFO, + "TLV Vendor Info"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY, + "TLV Stateful PCE Capability"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME, + "TLV Symbolic Path Name"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS, + "TLV IPv4 LSP Identifier"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS, + "TLV IPv6 LSP Identifier"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE, + "TLV LSP Error Code"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC, + "TLV RSVP Error Spec"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION, + "TLV LSP DB Version"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID, + "TLV Speaker Entity ID"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY, + "TLV SR PCE Capability"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE, + "TLV Path Setup Type"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY, + "TLV Path Setup Type Capability"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID, + "TLV SR Policy PolId"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME, + "TLV SR Policy PolName"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID, + "TLV SR Policy CpathId"); + create_subgroup_counter(rx_tlv_subgroup, + PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE, + "TLV SR Policy CpathRef"); + create_subgroup_counter(rx_tlv_subgroup, PCEP_OBJ_TLV_TYPE_UNKNOWN, + "TLV Unknown"); + + struct counters_subgroup *tx_tlv_subgroup = clone_counters_subgroup( + rx_tlv_subgroup, "TX TLV counters", COUNTER_SUBGROUP_ID_TX_TLV); + + /* + * PCEP Event counters + */ + struct counters_subgroup *events_subgroup = create_counters_subgroup( + "Events counters", COUNTER_SUBGROUP_ID_EVENT, MAX_COUNTERS); + create_subgroup_counter(events_subgroup, + PCEP_EVENT_COUNTER_ID_PCC_CONNECT, + "PCC connect"); + create_subgroup_counter(events_subgroup, + PCEP_EVENT_COUNTER_ID_PCE_CONNECT, + "PCE connect"); + create_subgroup_counter(events_subgroup, + PCEP_EVENT_COUNTER_ID_PCC_DISCONNECT, + "PCC disconnect"); + create_subgroup_counter(events_subgroup, + PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT, + "PCE disconnect"); + create_subgroup_counter(events_subgroup, + PCEP_EVENT_COUNTER_ID_TIMER_KEEPALIVE, + "Timer KeepAlive expired"); + create_subgroup_counter(events_subgroup, + PCEP_EVENT_COUNTER_ID_TIMER_DEADTIMER, + "Timer DeadTimer expired"); + create_subgroup_counter(events_subgroup, + PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPWAIT, + "Timer OpenKeepWait expired"); + create_subgroup_counter(events_subgroup, + PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPALIVE, + "Timer OpenKeepAlive expired"); + + /* + * Create the parent counters group + */ + time_t now = time(NULL); + char counters_name[MAX_COUNTER_STR_LENGTH] = {0}; + char ip_str[40] = {0}; + if (session->socket_comm_session->is_ipv6) { + inet_ntop(AF_INET6, + &session->socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv6.sin6_addr, + ip_str, 40); + } else { + inet_ntop(AF_INET, + &session->socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv4.sin_addr, + ip_str, 40); + } + snprintf(counters_name, MAX_COUNTER_STR_LENGTH, + "PCEP Session [%d], connected to [%s] for [%u seconds]", + session->session_id, ip_str, + (uint32_t)(now - session->time_connected)); + /* The (time(NULL) - session->time_connected) will probably be 0, + * so the group name will be updated when the counters are dumped */ + session->pcep_session_counters = + create_counters_group(counters_name, MAX_COUNTER_GROUPS); + + /* + * Add all the subgroups to the parent counters group + */ + add_counters_subgroup(session->pcep_session_counters, rx_msg_subgroup); + add_counters_subgroup(session->pcep_session_counters, tx_msg_subgroup); + add_counters_subgroup(session->pcep_session_counters, rx_obj_subgroup); + add_counters_subgroup(session->pcep_session_counters, tx_obj_subgroup); + add_counters_subgroup(session->pcep_session_counters, + rx_subobj_subgroup); + add_counters_subgroup(session->pcep_session_counters, + tx_subobj_subgroup); + add_counters_subgroup(session->pcep_session_counters, + rx_subobj_sr_nai_subgroup); + add_counters_subgroup(session->pcep_session_counters, + tx_subobj_sr_nai_subgroup); + add_counters_subgroup(session->pcep_session_counters, rx_tlv_subgroup); + add_counters_subgroup(session->pcep_session_counters, tx_tlv_subgroup); + add_counters_subgroup(session->pcep_session_counters, events_subgroup); +} + +/* Internal util function used by increment_message_rx_counters or + * increment_message_tx_counters */ +void increment_message_counters(pcep_session *session, + struct pcep_message *message, bool is_rx) +{ + uint16_t counter_subgroup_id_msg = (is_rx ? COUNTER_SUBGROUP_ID_RX_MSG + : COUNTER_SUBGROUP_ID_TX_MSG); + uint16_t counter_subgroup_id_obj = (is_rx ? COUNTER_SUBGROUP_ID_RX_OBJ + : COUNTER_SUBGROUP_ID_TX_OBJ); + uint16_t counter_subgroup_id_subobj = + (is_rx ? COUNTER_SUBGROUP_ID_RX_SUBOBJ + : COUNTER_SUBGROUP_ID_TX_SUBOBJ); + uint16_t counter_subgroup_id_ro_sr_subobj = + (is_rx ? COUNTER_SUBGROUP_ID_RX_RO_SR_SUBOBJ + : COUNTER_SUBGROUP_ID_TX_RO_SR_SUBOBJ); + uint16_t counter_subgroup_id_tlv = (is_rx ? COUNTER_SUBGROUP_ID_RX_TLV + : COUNTER_SUBGROUP_ID_TX_TLV); + + increment_counter(session->pcep_session_counters, + counter_subgroup_id_msg, message->msg_header->type); + + /* Iterate the objects */ + double_linked_list_node *obj_node = + (message->obj_list == NULL ? NULL : message->obj_list->head); + for (; obj_node != NULL; obj_node = obj_node->next_node) { + struct pcep_object_header *obj = + (struct pcep_object_header *)obj_node->data; + + /* Handle class: PCEP_OBJ_CLASS_ENDPOINTS, + * type: PCEP_OBJ_TYPE_ENDPOINT_IPV4 or + * PCEP_OBJ_TYPE_ENDPOINT_IPV6 */ + uint16_t obj_counter_id = + (obj->object_class == PCEP_OBJ_CLASS_ENDPOINTS + ? (obj->object_class << 4) | obj->object_type + : obj->object_class); + + increment_counter(session->pcep_session_counters, + counter_subgroup_id_obj, obj_counter_id); + + /* Iterate the RO Sub-objects */ + if (obj->object_class == PCEP_OBJ_CLASS_ERO + || obj->object_class == PCEP_OBJ_CLASS_IRO + || obj->object_class == PCEP_OBJ_CLASS_RRO) { + struct pcep_object_ro *ro_obj = + (struct pcep_object_ro *)obj; + + double_linked_list_node *ro_subobj_node = + (ro_obj->sub_objects == NULL + ? NULL + : ro_obj->sub_objects->head); + for (; ro_subobj_node != NULL; + ro_subobj_node = ro_subobj_node->next_node) { + struct pcep_object_ro_subobj *ro_subobj = + (struct pcep_object_ro_subobj *) + ro_subobj_node->data; + increment_counter( + session->pcep_session_counters, + counter_subgroup_id_subobj, + ro_subobj->ro_subobj_type); + + /* Handle the ro subobj type RO_SUBOBJ_TYPE_SR + * different NAI types */ + if (ro_subobj->ro_subobj_type + == RO_SUBOBJ_TYPE_SR) { + struct pcep_ro_subobj_sr *ro_sr_subobj = + (struct pcep_ro_subobj_sr *) + ro_subobj; + increment_counter( + session->pcep_session_counters, + counter_subgroup_id_ro_sr_subobj, + ro_sr_subobj->nai_type); + } + } + } + + /* Iterate the TLVs */ + double_linked_list_node *tlv_node = + (obj->tlv_list == NULL ? NULL : obj->tlv_list->head); + for (; tlv_node != NULL; tlv_node = tlv_node->next_node) { + struct pcep_object_tlv_header *tlv = + (struct pcep_object_tlv_header *)tlv_node->data; + increment_counter(session->pcep_session_counters, + counter_subgroup_id_tlv, tlv->type); + } + } +} + +void increment_message_rx_counters(pcep_session *session, + struct pcep_message *message) +{ + increment_message_counters(session, message, true); +} + +void increment_message_tx_counters(pcep_session *session, + struct pcep_message *message) +{ + increment_message_counters(session, message, false); +} + +void increment_event_counters( + pcep_session *session, + pcep_session_counters_event_counter_ids counter_id) +{ + increment_counter(session->pcep_session_counters, + COUNTER_SUBGROUP_ID_EVENT, counter_id); +} diff --git a/pceplib/pcep_session_logic_internals.h b/pceplib/pcep_session_logic_internals.h new file mode 100644 index 0000000..004459b --- /dev/null +++ b/pceplib/pcep_session_logic_internals.h @@ -0,0 +1,109 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +/* + * Internal Session Logic declarations, not intended to be in the public API. + */ + +#ifndef SRC_PCEPSESSIONLOGICINTERNALS_H_ +#define SRC_PCEPSESSIONLOGICINTERNALS_H_ + + +#include <pthread.h> +#include <stdbool.h> + +#include "pcep_msg_tools.h" + +#include "pcep_utils_double_linked_list.h" +#include "pcep_utils_ordered_list.h" +#include "pcep_utils_queue.h" + + +typedef struct pcep_session_logic_handle_ { + pthread_t session_logic_thread; + pthread_mutex_t session_logic_mutex; + pthread_cond_t session_logic_cond_var; + bool session_logic_condition; + bool active; + + ordered_list_handle *session_list; + pthread_mutex_t session_list_mutex; + /* Internal timers and socket events */ + queue_handle *session_event_queue; + +} pcep_session_logic_handle; + + +/* Used internally for Session events: message received, timer expired, + * or socket closed */ +typedef struct pcep_session_event_ { + pcep_session *session; + int expired_timer_id; + double_linked_list *received_msg_list; + bool socket_closed; + +} pcep_session_event; + +/* Event Counters counter-id definitions */ +typedef enum pcep_session_counters_event_counter_ids { + PCEP_EVENT_COUNTER_ID_PCC_CONNECT = 0, + PCEP_EVENT_COUNTER_ID_PCE_CONNECT = 1, + PCEP_EVENT_COUNTER_ID_PCC_DISCONNECT = 2, + PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT = 3, + PCEP_EVENT_COUNTER_ID_TIMER_KEEPALIVE = 4, + PCEP_EVENT_COUNTER_ID_TIMER_DEADTIMER = 5, + PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPWAIT = 6, + PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPALIVE = 7 + +} pcep_session_counters_event_counter_ids; + +/* functions implemented in pcep_session_logic_loop.c */ +void *session_logic_loop(void *data); +int session_logic_msg_ready_handler(void *data, int socket_fd); +void session_logic_message_sent_handler(void *data, int socket_fd); +void session_logic_conn_except_notifier(void *data, int socket_fd); +void session_logic_timer_expire_handler(void *data, int timer_id); + +void handle_timer_event(pcep_session_event *event); +void handle_socket_comm_event(pcep_session_event *event); +void session_send_message(pcep_session *session, struct pcep_message *message); + +/* defined in pcep_session_logic_states.c */ +void send_pcep_error(pcep_session *session, enum pcep_error_type error_type, + enum pcep_error_value error_value); +void enqueue_event(pcep_session *session, pcep_event_type event_type, + struct pcep_message *message); +void increment_unknown_message(pcep_session *session); + +/* defined in pcep_session_logic_counters.c */ +void create_session_counters(pcep_session *session); +void increment_event_counters( + pcep_session *session, + pcep_session_counters_event_counter_ids counter_id); +void increment_message_rx_counters(pcep_session *session, + struct pcep_message *message); + +/* defined in pcep_session_logic.c, also used in pcep_session_logic_states.c */ +struct pcep_message *create_pcep_open(pcep_session *session); + +#endif /* SRC_PCEPSESSIONLOGICINTERNALS_H_ */ diff --git a/pceplib/pcep_session_logic_loop.c b/pceplib/pcep_session_logic_loop.c new file mode 100644 index 0000000..77392fb --- /dev/null +++ b/pceplib/pcep_session_logic_loop.c @@ -0,0 +1,365 @@ +/* + * 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> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> + +#include "pcep_session_logic.h" +#include "pcep_session_logic_internals.h" +#include "pcep_timers.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +/* global var needed for callback handlers */ +extern pcep_session_logic_handle *session_logic_handle_; + +/* internal util function to create session_event's */ +static pcep_session_event *create_session_event(pcep_session *session) +{ + pcep_session_event *event = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_session_event)); + event->session = session; + event->expired_timer_id = TIMER_ID_NOT_SET; + event->received_msg_list = NULL; + event->socket_closed = false; + + return event; +} + + +/* A function pointer to this function is passed to pcep_socket_comm + * for each pcep_session creation, so it will be called whenever + * messages are ready to be read. This function will be called + * by the socket_comm thread. + * This function will decode the read PCEP message and give it + * to the session_logic_loop so it can be handled by the session_logic + * state machine. */ +int session_logic_msg_ready_handler(void *data, int socket_fd) +{ + if (data == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot handle msg_ready with NULL data", + __func__); + return -1; + } + + if (session_logic_handle_->active == false) { + pcep_log( + LOG_WARNING, + "%s: Received a message ready notification while the session logic is not active", + __func__); + return -1; + } + + pcep_session *session = (pcep_session *)data; + + pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex)); + session_logic_handle_->session_logic_condition = true; + + /* This event will ultimately be handled by handle_socket_comm_event() + * in pcep_session_logic_states.c */ + pcep_session_event *rcvd_msg_event = create_session_event(session); + + int msg_length = 0; + double_linked_list *msg_list = pcep_msg_read(socket_fd); + + if (msg_list == NULL) { + /* The socket was closed, or there was a socket read error */ + pcep_log(LOG_INFO, + "%s: PCEP connection closed for session [%d]", + __func__, session->session_id); + dll_destroy(msg_list); + rcvd_msg_event->socket_closed = true; + socket_comm_session_teardown(session->socket_comm_session); + pcep_session_cancel_timers(session); + session->socket_comm_session = NULL; + session->session_state = SESSION_STATE_INITIALIZED; + enqueue_event(session, PCE_CLOSED_SOCKET, NULL); + } else if (msg_list->num_entries == 0) { + /* Invalid message received */ + increment_unknown_message(session); + dll_destroy_with_data(msg_list); + } else { + /* Just logging the first of potentially several messages + * received */ + struct pcep_message *msg = + ((struct pcep_message *)msg_list->head->data); + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] session_logic_msg_ready_handler received message of type [%d] len [%d] on session [%d]", + __func__, time(NULL), pthread_self(), + msg->msg_header->type, msg->encoded_message_length, + session->session_id); + + rcvd_msg_event->received_msg_list = msg_list; + msg_length = msg->encoded_message_length; + } + + queue_enqueue(session_logic_handle_->session_event_queue, + rcvd_msg_event); + pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var)); + pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex)); + + return msg_length; +} + + +/* A function pointer to this function was passed to pcep_socket_comm, + * so it will be called when a message is sent. This is useful since + * message sending is asynchronous, and there are times that actions + * need to be performed only after a message has been sent. */ +void session_logic_message_sent_handler(void *data, int socket_fd) +{ + (void)socket_fd; + + if (data == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot handle msg_sent with NULL data", __func__); + return; + } + + pcep_session *session = (pcep_session *)data; + if (session->destroy_session_after_write == true) { + /* Do not call destroy until all of the queued messages are + * written */ + if (session->socket_comm_session != NULL + && session->socket_comm_session->message_queue->num_entries + == 0) { + destroy_pcep_session(session); + } + } else { + /* Reset the keep alive timer for every message sent on + * the session, only if the session is not destroyed */ + if (session->timer_id_keep_alive == TIMER_ID_NOT_SET) { + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic set keep alive timer [%d secs] for session [%d]", + __func__, time(NULL), pthread_self(), + session->pcc_config + .keep_alive_pce_negotiated_timer_seconds, + session->session_id); + session->timer_id_keep_alive = create_timer( + session->pcc_config + .keep_alive_pce_negotiated_timer_seconds, + session); + } else { + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic reset keep alive timer [%d secs] for session [%d]", + __func__, time(NULL), pthread_self(), + session->pcc_config + .keep_alive_pce_negotiated_timer_seconds, + session->session_id); + reset_timer(session->timer_id_keep_alive); + } + } +} + + +/* A function pointer to this function was passed to pcep_socket_comm, + * so it will be called whenever the socket is closed. this function + * will be called by the socket_comm thread. */ +void session_logic_conn_except_notifier(void *data, int socket_fd) +{ + if (data == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot handle conn_except with NULL data", + __func__); + return; + } + + if (session_logic_handle_->active == false) { + pcep_log( + LOG_WARNING, + "%s: Received a connection exception notification while the session logic is not active", + __func__); + return; + } + + pcep_session *session = (pcep_session *)data; + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic session_logic_conn_except_notifier socket closed [%d], session [%d]", + __func__, time(NULL), pthread_self(), socket_fd, + session->session_id); + + pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex)); + pcep_session_event *socket_event = create_session_event(session); + socket_event->socket_closed = true; + queue_enqueue(session_logic_handle_->session_event_queue, socket_event); + session_logic_handle_->session_logic_condition = true; + + pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var)); + pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex)); +} + + +/* + * this method is the timer expire handler, and will only + * pass the event to the session_logic loop and notify it + * that there is a timer available. this function will be + * called by the timers thread. + */ +void session_logic_timer_expire_handler(void *data, int timer_id) +{ + if (data == NULL) { + pcep_log(LOG_WARNING, "%s: Cannot handle timer with NULL data", + __func__); + return; + } + + if (session_logic_handle_->active == false) { + pcep_log( + LOG_WARNING, + "%s: Received a timer expiration while the session logic is not active", + __func__); + return; + } + + pcep_log(LOG_INFO, "%s: [%ld-%ld] timer expired handler timer_id [%d]", + __func__, time(NULL), pthread_self(), timer_id); + pcep_session_event *expired_timer_event = + create_session_event((pcep_session *)data); + expired_timer_event->expired_timer_id = timer_id; + + pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex)); + session_logic_handle_->session_logic_condition = true; + queue_enqueue(session_logic_handle_->session_event_queue, + expired_timer_event); + + pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var)); + pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex)); +} + + +/* + * session_logic event loop + * this function is called upon thread creation from pcep_session_logic.c + */ +void *session_logic_loop(void *data) +{ + if (data == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot start session_logic_loop with NULL data", + __func__); + + return NULL; + } + + pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting session_logic_loop thread", + __func__, time(NULL), pthread_self()); + + pcep_session_logic_handle *session_logic_handle = + (pcep_session_logic_handle *)data; + + while (session_logic_handle->active) { + /* Mutex locking for session_logic_loop condition variable */ + pthread_mutex_lock( + &(session_logic_handle->session_logic_mutex)); + + /* this internal loop helps avoid spurious interrupts */ + while (!session_logic_handle->session_logic_condition) { + pthread_cond_wait( + &(session_logic_handle->session_logic_cond_var), + &(session_logic_handle->session_logic_mutex)); + } + + pcep_session_event *event = queue_dequeue( + session_logic_handle->session_event_queue); + while (event != NULL) { + if (event->session == NULL) { + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] Invalid session_logic_loop event [%s] with NULL session", + __func__, time(NULL), pthread_self(), + (event->expired_timer_id + != TIMER_ID_NOT_SET) + ? "timer" + : "message"); + pceplib_free(PCEPLIB_INFRA, event); + event = queue_dequeue( + session_logic_handle + ->session_event_queue); + continue; + } + + /* Check if the session still exists, and synchronize + * possible session destroy */ + pcep_log( + LOG_DEBUG, + "%s: session_logic_loop checking session_list sessionPtr %p", + __func__, event->session); + pthread_mutex_lock( + &(session_logic_handle->session_list_mutex)); + if (ordered_list_find( + session_logic_handle->session_list, + event->session) + == NULL) { + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] In-flight event [%s] for destroyed session being discarded", + __func__, time(NULL), pthread_self(), + (event->expired_timer_id + != TIMER_ID_NOT_SET) + ? "timer" + : "message"); + pceplib_free(PCEPLIB_INFRA, event); + event = queue_dequeue( + session_logic_handle + ->session_event_queue); + pthread_mutex_unlock( + &(session_logic_handle + ->session_list_mutex)); + continue; + } + + if (event->expired_timer_id != TIMER_ID_NOT_SET) { + handle_timer_event(event); + } + + if (event->received_msg_list != NULL) { + handle_socket_comm_event(event); + } + + pceplib_free(PCEPLIB_INFRA, event); + event = queue_dequeue( + session_logic_handle->session_event_queue); + + pthread_mutex_unlock( + &(session_logic_handle->session_list_mutex)); + } + + session_logic_handle->session_logic_condition = false; + pthread_mutex_unlock( + &(session_logic_handle->session_logic_mutex)); + } + + pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished session_logic_loop thread", + __func__, time(NULL), pthread_self()); + + return NULL; +} diff --git a/pceplib/pcep_session_logic_states.c b/pceplib/pcep_session_logic_states.c new file mode 100644 index 0000000..3e9c701 --- /dev/null +++ b/pceplib/pcep_session_logic_states.c @@ -0,0 +1,1139 @@ +/* + * 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> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include "pcep_msg_encoding.h" +#include "pcep_session_logic.h" +#include "pcep_session_logic_internals.h" +#include "pcep_timers.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +#define TIMER_OPEN_KEEP_ALIVE_SECONDS 1 + +/* Session Logic Handle managed in pcep_session_logic.c */ +extern pcep_event_queue *session_logic_event_queue_; +void send_keep_alive(pcep_session *session); +void send_pcep_error_with_object(pcep_session *session, + enum pcep_error_type error_type, + enum pcep_error_value error_value, + struct pcep_object_header *object); +void reset_dead_timer(pcep_session *session); +bool verify_pcep_open_object(pcep_session *session, + struct pcep_object_open *open_object); +void send_reconciled_pcep_open(pcep_session *session, + struct pcep_message *error_msg); +bool handle_pcep_update(pcep_session *session, struct pcep_message *upd_msg); +bool handle_pcep_initiate(pcep_session *session, struct pcep_message *init_msg); +bool check_and_send_open_keep_alive(pcep_session *session); +void log_pcc_pce_connection(pcep_session *session); +bool handle_pcep_open(pcep_session *session, struct pcep_message *open_msg); + +/* + * util functions called by the state handling below + */ + +void send_keep_alive(pcep_session *session) +{ + struct pcep_message *keep_alive_msg = pcep_msg_create_keepalive(); + + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic send keep_alive message for session [%d]", + __func__, time(NULL), pthread_self(), session->session_id); + + session_send_message(session, keep_alive_msg); + + /* The keep alive timer will be (re)set once the message + * is sent in session_logic_message_sent_handler() */ +} + + +/* Send an error message with the corrected or offending object */ +void send_pcep_error_with_object(pcep_session *session, + enum pcep_error_type error_type, + enum pcep_error_value error_value, + struct pcep_object_header *object) +{ + double_linked_list *obj_list = dll_initialize(); + dll_append(obj_list, object); + struct pcep_message *error_msg = pcep_msg_create_error_with_objects( + error_type, error_value, obj_list); + + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic send error message with object [%d][%d] for session [%d]", + __func__, time(NULL), pthread_self(), error_type, error_value, + session->session_id); + + session_send_message(session, error_msg); +} + + +void send_pcep_error(pcep_session *session, enum pcep_error_type error_type, + enum pcep_error_value error_value) +{ + struct pcep_message *error_msg = + pcep_msg_create_error(error_type, error_value); + + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic send error message [%d][%d] for session [%d]", + __func__, time(NULL), pthread_self(), error_type, error_value, + session->session_id); + + session_send_message(session, error_msg); +} + + +void reset_dead_timer(pcep_session *session) +{ + /* Default to configured dead_timer if its not set yet or set to 0 by + * the PCE */ + int dead_timer_seconds = + (session->pcc_config.dead_timer_pce_negotiated_seconds == 0) + ? session->pcc_config.dead_timer_seconds + : session->pcc_config.dead_timer_pce_negotiated_seconds; + + if (session->timer_id_dead_timer == TIMER_ID_NOT_SET) { + session->timer_id_dead_timer = + create_timer(dead_timer_seconds, session); + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic set dead timer [%d secs] id [%d] for session [%d]", + __func__, time(NULL), pthread_self(), + dead_timer_seconds, session->timer_id_dead_timer, + session->session_id); + } else { + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic reset dead timer [%d secs] id [%d] for session [%d]", + __func__, time(NULL), pthread_self(), + dead_timer_seconds, session->timer_id_dead_timer, + session->session_id); + reset_timer(session->timer_id_dead_timer); + } +} + + +void enqueue_event(pcep_session *session, pcep_event_type event_type, + struct pcep_message *message) +{ + if (event_type == MESSAGE_RECEIVED && message == NULL) { + pcep_log( + LOG_WARNING, + "%s: enqueue_event cannot enqueue a NULL message session [%d]", + __func__, session->session_id); + return; + } + + pcep_event *event = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event)); + memset(event, 0, sizeof(pcep_event)); + + event->session = session; + event->event_type = event_type; + event->event_time = time(NULL); + event->message = message; + + pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex); + if (session_logic_event_queue_->event_callback != NULL) { + session_logic_event_queue_->event_callback( + session_logic_event_queue_->event_callback_data, event); + } else { + queue_enqueue(session_logic_event_queue_->event_queue, event); + } + pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex); +} + +/* Verify the received PCEP Open object parameters are acceptable. If not, + * update the unacceptable value(s) with an acceptable value so it can be sent + * back to the sender. */ +bool verify_pcep_open_object(pcep_session *session, + struct pcep_object_open *open_object) +{ + int retval = true; + + if (open_object->open_keepalive + < session->pcc_config.min_keep_alive_seconds) { + pcep_log( + LOG_INFO, + "%s: Rejecting unsupported Open Keep Alive value [%d] min [%d]", + __func__, open_object->open_keepalive, + session->pcc_config.min_keep_alive_seconds); + open_object->open_keepalive = + session->pcc_config.min_keep_alive_seconds; + retval = false; + } else if (open_object->open_keepalive + > session->pcc_config.max_keep_alive_seconds) { + pcep_log( + LOG_INFO, + "%s: Rejecting unsupported Open Keep Alive value [%d] max [%d]", + __func__, open_object->open_keepalive, + session->pcc_config.max_keep_alive_seconds); + open_object->open_keepalive = + session->pcc_config.max_keep_alive_seconds; + retval = false; + } + + if (open_object->open_deadtimer + < session->pcc_config.min_dead_timer_seconds) { + pcep_log(LOG_INFO, + "%s: Rejecting unsupported Open Dead Timer value [%d]", + __func__, open_object->open_deadtimer); + open_object->open_deadtimer = + session->pcc_config.min_dead_timer_seconds; + retval = false; + } else if (open_object->open_deadtimer + > session->pcc_config.max_dead_timer_seconds) { + pcep_log(LOG_INFO, + "%s: Rejecting unsupported Open Dead Timer value [%d]", + __func__, open_object->open_deadtimer); + open_object->open_deadtimer = + session->pcc_config.max_dead_timer_seconds; + retval = false; + } + + /* Check for Open Object TLVs */ + if (pcep_object_has_tlvs((struct pcep_object_header *)open_object) + == false) { + /* There are no TLVs, all done */ + return retval; + } + + double_linked_list_node *tlv_node = open_object->header.tlv_list->head; + while (tlv_node != NULL) { + struct pcep_object_tlv_header *tlv = tlv_node->data; + tlv_node = tlv_node->next_node; + + /* Supported Open Object TLVs */ + switch (tlv->type) { + case PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION: + case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY: + case PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID: + case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY: + case PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY: + break; + + default: + /* TODO how to handle unrecognized TLV ?? */ + pcep_log( + LOG_INFO, + "%s: Unhandled OPEN Object TLV type: %d, length %d", + __func__, tlv->type, tlv->encoded_tlv_length); + break; + } + + /* Verify the STATEFUL-PCE-CAPABILITY TLV */ + if (tlv->type == PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY) { + struct pcep_object_tlv_stateful_pce_capability + *pce_cap_tlv = + (struct + pcep_object_tlv_stateful_pce_capability + *)tlv; + + /* If the U flag is set, then the PCE is + * capable of updating LSP parameters */ + if (pce_cap_tlv->flag_u_lsp_update_capability) { + if (session->pce_config + .support_stateful_pce_lsp_update + == false) { + /* Turn off the U bit, as it is not + * supported */ + pcep_log( + LOG_INFO, + "%s: Rejecting unsupported Open STATEFUL-PCE-CAPABILITY TLV U flag", + __func__); + pce_cap_tlv + ->flag_u_lsp_update_capability = + false; + retval = false; + } else { + session->stateful_pce = true; + pcep_log( + LOG_INFO, + "%s: Setting PCEP session [%d] STATEFUL to support LSP updates", + __func__, session->session_id); + } + } + /* TODO the rest of the flags are not implemented yet */ + else if (pce_cap_tlv->flag_s_include_db_version) { + pcep_log( + LOG_INFO, + "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV S Include DB Version flag", + __func__); + } else if ( + pce_cap_tlv + ->flag_i_lsp_instantiation_capability) { + pcep_log( + LOG_INFO, + "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV I LSP Instantiation Capability flag", + __func__); + } else if (pce_cap_tlv->flag_t_triggered_resync) { + pcep_log( + LOG_INFO, + "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV T Triggered Resync flag", + __func__); + } else if (pce_cap_tlv->flag_d_delta_lsp_sync) { + pcep_log( + LOG_INFO, + "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV D Delta LSP Sync flag", + __func__); + } else if (pce_cap_tlv->flag_f_triggered_initial_sync) { + pcep_log( + LOG_INFO, + "%s: Ignoring Open STATEFUL-PCE-CAPABILITY TLV F Triggered Initial Sync flag", + __func__); + } + } else if (tlv->type == PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION) { + if (session->pce_config.support_include_db_version + == false) { + pcep_log( + LOG_INFO, + "%s: Rejecting unsupported Open LSP DB VERSION TLV", + __func__); + /* Remove this TLV from the list */ + dll_delete_node(open_object->header.tlv_list, + tlv_node); + retval = false; + } + } + } + + return retval; +} + + +bool handle_pcep_open(pcep_session *session, struct pcep_message *open_msg) +{ + /* Open Message validation and errors according to: + * https://tools.ietf.org/html/rfc5440#section-7.15 */ + + if (session->session_state != SESSION_STATE_PCEP_CONNECTING + && session->session_state != SESSION_STATE_INITIALIZED) { + pcep_log( + LOG_INFO, + "%s: Received unexpected OPEN, current session state [%d, replying with error]", + __func__, session->session_state); + send_pcep_error(session, + PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION, + PCEP_ERRV_RECVD_INVALID_OPEN_MSG); + return false; + } + + if (session->pce_open_received == true + && session->pce_open_rejected == false) { + pcep_log(LOG_INFO, + "%s: Received duplicate OPEN, replying with error", + __func__); + send_pcep_error(session, + PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION, + PCEP_ERRV_RECVD_INVALID_OPEN_MSG); + return false; + } + + struct pcep_object_open *open_object = + (struct pcep_object_open *)pcep_obj_get(open_msg->obj_list, + PCEP_OBJ_CLASS_OPEN); + if (open_object == NULL) { + pcep_log( + LOG_INFO, + "%s: Received OPEN message with no OPEN object, replying with error", + __func__); + send_pcep_error(session, PCEP_ERRT_SESSION_FAILURE, + PCEP_ERRV_RECVD_INVALID_OPEN_MSG); + return false; + } + + /* Check for additional Open Msg objects */ + if (open_msg->obj_list->num_entries > 1) { + pcep_log( + LOG_INFO, + "%s: Found additional unsupported objects in the Open message, replying with error", + __func__); + send_pcep_error(session, PCEP_ERRT_SESSION_FAILURE, + PCEP_ERRV_RECVD_INVALID_OPEN_MSG); + return false; + } + + session->pce_open_received = true; + + /* Verify the open object parameters and TLVs */ + if (verify_pcep_open_object(session, open_object) == false) { + enqueue_event(session, PCC_RCVD_INVALID_OPEN, NULL); + if (session->pce_open_rejected) { + /* The Open message was already rejected once, so + * according to the spec, send an error message and + * close the TCP connection. */ + pcep_log( + LOG_INFO, + "%s: Received 2 consecutive unsupported Open messages, closing the connection.", + __func__); + send_pcep_error( + session, PCEP_ERRT_SESSION_FAILURE, + PCEP_ERRV_RECVD_SECOND_OPEN_MSG_UNACCEPTABLE); + socket_comm_session_close_tcp_after_write( + session->socket_comm_session); + session->session_state = SESSION_STATE_INITIALIZED; + enqueue_event(session, PCC_CONNECTION_FAILURE, NULL); + } else { + session->pce_open_rejected = true; + /* Clone the object here, since the encapsulating + * message will be deleted in handle_socket_comm_event() + * most likely before this error message is sent */ + struct pcep_object_open *cloned_open_object = + pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct pcep_object_open)); + memcpy(cloned_open_object, open_object, + sizeof(struct pcep_object_open)); + open_object->header.tlv_list = NULL; + cloned_open_object->header.encoded_object = NULL; + cloned_open_object->header.encoded_object_length = 0; + send_pcep_error_with_object( + session, PCEP_ERRT_SESSION_FAILURE, + PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG, + &cloned_open_object->header); + } + + return false; + } + + /* + * Open Message accepted + * Sending the keep-alive response will be managed the function caller + */ + + session->timer_id_open_keep_alive = + create_timer(TIMER_OPEN_KEEP_ALIVE_SECONDS, session); + session->pcc_config.dead_timer_pce_negotiated_seconds = + (int)open_object->open_deadtimer; + /* Cancel the timer so we can change the dead_timer value */ + cancel_timer(session->timer_id_dead_timer); + session->timer_id_dead_timer = TIMER_ID_NOT_SET; + reset_dead_timer(session); + + return true; +} + + +/* The original PCEP Open message sent to the PCE was rejected, + * try to reconcile the differences and re-send a new Open. */ +void send_reconciled_pcep_open(pcep_session *session, + struct pcep_message *error_msg) +{ + struct pcep_message *open_msg = create_pcep_open(session); + + struct pcep_object_open *error_open_obj = + (struct pcep_object_open *)pcep_obj_get(error_msg->obj_list, + PCEP_OBJ_CLASS_OPEN); + if (error_open_obj == NULL) { + /* Nothing to reconcile, send the same Open message again */ + pcep_log( + LOG_INFO, + "%s: No Open object received in Error, sending the same Open message", + __func__); + session_send_message(session, open_msg); + return; + } + + struct pcep_object_open *open_obj = + (struct pcep_object_open *)pcep_obj_get(open_msg->obj_list, + PCEP_OBJ_CLASS_OPEN); + // open_msg can not have empty obj_list + assert(open_obj != NULL); + + if (error_open_obj->open_deadtimer + != session->pce_config.dead_timer_seconds) { + if (error_open_obj->open_deadtimer + >= session->pce_config.min_dead_timer_seconds + && error_open_obj->open_deadtimer + <= session->pce_config.max_dead_timer_seconds) { + open_obj->open_deadtimer = + error_open_obj->open_deadtimer; + session->pcc_config.dead_timer_pce_negotiated_seconds = + error_open_obj->open_deadtimer; + pcep_log( + LOG_INFO, + "%s: Open deadtimer value [%d] rejected, using PCE value [%d]", + __func__, + session->pcc_config.dead_timer_seconds, + session->pcc_config + .dead_timer_pce_negotiated_seconds); + /* Reset the timer with the new value */ + cancel_timer(session->timer_id_dead_timer); + session->timer_id_dead_timer = TIMER_ID_NOT_SET; + reset_dead_timer(session); + } else { + pcep_log( + LOG_INFO, + "%s: Can not reconcile Open with suggested deadtimer [%d]", + __func__, error_open_obj->open_deadtimer); + } + } + + if (error_open_obj->open_keepalive + != session->pce_config.keep_alive_seconds) { + if (error_open_obj->open_keepalive + >= session->pce_config.min_keep_alive_seconds + && error_open_obj->open_keepalive + <= session->pce_config.max_keep_alive_seconds) { + open_obj->open_keepalive = + error_open_obj->open_keepalive; + session->pcc_config + .keep_alive_pce_negotiated_timer_seconds = + error_open_obj->open_keepalive; + pcep_log( + LOG_INFO, + "%s: Open keep alive value [%d] rejected, using PCE value [%d]", + __func__, + session->pcc_config.keep_alive_seconds, + session->pcc_config + .keep_alive_pce_negotiated_timer_seconds); + /* Cancel the timer, the timer will be set again with + * the new value when this open message is sent */ + cancel_timer(session->timer_id_keep_alive); + session->timer_id_keep_alive = TIMER_ID_NOT_SET; + } else { + pcep_log( + LOG_INFO, + "%s: Can not reconcile Open with suggested keepalive [%d]", + __func__, error_open_obj->open_keepalive); + } + } + + /* TODO reconcile the TLVs */ + + session_send_message(session, open_msg); + reset_timer(session->timer_id_open_keep_alive); +} + + +bool handle_pcep_update(pcep_session *session, struct pcep_message *upd_msg) +{ + /* Update Message validation and errors according to: + * https://tools.ietf.org/html/rfc8231#section-6.2 */ + + if (upd_msg->obj_list == NULL) { + pcep_log(LOG_INFO, + "%s: Invalid PcUpd message: Message has no objects", + __func__); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_SRP_OBJECT_MISSING); + return false; + } + + /* Verify the mandatory objects are present */ + struct pcep_object_header *obj = + pcep_obj_get(upd_msg->obj_list, PCEP_OBJ_CLASS_SRP); + if (obj == NULL) { + pcep_log(LOG_INFO, + "%s: Invalid PcUpd message: Missing SRP object", + __func__); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_SRP_OBJECT_MISSING); + return false; + } + + obj = pcep_obj_get(upd_msg->obj_list, PCEP_OBJ_CLASS_LSP); + if (obj == NULL) { + pcep_log(LOG_INFO, + "%s: Invalid PcUpd message: Missing LSP object", + __func__); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_LSP_OBJECT_MISSING); + return false; + } + + obj = pcep_obj_get(upd_msg->obj_list, PCEP_OBJ_CLASS_ERO); + if (obj == NULL) { + pcep_log(LOG_INFO, + "%s: Invalid PcUpd message: Missing ERO object", + __func__); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_ERO_OBJECT_MISSING); + return false; + } + + /* Verify the objects are are in the correct order */ + double_linked_list_node *node = upd_msg->obj_list->head; + struct pcep_object_srp *srp_object = + (struct pcep_object_srp *)node->data; + if (srp_object->header.object_class != PCEP_OBJ_CLASS_SRP) { + pcep_log( + LOG_INFO, + "%s: Invalid PcUpd message: First object must be an SRP, found [%d]", + __func__, srp_object->header.object_class); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_SRP_OBJECT_MISSING); + return false; + } + + node = node->next_node; + struct pcep_object_lsp *lsp_object = + (struct pcep_object_lsp *)node->data; + if (lsp_object->header.object_class != PCEP_OBJ_CLASS_LSP) { + pcep_log( + LOG_INFO, + "%s: Invalid PcUpd message: Second object must be an LSP, found [%d]", + __func__, lsp_object->header.object_class); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_LSP_OBJECT_MISSING); + return false; + } + + node = node->next_node; + struct pcep_object_ro *ero_object = node->data; + if (ero_object->header.object_class != PCEP_OBJ_CLASS_ERO) { + pcep_log( + LOG_INFO, + "%s: Invalid PcUpd message: Third object must be an ERO, found [%d]", + __func__, ero_object->header.object_class); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_ERO_OBJECT_MISSING); + return false; + } + + return true; +} + +bool handle_pcep_initiate(pcep_session *session, struct pcep_message *init_msg) +{ + /* Instantiate Message validation and errors according to: + * https://tools.ietf.org/html/rfc8281#section-5 */ + + if (init_msg->obj_list == NULL) { + pcep_log( + LOG_INFO, + "%s: Invalid PcInitiate message: Message has no objects", + __func__); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_SRP_OBJECT_MISSING); + return false; + } + + /* Verify the mandatory objects are present */ + struct pcep_object_header *obj = + pcep_obj_get(init_msg->obj_list, PCEP_OBJ_CLASS_SRP); + if (obj == NULL) { + pcep_log(LOG_INFO, + "%s: Invalid PcInitiate message: Missing SRP object", + __func__); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_SRP_OBJECT_MISSING); + return false; + } + + obj = pcep_obj_get(init_msg->obj_list, PCEP_OBJ_CLASS_LSP); + if (obj == NULL) { + pcep_log(LOG_INFO, + "%s: Invalid PcInitiate message: Missing LSP object", + __func__); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_LSP_OBJECT_MISSING); + return false; + } + + /* Verify the objects are are in the correct order */ + double_linked_list_node *node = init_msg->obj_list->head; + struct pcep_object_srp *srp_object = + (struct pcep_object_srp *)node->data; + if (srp_object->header.object_class != PCEP_OBJ_CLASS_SRP) { + pcep_log( + LOG_INFO, + "%s: Invalid PcInitiate message: First object must be an SRP, found [%d]", + __func__, srp_object->header.object_class); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_SRP_OBJECT_MISSING); + return false; + } + + node = node->next_node; + struct pcep_object_lsp *lsp_object = + (struct pcep_object_lsp *)node->data; + if (lsp_object->header.object_class != PCEP_OBJ_CLASS_LSP) { + pcep_log( + LOG_INFO, + "%s: Invalid PcInitiate message: Second object must be an LSP, found [%d]", + __func__, lsp_object->header.object_class); + send_pcep_error(session, PCEP_ERRT_MANDATORY_OBJECT_MISSING, + PCEP_ERRV_LSP_OBJECT_MISSING); + return false; + } + + /* There may be more optional objects */ + return true; +} + +void increment_unknown_message(pcep_session *session) +{ + /* https://tools.ietf.org/html/rfc5440#section-6.9 + * If a PCC/PCE receives unrecognized messages at a rate equal or + * greater than MAX-UNKNOWN-MESSAGES unknown message requests per + * minute, the PCC/PCE MUST send a PCEP CLOSE message */ + + time_t *unknown_message_time = + pceplib_malloc(PCEPLIB_INFRA, sizeof(time_t)); + *unknown_message_time = time(NULL); + time_t expire_time = *unknown_message_time + 60; + queue_enqueue(session->num_unknown_messages_time_queue, + unknown_message_time); + + /* Purge any entries older than 1 minute. The oldest entries are at the + * queue head */ + queue_node *time_node = session->num_unknown_messages_time_queue->head; + while (time_node != NULL) { + if (*((time_t *)time_node->data) > expire_time) { + pceplib_free( + PCEPLIB_INFRA, + queue_dequeue( + session->num_unknown_messages_time_queue)); + time_node = + session->num_unknown_messages_time_queue->head; + } else { + time_node = NULL; + } + } + + if ((int)session->num_unknown_messages_time_queue->num_entries + >= session->pcc_config.max_unknown_messages) { + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] Max unknown messages reached [%d] closing session [%d]", + __func__, time(NULL), pthread_self(), + session->pcc_config.max_unknown_messages, + session->session_id); + + close_pcep_session_with_reason(session, + PCEP_CLOSE_REASON_UNREC_MSG); + enqueue_event(session, PCC_RCVD_MAX_UNKOWN_MSGS, NULL); + } +} + +bool check_and_send_open_keep_alive(pcep_session *session) +{ + if (session->pce_open_received == true + && session->pce_open_rejected == false + && session->pce_open_keep_alive_sent == false) { + /* Send the PCE Open keep-alive response if it hasnt been sent + * yet */ + cancel_timer(session->timer_id_open_keep_alive); + session->timer_id_open_keep_alive = TIMER_ID_NOT_SET; + send_keep_alive(session); + session->pce_open_keep_alive_sent = true; + + return true; + } + + return false; +} + +void log_pcc_pce_connection(pcep_session *session) +{ + if (session->socket_comm_session == NULL) { + /* This only happens in UT */ + return; + } + + char src_ip_buf[40] = {0}, dst_ip_buf[40] = {0}; + uint16_t src_port, dst_port; + + if (session->socket_comm_session->is_ipv6) { + inet_ntop(AF_INET6, + &session->socket_comm_session->src_sock_addr + .src_sock_addr_ipv6.sin6_addr, + src_ip_buf, sizeof(src_ip_buf)); + inet_ntop(AF_INET6, + &session->socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv6.sin6_addr, + dst_ip_buf, sizeof(dst_ip_buf)); + src_port = htons(session->socket_comm_session->src_sock_addr + .src_sock_addr_ipv6.sin6_port); + dst_port = htons(session->socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv6.sin6_port); + } else { + inet_ntop(AF_INET, + &session->socket_comm_session->src_sock_addr + .src_sock_addr_ipv4.sin_addr, + src_ip_buf, sizeof(src_ip_buf)); + inet_ntop(AF_INET, + &session->socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv4.sin_addr, + dst_ip_buf, sizeof(dst_ip_buf)); + src_port = htons(session->socket_comm_session->src_sock_addr + .src_sock_addr_ipv4.sin_port); + dst_port = htons(session->socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv4.sin_port); + } + + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] Successful PCC [%s:%d] connection to PCE [%s:%d] session [%d] fd [%d]", + __func__, time(NULL), pthread_self(), src_ip_buf, src_port, + dst_ip_buf, dst_port, session->session_id, + session->socket_comm_session->socket_fd); +} + +/* + * these functions are called by session_logic_loop() from + * pcep_session_logic_loop.c these functions are executed in the + * session_logic_loop thread, and the mutex is locked before calling these + * functions, so they are thread safe. + */ + +/* state machine handling for expired timers */ +void handle_timer_event(pcep_session_event *event) +{ + if (event == NULL) { + pcep_log(LOG_INFO, "%s: handle_timer_event NULL event", + __func__); + return; + } + + pcep_session *session = event->session; + + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic handle_timer_event: session [%d] event timer_id [%d] session timers [OKW, OKA, DT, KA] [%d, %d, %d, %d]", + __func__, time(NULL), pthread_self(), session->session_id, + event->expired_timer_id, session->timer_id_open_keep_wait, + session->timer_id_open_keep_alive, session->timer_id_dead_timer, + session->timer_id_keep_alive); + + /* + * these timer expirations are independent of the session state + */ + if (event->expired_timer_id == session->timer_id_dead_timer) { + session->timer_id_dead_timer = TIMER_ID_NOT_SET; + increment_event_counters(session, + PCEP_EVENT_COUNTER_ID_TIMER_DEADTIMER); + close_pcep_session_with_reason(session, + PCEP_CLOSE_REASON_DEADTIMER); + enqueue_event(session, PCE_DEAD_TIMER_EXPIRED, NULL); + return; + } else if (event->expired_timer_id == session->timer_id_keep_alive) { + session->timer_id_keep_alive = TIMER_ID_NOT_SET; + increment_event_counters(session, + PCEP_EVENT_COUNTER_ID_TIMER_KEEPALIVE); + send_keep_alive(session); + return; + } + + /* + * handle timers that depend on the session state + */ + switch (session->session_state) { + case SESSION_STATE_PCEP_CONNECTING: + if (event->expired_timer_id + == session->timer_id_open_keep_wait) { + /* close the TCP session */ + pcep_log( + LOG_INFO, + "%s: handle_timer_event open_keep_wait timer expired for session [%d]", + __func__, session->session_id); + increment_event_counters( + session, + PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPWAIT); + socket_comm_session_close_tcp_after_write( + session->socket_comm_session); + session->session_state = SESSION_STATE_INITIALIZED; + session->timer_id_open_keep_wait = TIMER_ID_NOT_SET; + enqueue_event(session, PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED, + NULL); + } + + if (event->expired_timer_id + == session->timer_id_open_keep_alive) { + increment_event_counters( + session, + PCEP_EVENT_COUNTER_ID_TIMER_OPENKEEPALIVE); + session->timer_id_open_keep_alive = TIMER_ID_NOT_SET; + if (check_and_send_open_keep_alive(session) == true) { + if (session->pcc_open_accepted == true + && session->session_state + != SESSION_STATE_PCEP_CONNECTED) { + log_pcc_pce_connection(session); + session->session_state = + SESSION_STATE_PCEP_CONNECTED; + increment_event_counters( + session, + PCEP_EVENT_COUNTER_ID_PCE_CONNECT); + enqueue_event(session, + PCC_CONNECTED_TO_PCE, + NULL); + } + } + return; + } + break; + + case SESSION_STATE_INITIALIZED: + case SESSION_STATE_PCEP_CONNECTED: + default: + pcep_log( + LOG_INFO, + "%s: handle_timer_event unrecognized state transition, timer_id [%d] state [%d] session [%d]", + __func__, event->expired_timer_id, + session->session_state, session->session_id); + break; + } +} + +/* State machine handling for received messages. + * This event was created in session_logic_msg_ready_handler() in + * pcep_session_logic_loop.c */ +void handle_socket_comm_event(pcep_session_event *event) +{ + if (event == NULL) { + pcep_log(LOG_INFO, "%s: handle_socket_comm_event NULL event", + __func__); + return; + } + + pcep_session *session = event->session; + + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] pcep_session_logic handle_socket_comm_event: session [%d] num messages [%d] socket_closed [%d]", + __func__, time(NULL), pthread_self(), session->session_id, + (event->received_msg_list == NULL + ? -1 + : (int)event->received_msg_list->num_entries), + event->socket_closed); + + /* + * independent of the session state + */ + if (event->socket_closed) { + pcep_log( + LOG_INFO, + "%s: handle_socket_comm_event socket closed for session [%d]", + __func__, session->session_id); + socket_comm_session_close_tcp(session->socket_comm_session); + enqueue_event(session, PCE_CLOSED_SOCKET, NULL); + if (session->session_state == SESSION_STATE_PCEP_CONNECTING) { + enqueue_event(session, PCC_CONNECTION_FAILURE, NULL); + } + session->session_state = SESSION_STATE_INITIALIZED; + increment_event_counters(session, + PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT); + return; + } + + reset_dead_timer(session); + + if (event->received_msg_list == NULL) { + return; + } + + /* Message received on socket */ + double_linked_list_node *msg_node; + for (msg_node = event->received_msg_list->head; msg_node != NULL; + msg_node = msg_node->next_node) { + bool message_enqueued = false; + struct pcep_message *msg = + (struct pcep_message *)msg_node->data; + pcep_log(LOG_INFO, "%s: \t %s message", __func__, + get_message_type_str(msg->msg_header->type)); + + increment_message_rx_counters(session, msg); + + switch (msg->msg_header->type) { + case PCEP_TYPE_OPEN: + /* handle_pcep_open() checks session state, and for + * duplicate erroneous open messages, and replies with + * error messages as needed. It also sets + * pce_open_received. */ + if (handle_pcep_open(session, msg) == true) { + /* PCE Open Message Accepted */ + enqueue_event(session, MESSAGE_RECEIVED, msg); + message_enqueued = true; + session->pce_open_accepted = true; + session->pce_open_rejected = false; + if (session->pcc_open_accepted) { + /* If both the PCC and PCE Opens are + * accepted, then the session is + * connected */ + + check_and_send_open_keep_alive(session); + log_pcc_pce_connection(session); + session->session_state = + SESSION_STATE_PCEP_CONNECTED; + increment_event_counters( + session, + PCEP_EVENT_COUNTER_ID_PCE_CONNECT); + enqueue_event(session, + PCC_CONNECTED_TO_PCE, + NULL); + } + } + break; + + case PCEP_TYPE_KEEPALIVE: + if (session->session_state + == SESSION_STATE_PCEP_CONNECTING) { + /* PCC Open Message Accepted */ + cancel_timer(session->timer_id_open_keep_wait); + session->timer_id_open_keep_wait = + TIMER_ID_NOT_SET; + session->pcc_open_accepted = true; + session->pcc_open_rejected = false; + check_and_send_open_keep_alive(session); + + if (session->pce_open_accepted) { + /* If both the PCC and PCE Opens are + * accepted, then the session is + * connected */ + log_pcc_pce_connection(session); + session->session_state = + SESSION_STATE_PCEP_CONNECTED; + increment_event_counters( + session, + PCEP_EVENT_COUNTER_ID_PCC_CONNECT); + enqueue_event(session, + PCC_CONNECTED_TO_PCE, + NULL); + } + } + /* The dead_timer was already reset above, so nothing + * extra to do here */ + break; + + case PCEP_TYPE_PCREP: + enqueue_event(session, MESSAGE_RECEIVED, msg); + message_enqueued = true; + break; + + case PCEP_TYPE_CLOSE: + session->session_state = SESSION_STATE_INITIALIZED; + socket_comm_session_close_tcp( + session->socket_comm_session); + /* TODO should we also enqueue the message, so they can + * see the reasons?? */ + enqueue_event(session, PCE_SENT_PCEP_CLOSE, NULL); + /* TODO could this duplicate the disconnect counter with + * socket close ?? */ + increment_event_counters( + session, PCEP_EVENT_COUNTER_ID_PCE_DISCONNECT); + break; + + case PCEP_TYPE_PCREQ: + /* The PCC does not support receiving PcReq messages */ + send_pcep_error(session, + PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, + PCEP_ERRV_UNASSIGNED); + break; + + case PCEP_TYPE_REPORT: + /* The PCC does not support receiving Report messages */ + send_pcep_error(session, + PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, + PCEP_ERRV_UNASSIGNED); + break; + + case PCEP_TYPE_UPDATE: + /* Should reply with a PcRpt */ + if (handle_pcep_update(session, msg) == true) { + enqueue_event(session, MESSAGE_RECEIVED, msg); + message_enqueued = true; + } + break; + + case PCEP_TYPE_INITIATE: + /* Should reply with a PcRpt */ + if (handle_pcep_initiate(session, msg) == true) { + enqueue_event(session, MESSAGE_RECEIVED, msg); + message_enqueued = true; + } + break; + + case PCEP_TYPE_PCNOTF: + enqueue_event(session, MESSAGE_RECEIVED, msg); + message_enqueued = true; + break; + + case PCEP_TYPE_ERROR: + if (msg->obj_list != NULL + && msg->obj_list->num_entries > 0) { + struct pcep_object_header *obj_hdr = + pcep_obj_get(msg->obj_list, + PCEP_OBJ_CLASS_ERROR); + if (obj_hdr != NULL) { + struct pcep_object_error *error_obj = + (struct pcep_object_error *) + obj_hdr; + pcep_log( + LOG_DEBUG, + "%s: Error object [type, value] = [%s, %s]", + __func__, + get_error_type_str( + error_obj->error_type), + get_error_value_str( + error_obj->error_type, + error_obj + ->error_value)); + } + } + + if (session->session_state + == SESSION_STATE_PCEP_CONNECTING) { + /* A PCC_CONNECTION_FAILURE event will be sent + * when the socket is closed, if the state is + * SESSION_STATE_PCEP_CONNECTING, in case the + * PCE allows more than 2 failed open messages. + */ + pcep_log(LOG_INFO, + "%s: PCC Open message rejected by PCE", + __func__); + session->pcc_open_rejected = true; + send_reconciled_pcep_open(session, msg); + enqueue_event(session, PCC_SENT_INVALID_OPEN, + NULL); + } + enqueue_event(session, MESSAGE_RECEIVED, msg); + message_enqueued = true; + break; + + default: + pcep_log(LOG_INFO, "%s: \t UnSupported message", + __func__); + send_pcep_error(session, + PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, + PCEP_ERRV_UNASSIGNED); + increment_unknown_message(session); + break; + } + + /* if the message was enqueued, dont free it yet */ + if (message_enqueued == false) { + pcep_msg_free_message(msg); + } + } + dll_destroy(event->received_msg_list); +} diff --git a/pceplib/pcep_socket_comm.c b/pceplib/pcep_socket_comm.c new file mode 100644 index 0000000..e22eb6e --- /dev/null +++ b/pceplib/pcep_socket_comm.c @@ -0,0 +1,781 @@ +/* + * 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> + * + */ + + +/* + * Implementation of public API functions. + */ + +#include <zebra.h> + +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> // gethostbyname +#include <stdbool.h> +#include <string.h> +#include <unistd.h> // close + +#include <arpa/inet.h> // sockets etc. +#include <sys/types.h> // sockets etc. +#include <sys/socket.h> // sockets etc. + +#include "pcep.h" +#include "pcep_socket_comm.h" +#include "pcep_socket_comm_internals.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" +#include "pcep_utils_ordered_list.h" +#include "pcep_utils_queue.h" + +bool initialize_socket_comm_pre(void); +bool socket_comm_session_initialize_post( + pcep_socket_comm_session *socket_comm_session); + +pcep_socket_comm_handle *socket_comm_handle_ = NULL; + + +/* simple compare method callback used by pcep_utils_ordered_list + * for ordered list insertion. */ +int socket_fd_node_compare(void *list_entry, void *new_entry) +{ + return ((pcep_socket_comm_session *)new_entry)->socket_fd + - ((pcep_socket_comm_session *)list_entry)->socket_fd; +} + + +bool initialize_socket_comm_pre() +{ + socket_comm_handle_ = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_handle)); + memset(socket_comm_handle_, 0, sizeof(pcep_socket_comm_handle)); + + socket_comm_handle_->active = true; + socket_comm_handle_->num_active_sessions = 0; + socket_comm_handle_->read_list = + ordered_list_initialize(socket_fd_node_compare); + socket_comm_handle_->write_list = + ordered_list_initialize(socket_fd_node_compare); + socket_comm_handle_->session_list = + ordered_list_initialize(pointer_compare_function); + FD_ZERO(&socket_comm_handle_->except_master_set); + FD_ZERO(&socket_comm_handle_->read_master_set); + FD_ZERO(&socket_comm_handle_->write_master_set); + + if (pthread_mutex_init(&(socket_comm_handle_->socket_comm_mutex), NULL) + != 0) { + pcep_log(LOG_ERR, "%s: Cannot initialize socket_comm mutex.", + __func__); + pceplib_free(PCEPLIB_INFRA, socket_comm_handle_); + socket_comm_handle_ = NULL; + + return false; + } + + return true; +} + +bool initialize_socket_comm_external_infra( + void *external_infra_data, ext_socket_read socket_read_cb, + ext_socket_write socket_write_cb, + ext_socket_pthread_create_callback thread_create_func) +{ + if (socket_comm_handle_ != NULL) { + /* already initialized */ + return true; + } + + if (initialize_socket_comm_pre() == false) { + return false; + } + + /* Notice: If the thread_create_func is set, then both the + * socket_read_cb and the socket_write_cb SHOULD be NULL. */ + if (thread_create_func != NULL) { + if (thread_create_func( + &(socket_comm_handle_->socket_comm_thread), NULL, + socket_comm_loop, socket_comm_handle_, + "pceplib_timers")) { + pcep_log( + LOG_ERR, + "%s: Cannot initialize external socket_comm thread.", + __func__); + return false; + } + } + + socket_comm_handle_->external_infra_data = external_infra_data; + socket_comm_handle_->socket_write_func = socket_write_cb; + socket_comm_handle_->socket_read_func = socket_read_cb; + + return true; +} + +bool initialize_socket_comm_loop() +{ + if (socket_comm_handle_ != NULL) { + /* already initialized */ + return true; + } + + if (initialize_socket_comm_pre() == false) { + return false; + } + + /* Launch socket comm loop pthread */ + if (pthread_create(&(socket_comm_handle_->socket_comm_thread), NULL, + socket_comm_loop, socket_comm_handle_)) { + pcep_log(LOG_ERR, "%s: Cannot initialize socket_comm thread.", + __func__); + return false; + } + + return true; +} + + +bool destroy_socket_comm_loop() +{ + socket_comm_handle_->active = false; + + pthread_join(socket_comm_handle_->socket_comm_thread, NULL); + ordered_list_destroy(socket_comm_handle_->read_list); + ordered_list_destroy(socket_comm_handle_->write_list); + ordered_list_destroy(socket_comm_handle_->session_list); + pthread_mutex_destroy(&(socket_comm_handle_->socket_comm_mutex)); + + pceplib_free(PCEPLIB_INFRA, socket_comm_handle_); + socket_comm_handle_ = NULL; + + return true; +} + +/* Internal common init function */ +static pcep_socket_comm_session *socket_comm_session_initialize_pre( + message_received_handler message_handler, + message_ready_to_read_handler message_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, uint32_t connect_timeout_millis, + const char *tcp_authentication_str, bool is_tcp_auth_md5, + void *session_data) +{ + /* check that not both message handlers were set */ + if (message_handler != NULL && message_ready_handler != NULL) { + pcep_log( + LOG_WARNING, + "%s: Only one of <message_received_handler | message_ready_to_read_handler> can be set.", + __func__); + return NULL; + } + + /* check that at least one message handler was set */ + if (message_handler == NULL && message_ready_handler == NULL) { + pcep_log( + LOG_WARNING, + "%s: At least one of <message_received_handler | message_ready_to_read_handler> must be set.", + __func__); + return NULL; + } + + if (!initialize_socket_comm_loop()) { + pcep_log(LOG_WARNING, + "%s: ERROR: cannot initialize socket_comm_loop.", + __func__); + + return NULL; + } + + /* initialize everything for a pcep_session socket_comm */ + + pcep_socket_comm_session *socket_comm_session = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_session)); + memset(socket_comm_session, 0, sizeof(pcep_socket_comm_session)); + + socket_comm_handle_->num_active_sessions++; + socket_comm_session->close_after_write = false; + socket_comm_session->session_data = session_data; + socket_comm_session->message_handler = message_handler; + socket_comm_session->message_ready_to_read_handler = + message_ready_handler; + socket_comm_session->message_sent_handler = msg_sent_notifier; + socket_comm_session->conn_except_notifier = notifier; + socket_comm_session->message_queue = queue_initialize(); + socket_comm_session->connect_timeout_millis = connect_timeout_millis; + socket_comm_session->external_socket_data = NULL; + if (tcp_authentication_str != NULL) { + socket_comm_session->is_tcp_auth_md5 = is_tcp_auth_md5; + strlcpy(socket_comm_session->tcp_authentication_str, + tcp_authentication_str, + sizeof(socket_comm_session->tcp_authentication_str)); + } + + return socket_comm_session; +} + +/* Internal common init function */ +bool socket_comm_session_initialize_post( + pcep_socket_comm_session *socket_comm_session) +{ + /* If we dont use SO_REUSEADDR, the socket will take 2 TIME_WAIT + * periods before being closed in the kernel if bind() was called */ + int reuse_addr = 1; + if (setsockopt(socket_comm_session->socket_fd, SOL_SOCKET, SO_REUSEADDR, + &reuse_addr, sizeof(int)) + < 0) { + pcep_log( + LOG_WARNING, + "%s: Error in setsockopt() SO_REUSEADDR errno [%d %s].", + __func__, errno, strerror(errno)); + socket_comm_session_teardown(socket_comm_session); + + return false; + } + + struct sockaddr *src_sock_addr = + (socket_comm_session->is_ipv6 + ? (struct sockaddr *)&( + socket_comm_session->src_sock_addr + .src_sock_addr_ipv6) + : (struct sockaddr *)&( + socket_comm_session->src_sock_addr + .src_sock_addr_ipv4)); + int addr_len = (socket_comm_session->is_ipv6 + ? sizeof(socket_comm_session->src_sock_addr + .src_sock_addr_ipv6) + : sizeof(socket_comm_session->src_sock_addr + .src_sock_addr_ipv4)); + if (bind(socket_comm_session->socket_fd, src_sock_addr, addr_len) + == -1) { + pcep_log(LOG_WARNING, + "%s: Cannot bind address to socket errno [%d %s].", + __func__, errno, strerror(errno)); + socket_comm_session_teardown(socket_comm_session); + + return false; + } + + /* Register the session as active with the Socket Comm Loop */ + pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); + ordered_list_add_node(socket_comm_handle_->session_list, + socket_comm_session); + pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); + + /* dont connect to the destination yet, since the PCE will have a timer + * for max time between TCP connect and PCEP open. we'll connect later + * when we send the PCEP open. */ + + return true; +} + + +pcep_socket_comm_session *socket_comm_session_initialize( + message_received_handler message_handler, + message_ready_to_read_handler message_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, struct in_addr *dest_ip, + short dest_port, uint32_t connect_timeout_millis, + const char *tcp_authentication_str, bool is_tcp_auth_md5, + void *session_data) +{ + return socket_comm_session_initialize_with_src( + message_handler, message_ready_handler, msg_sent_notifier, + notifier, NULL, 0, dest_ip, dest_port, connect_timeout_millis, + tcp_authentication_str, is_tcp_auth_md5, session_data); +} + +pcep_socket_comm_session *socket_comm_session_initialize_ipv6( + message_received_handler message_handler, + message_ready_to_read_handler message_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, struct in6_addr *dest_ip, + short dest_port, uint32_t connect_timeout_millis, + const char *tcp_authentication_str, bool is_tcp_auth_md5, + void *session_data) +{ + return socket_comm_session_initialize_with_src_ipv6( + message_handler, message_ready_handler, msg_sent_notifier, + notifier, NULL, 0, dest_ip, dest_port, connect_timeout_millis, + tcp_authentication_str, is_tcp_auth_md5, session_data); +} + + +pcep_socket_comm_session *socket_comm_session_initialize_with_src( + message_received_handler message_handler, + message_ready_to_read_handler message_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, struct in_addr *src_ip, + short src_port, struct in_addr *dest_ip, short dest_port, + uint32_t connect_timeout_millis, const char *tcp_authentication_str, + bool is_tcp_auth_md5, void *session_data) +{ + if (dest_ip == NULL) { + pcep_log(LOG_WARNING, "%s: dest_ipv4 is NULL", __func__); + return NULL; + } + + pcep_socket_comm_session *socket_comm_session = + socket_comm_session_initialize_pre( + message_handler, message_ready_handler, + msg_sent_notifier, notifier, connect_timeout_millis, + tcp_authentication_str, is_tcp_auth_md5, session_data); + if (socket_comm_session == NULL) { + return NULL; + } + + socket_comm_session->socket_fd = + socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (socket_comm_session->socket_fd == -1) { + pcep_log(LOG_WARNING, + "%s: Cannot create ipv4 socket errno [%d %s].", + __func__, errno, strerror(errno)); + socket_comm_session_teardown( + socket_comm_session); // socket_comm_session freed + // inside fn so NOLINT next. + + return NULL; // NOLINT(clang-analyzer-unix.Malloc) + } + + socket_comm_session->is_ipv6 = false; + socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family = + AF_INET; + socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_family = + AF_INET; + socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port = + htons(dest_port); + socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_port = + htons(src_port); + socket_comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr + .s_addr = dest_ip->s_addr; + if (src_ip != NULL) { + socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr + .s_addr = src_ip->s_addr; + } else { + socket_comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr + .s_addr = INADDR_ANY; + } + + if (socket_comm_session_initialize_post(socket_comm_session) == false) { + return NULL; + } + + return socket_comm_session; +} + +pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6( + message_received_handler message_handler, + message_ready_to_read_handler message_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, struct in6_addr *src_ip, + short src_port, struct in6_addr *dest_ip, short dest_port, + uint32_t connect_timeout_millis, const char *tcp_authentication_str, + bool is_tcp_auth_md5, void *session_data) +{ + if (dest_ip == NULL) { + pcep_log(LOG_WARNING, "%s: dest_ipv6 is NULL", __func__); + return NULL; + } + + pcep_socket_comm_session *socket_comm_session = + socket_comm_session_initialize_pre( + message_handler, message_ready_handler, + msg_sent_notifier, notifier, connect_timeout_millis, + tcp_authentication_str, is_tcp_auth_md5, session_data); + if (socket_comm_session == NULL) { + return NULL; + } + + socket_comm_session->socket_fd = + socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (socket_comm_session->socket_fd == -1) { + pcep_log(LOG_WARNING, + "%s: Cannot create ipv6 socket errno [%d %s].", + __func__, errno, strerror(errno)); + socket_comm_session_teardown( + socket_comm_session); // socket_comm_session freed + // inside fn so NOLINT next. + + return NULL; // NOLINT(clang-analyzer-unix.Malloc) + } + + socket_comm_session->is_ipv6 = true; + socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family = + AF_INET6; + socket_comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_family = + AF_INET6; + socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port = + htons(dest_port); + socket_comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_port = + htons(src_port); + memcpy(&socket_comm_session->dest_sock_addr.dest_sock_addr_ipv6 + .sin6_addr, + dest_ip, sizeof(struct in6_addr)); + if (src_ip != NULL) { + memcpy(&socket_comm_session->src_sock_addr.src_sock_addr_ipv6 + .sin6_addr, + src_ip, sizeof(struct in6_addr)); + } else { + socket_comm_session->src_sock_addr.src_sock_addr_ipv6 + .sin6_addr = in6addr_any; + } + + if (socket_comm_session_initialize_post(socket_comm_session) == false) { + return NULL; + } + + return socket_comm_session; +} + + +bool socket_comm_session_connect_tcp( + pcep_socket_comm_session *socket_comm_session) +{ + if (socket_comm_session == NULL) { + pcep_log( + LOG_WARNING, + "%s: socket_comm_session_connect_tcp NULL socket_comm_session.", + __func__); + return NULL; + } + + /* Set the socket to non-blocking, so connect() does not block */ + int fcntl_arg; + if ((fcntl_arg = fcntl(socket_comm_session->socket_fd, F_GETFL, NULL)) + < 0) { + pcep_log(LOG_WARNING, "%s: Error fcntl(..., F_GETFL) [%d %s]", + __func__, errno, strerror(errno)); + return false; + } + + fcntl_arg |= O_NONBLOCK; + if (fcntl(socket_comm_session->socket_fd, F_SETFL, fcntl_arg) < 0) { + pcep_log(LOG_WARNING, "%s: Error fcntl(..., F_SETFL) [%d %s]", + __func__, errno, strerror(errno)); + return false; + } + +#if HAVE_DECL_TCP_MD5SIG + /* TCP authentication, currently only TCP MD5 RFC2385 is supported */ + if (socket_comm_session->tcp_authentication_str[0] != '\0') { +#if defined(linux) || defined(GNU_LINUX) + struct tcp_md5sig sig; + memset(&sig, 0, sizeof(sig)); + if (socket_comm_session->is_ipv6) { + memcpy(&sig.tcpm_addr, + &socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv6, + sizeof(struct sockaddr_in6)); + } else { + memcpy(&sig.tcpm_addr, + &socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv4, + sizeof(struct sockaddr_in)); + } + sig.tcpm_keylen = + strlen(socket_comm_session->tcp_authentication_str); + memcpy(sig.tcpm_key, + socket_comm_session->tcp_authentication_str, + sig.tcpm_keylen); +#else + int sig = 1; +#endif + if (setsockopt(socket_comm_session->socket_fd, IPPROTO_TCP, + TCP_MD5SIG, &sig, sizeof(sig)) + == -1) { + pcep_log(LOG_ERR, "%s: Failed to setsockopt(): [%d %s]", + __func__, errno, strerror(errno)); + return false; + } + } +#endif + + int connect_result = 0; + if (socket_comm_session->is_ipv6) { + connect_result = connect( + socket_comm_session->socket_fd, + (struct sockaddr *)&(socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv6), + sizeof(socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv6)); + } else { + connect_result = connect( + socket_comm_session->socket_fd, + (struct sockaddr *)&(socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv4), + sizeof(socket_comm_session->dest_sock_addr + .dest_sock_addr_ipv4)); + } + + if (connect_result < 0) { + if (errno == EINPROGRESS) { + /* Calculate the configured timeout in seconds and + * microseconds */ + struct timeval tv; + if (socket_comm_session->connect_timeout_millis + > 1000) { + tv.tv_sec = socket_comm_session + ->connect_timeout_millis + / 1000; + tv.tv_usec = (socket_comm_session + ->connect_timeout_millis + - (tv.tv_sec * 1000)) + * 1000; + } else { + tv.tv_sec = 0; + tv.tv_usec = socket_comm_session + ->connect_timeout_millis + * 1000; + } + + /* Use select to wait a max timeout for connect + * https://stackoverflow.com/questions/2597608/c-socket-connection-timeout + */ + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(socket_comm_session->socket_fd, &fdset); + if (select(socket_comm_session->socket_fd + 1, NULL, + &fdset, NULL, &tv) + > 0) { + int so_error; + socklen_t len = sizeof(so_error); + getsockopt(socket_comm_session->socket_fd, + SOL_SOCKET, SO_ERROR, &so_error, + &len); + if (so_error) { + pcep_log( + LOG_WARNING, + "%s: TCP connect failed on socket_fd [%d].", + __func__, + socket_comm_session->socket_fd); + return false; + } + } else { + pcep_log( + LOG_WARNING, + "%s: TCP connect timed-out on socket_fd [%d].", + __func__, + socket_comm_session->socket_fd); + return false; + } + } else { + pcep_log( + LOG_WARNING, + "%s: TCP connect, error connecting on socket_fd [%d] errno [%d %s]", + __func__, socket_comm_session->socket_fd, errno, + strerror(errno)); + return false; + } + } + + pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); + /* once the TCP connection is open, we should be ready to read at any + * time */ + ordered_list_add_node(socket_comm_handle_->read_list, + socket_comm_session); + + if (socket_comm_handle_->socket_read_func != NULL) { + socket_comm_handle_->socket_read_func( + socket_comm_handle_->external_infra_data, + &socket_comm_session->external_socket_data, + socket_comm_session->socket_fd, socket_comm_handle_); + } + pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); + + return true; +} + + +bool socket_comm_session_close_tcp( + pcep_socket_comm_session *socket_comm_session) +{ + if (socket_comm_session == NULL) { + pcep_log( + LOG_WARNING, + "%s: socket_comm_session_close_tcp NULL socket_comm_session.", + __func__); + return false; + } + + pcep_log(LOG_DEBUG, + "%s: socket_comm_session_close_tcp close() socket fd [%d]", + __func__, socket_comm_session->socket_fd); + + pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); + ordered_list_remove_first_node_equals(socket_comm_handle_->read_list, + socket_comm_session); + ordered_list_remove_first_node_equals(socket_comm_handle_->write_list, + socket_comm_session); + // TODO should it be close() or shutdown()?? + close(socket_comm_session->socket_fd); + socket_comm_session->socket_fd = -1; + pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); + + return true; +} + + +bool socket_comm_session_close_tcp_after_write( + pcep_socket_comm_session *socket_comm_session) +{ + if (socket_comm_session == NULL) { + pcep_log( + LOG_WARNING, + "%s: socket_comm_session_close_tcp_after_write NULL socket_comm_session.", + __func__); + return false; + } + + pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); + socket_comm_session->close_after_write = true; + pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); + + return true; +} + + +bool socket_comm_session_teardown(pcep_socket_comm_session *socket_comm_session) +{ + if (socket_comm_handle_ == NULL) { + pcep_log(LOG_WARNING, + "%s: Cannot teardown NULL socket_comm_handle", + __func__); + return false; + } + + if (socket_comm_session == NULL) { + pcep_log(LOG_WARNING, "%s: Cannot teardown NULL session", + __func__); + return false; + } + + if (comm_session_exists_locking(socket_comm_handle_, + socket_comm_session) + == false) { + pcep_log(LOG_WARNING, + "%s: Cannot teardown session that does not exist", + __func__); + return false; + } + + if (socket_comm_session->socket_fd >= 0) { + shutdown(socket_comm_session->socket_fd, SHUT_RDWR); + close(socket_comm_session->socket_fd); + } + + pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); + queue_destroy(socket_comm_session->message_queue); + ordered_list_remove_first_node_equals(socket_comm_handle_->session_list, + socket_comm_session); + ordered_list_remove_first_node_equals(socket_comm_handle_->read_list, + socket_comm_session); + ordered_list_remove_first_node_equals(socket_comm_handle_->write_list, + socket_comm_session); + socket_comm_handle_->num_active_sessions--; + pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); + + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] socket_comm_session fd [%d] destroyed, [%d] sessions remaining", + __func__, time(NULL), pthread_self(), + socket_comm_session->socket_fd, + socket_comm_handle_->num_active_sessions); + + pceplib_free(PCEPLIB_INFRA, socket_comm_session); + + /* It would be nice to call destroy_socket_comm_loop() here if + * socket_comm_handle_->num_active_sessions == 0, but this function + * will usually be called from the message_sent_notifier callback, + * which gets called in the middle of the socket_comm_loop, and that + * is dangerous, so destroy_socket_comm_loop() must be called upon + * application exit. */ + + return true; +} + + +void socket_comm_session_send_message( + pcep_socket_comm_session *socket_comm_session, + const char *encoded_message, unsigned int msg_length, + bool free_after_send) +{ + if (socket_comm_session == NULL) { + pcep_log( + LOG_WARNING, + "%s: socket_comm_session_send_message NULL socket_comm_session.", + __func__); + return; + } + + pcep_socket_comm_queued_message *queued_message = pceplib_malloc( + PCEPLIB_MESSAGES, sizeof(pcep_socket_comm_queued_message)); + queued_message->encoded_message = encoded_message; + queued_message->msg_length = msg_length; + queued_message->free_after_send = free_after_send; + + pthread_mutex_lock(&(socket_comm_handle_->socket_comm_mutex)); + + /* Do not proceed if the socket_comm_session has been deleted */ + if (ordered_list_find(socket_comm_handle_->session_list, + socket_comm_session) + == NULL) { + /* Should never get here, only if the session was deleted and + * someone still tries to write on it */ + pcep_log( + LOG_WARNING, + "%s: Cannot write a message on a deleted socket comm session, discarding message", + __func__); + pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); + pceplib_free(PCEPLIB_MESSAGES, queued_message); + + return; + } + + /* Do not proceed if the socket has been closed */ + if (socket_comm_session->socket_fd < 0) { + /* Should never get here, only if the session was deleted and + * someone still tries to write on it */ + pcep_log( + LOG_WARNING, + "%s: Cannot write a message on a closed socket, discarding message", + __func__); + pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); + pceplib_free(PCEPLIB_MESSAGES, queued_message); + + return; + } + + queue_enqueue(socket_comm_session->message_queue, queued_message); + + /* Add it to the write list only if its not already there */ + if (ordered_list_find(socket_comm_handle_->write_list, + socket_comm_session) + == NULL) { + ordered_list_add_node(socket_comm_handle_->write_list, + socket_comm_session); + } + + if (socket_comm_handle_->socket_write_func != NULL) { + socket_comm_handle_->socket_write_func( + socket_comm_handle_->external_infra_data, + &socket_comm_session->external_socket_data, + socket_comm_session->socket_fd, socket_comm_handle_); + } + pthread_mutex_unlock(&(socket_comm_handle_->socket_comm_mutex)); +} diff --git a/pceplib/pcep_socket_comm.h b/pceplib/pcep_socket_comm.h new file mode 100644 index 0000000..89d2492 --- /dev/null +++ b/pceplib/pcep_socket_comm.h @@ -0,0 +1,198 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +/* + * Declaration of public API functions. + */ + +#ifndef INCLUDE_PCEPSOCKETCOMM_H_ +#define INCLUDE_PCEPSOCKETCOMM_H_ + +#include "pcep.h" +#include <arpa/inet.h> // sockaddr_in +#include <netinet/tcp.h> +#include <stdbool.h> + +#include "pcep_utils_queue.h" + +#define MAX_RECVD_MSG_SIZE 2048 + +/* + * A socket_comm_session can be initialized with 1 of 2 types of mutually + * exclusive message callbacks: + * - message_received_handler : the socket_comm library reads the message and + * calls the callback with the message_data and message_length. this callback + * should be used for smaller/simpler messages. + * - message_ready_to_read_handler : the socket_comm library will call this + * callback when a message is ready to be read on a socket_fd. this callback + * should be used if the + */ + +/* message received handler that receives the message data and message length */ +typedef void (*message_received_handler)(void *session_data, + const char *message_data, + unsigned int message_length); +/* message ready received handler that should read the message on socket_fd + * and return the number of bytes read */ +typedef int (*message_ready_to_read_handler)(void *session_data, int socket_fd); +/* callback handler called when a messages is sent */ +typedef void (*message_sent_notifier)(void *session_data, int socket_fd); +/* callback handler called when the socket is closed */ +typedef void (*connection_except_notifier)(void *session_data, int socket_fd); + +/* Function pointers when an external socket infrastructure is used */ +typedef int (*ext_socket_write)(void *infra_data, void **infra_socket_data, + int fd, void *data); +typedef int (*ext_socket_read)(void *infra_data, void **infra_socket_data, + int fd, void *data); +typedef int (*ext_socket_pthread_create_callback)( + pthread_t *pthread_id, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *data, const char *thread_name); + +typedef struct pcep_socket_comm_session_ { + message_received_handler message_handler; + message_ready_to_read_handler message_ready_to_read_handler; + message_sent_notifier message_sent_handler; + connection_except_notifier conn_except_notifier; + union src_sock_addr { + struct sockaddr_in src_sock_addr_ipv4; + struct sockaddr_in6 src_sock_addr_ipv6; + } src_sock_addr; + union dest_sock_addr { + struct sockaddr_in dest_sock_addr_ipv4; + struct sockaddr_in6 dest_sock_addr_ipv6; + } dest_sock_addr; + bool is_ipv6; + uint32_t connect_timeout_millis; + int socket_fd; + void *session_data; + queue_handle *message_queue; + char received_message[MAX_RECVD_MSG_SIZE]; + int received_bytes; + bool close_after_write; + void *external_socket_data; /* used for external socket infra */ + /* should be used with is_tcp_auth_md5 flag */ + char tcp_authentication_str[PCEP_MD5SIG_MAXKEYLEN + 1]; + + bool is_tcp_auth_md5; /* flag to distinguish between rfc 2385 (md5) and + rfc 5925 (tcp-ao) */ + +} pcep_socket_comm_session; + + +/* Need to document that when the msg_rcv_handler is called, the data needs + * to be handled in the same function call, else it may be overwritten by + * the next read from this socket */ + + +/* Initialize the Socket Comm infrastructure, with either an internal pthread + * or with an external infrastructure. + * If an internal pthread infrastructure is to be used, then it is not necessary + * to explicitly call initialize_socket_comm_loop() as it will be called + * internally when a socket comm session is initialized. */ + +/* Initialize the Socket Comm infrastructure with an internal pthread */ +bool initialize_socket_comm_loop(void); +/* Initialize the Socket Comm infrastructure with an external infrastructure. + * Notice: If the thread_create_func is set, then both the socket_read_cb + * and the socket_write_cb SHOULD be NULL. */ +bool initialize_socket_comm_external_infra( + void *external_infra_data, ext_socket_read socket_read_cb, + ext_socket_write socket_write_cb, + ext_socket_pthread_create_callback thread_create_func); + +/* The msg_rcv_handler and msg_ready_handler are mutually exclusive, and only + * one can be set (as explained above), else NULL will be returned. */ +pcep_socket_comm_session * +socket_comm_session_initialize(message_received_handler msg_rcv_handler, + message_ready_to_read_handler msg_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, + struct in_addr *dst_ip, short dst_port, + uint32_t connect_timeout_millis, + const char *tcp_authentication_str, + bool is_tcp_auth_md5, void *session_data); + +pcep_socket_comm_session *socket_comm_session_initialize_ipv6( + message_received_handler msg_rcv_handler, + message_ready_to_read_handler msg_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, struct in6_addr *dst_ip, + short dst_port, uint32_t connect_timeout_millis, + const char *tcp_authentication_str, bool is_tcp_auth_md5, + void *session_data); + +pcep_socket_comm_session *socket_comm_session_initialize_with_src( + message_received_handler msg_rcv_handler, + message_ready_to_read_handler msg_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, struct in_addr *src_ip, + short src_port, struct in_addr *dst_ip, short dst_port, + uint32_t connect_timeout_millis, const char *tcp_authentication_str, + bool is_tcp_auth_md5, void *session_data); + +pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6( + message_received_handler msg_rcv_handler, + message_ready_to_read_handler msg_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, struct in6_addr *src_ip, + short src_port, struct in6_addr *dst_ip, short dst_port, + uint32_t connect_timeout_millis, const char *tcp_authentication_str, + bool is_tcp_auth_md5, void *session_data); + +bool socket_comm_session_teardown( + pcep_socket_comm_session *socket_comm_session); + +bool socket_comm_session_connect_tcp( + pcep_socket_comm_session *socket_comm_session); + +/* Immediately close the TCP connection, irregardless if there are pending + * messages to be sent. */ +bool socket_comm_session_close_tcp( + pcep_socket_comm_session *socket_comm_session); + +/* Sets a flag to close the TCP connection either after all the pending messages + * are written, or if there are no pending messages, the next time the socket is + * checked to be writeable. */ +bool socket_comm_session_close_tcp_after_write( + pcep_socket_comm_session *socket_comm_session); + +void socket_comm_session_send_message( + pcep_socket_comm_session *socket_comm_session, + const char *encoded_message, unsigned int msg_length, + bool free_after_send); + +/* If an external Socket infra like FRR is used, then these functions will + * be called when a socket is ready to read/write in the external infra. + * Implemented in pcep_socket_comm_loop.c */ +int pceplib_external_socket_read(int fd, void *payload); +int pceplib_external_socket_write(int fd, void *payload); + +/* the socket comm loop is started internally by + * socket_comm_session_initialize() + * but needs to be explicitly stopped with this call. */ +bool destroy_socket_comm_loop(void); + +int socket_fd_node_compare(void *list_entry, void *new_entry); + +#endif /* INCLUDE_PCEPSOCKETCOMM_H_ */ diff --git a/pceplib/pcep_socket_comm_internals.h b/pceplib/pcep_socket_comm_internals.h new file mode 100644 index 0000000..4445a14 --- /dev/null +++ b/pceplib/pcep_socket_comm_internals.h @@ -0,0 +1,69 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +#ifndef SRC_PCEPSOCKETCOMMINTERNALS_H_ +#define SRC_PCEPSOCKETCOMMINTERNALS_H_ + +#include <pthread.h> +#include <stdbool.h> + +#include "pcep_utils_ordered_list.h" +#include "pcep_socket_comm.h" + + +typedef struct pcep_socket_comm_handle_ { + bool active; + pthread_t socket_comm_thread; + pthread_mutex_t socket_comm_mutex; + fd_set read_master_set; + fd_set write_master_set; + fd_set except_master_set; + /* ordered_list of socket_descriptors to read from */ + ordered_list_handle *read_list; + /* ordered_list of socket_descriptors to write to */ + ordered_list_handle *write_list; + ordered_list_handle *session_list; + int num_active_sessions; + void *external_infra_data; + ext_socket_write socket_write_func; + ext_socket_read socket_read_func; + +} pcep_socket_comm_handle; + + +typedef struct pcep_socket_comm_queued_message_ { + const char *encoded_message; + int msg_length; + bool free_after_send; + +} pcep_socket_comm_queued_message; + + +/* Functions implemented in pcep_socket_comm_loop.c */ +void *socket_comm_loop(void *data); +bool comm_session_exists(pcep_socket_comm_handle *socket_comm_handle, + pcep_socket_comm_session *socket_comm_session); +bool comm_session_exists_locking(pcep_socket_comm_handle *socket_comm_handle, + pcep_socket_comm_session *socket_comm_session); + +#endif /* SRC_PCEPSOCKETCOMMINTERNALS_H_ */ diff --git a/pceplib/pcep_socket_comm_loop.c b/pceplib/pcep_socket_comm_loop.c new file mode 100644 index 0000000..ed8037d --- /dev/null +++ b/pceplib/pcep_socket_comm_loop.c @@ -0,0 +1,497 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> +#include <assert.h> + +#include "pcep_socket_comm_internals.h" +#include "pcep_socket_comm_loop.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_ordered_list.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +void write_message(int socket_fd, const char *message, unsigned int msg_length); +unsigned int read_message(int socket_fd, char *received_message, + unsigned int max_message_size); +int build_fd_sets(pcep_socket_comm_handle *socket_comm_handle); +void handle_writes(pcep_socket_comm_handle *socket_comm_handle); +void handle_excepts(pcep_socket_comm_handle *socket_comm_handle); + +bool comm_session_exists(pcep_socket_comm_handle *socket_comm_handle, + pcep_socket_comm_session *socket_comm_session) +{ + if (socket_comm_handle == NULL) { + return false; + } + + return (ordered_list_find(socket_comm_handle->session_list, + socket_comm_session) + != NULL); +} + + +bool comm_session_exists_locking(pcep_socket_comm_handle *socket_comm_handle, + pcep_socket_comm_session *socket_comm_session) +{ + if (socket_comm_handle == NULL) { + return false; + } + + pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex)); + bool exists = + comm_session_exists(socket_comm_handle, socket_comm_session); + pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex)); + + return exists; +} + + +void write_message(int socket_fd, const char *message, unsigned int msg_length) +{ + ssize_t bytes_sent = 0; + unsigned int total_bytes_sent = 0; + + while ((uint32_t)bytes_sent < msg_length) { + bytes_sent = write(socket_fd, message + total_bytes_sent, + msg_length); + + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] socket_comm writing on socket fd [%d] msg_lenth [%u] bytes sent [%d]", + __func__, time(NULL), pthread_self(), socket_fd, + msg_length, bytes_sent); + + if (bytes_sent < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + pcep_log(LOG_WARNING, "%s: send() failure", + __func__); + + return; + } + } else { + total_bytes_sent += bytes_sent; + } + } +} + + +unsigned int read_message(int socket_fd, char *received_message, + unsigned int max_message_size) +{ + /* TODO what if bytes_read == max_message_size? there could be more to + * read */ + unsigned int bytes_read = + read(socket_fd, received_message, max_message_size); + pcep_log( + LOG_INFO, + "%s: [%ld-%ld] socket_comm read message bytes_read [%u] on socket fd [%d]", + __func__, time(NULL), pthread_self(), bytes_read, socket_fd); + + return bytes_read; +} + + +int build_fd_sets(pcep_socket_comm_handle *socket_comm_handle) +{ + int max_fd = 0; + + pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex)); + + FD_ZERO(&socket_comm_handle->except_master_set); + FD_ZERO(&socket_comm_handle->read_master_set); + ordered_list_node *node = socket_comm_handle->read_list->head; + pcep_socket_comm_session *comm_session; + while (node != NULL) { + comm_session = (pcep_socket_comm_session *)node->data; + if (comm_session->socket_fd > max_fd) { + max_fd = comm_session->socket_fd; + } else if (comm_session->socket_fd < 0) { + pcep_log(LOG_ERR, "%s: Negative fd", __func__); + assert(comm_session->socket_fd > 0); + } + + /*pcep_log(LOG_DEBUG, ld] socket_comm::build_fdSets set + ready_toRead + [%d]", __func__, time(NULL), comm_session->socket_fd);*/ + FD_SET(comm_session->socket_fd, + &socket_comm_handle->read_master_set); + FD_SET(comm_session->socket_fd, + &socket_comm_handle->except_master_set); + node = node->next_node; + } + + FD_ZERO(&socket_comm_handle->write_master_set); + node = socket_comm_handle->write_list->head; + while (node != NULL) { + comm_session = (pcep_socket_comm_session *)node->data; + if (comm_session->socket_fd > max_fd) { + max_fd = comm_session->socket_fd; + } else if (comm_session->socket_fd < 0) { + pcep_log(LOG_ERR, "%s: Negative fd", __func__); + assert(comm_session->socket_fd > 0); + } + + /*pcep_log(LOG_DEBUG, "%s: [%ld] socket_comm::build_fdSets set + ready_toWrite [%d]", __func__, time(NULL), + comm_session->socket_fd);*/ + FD_SET(comm_session->socket_fd, + &socket_comm_handle->write_master_set); + FD_SET(comm_session->socket_fd, + &socket_comm_handle->except_master_set); + node = node->next_node; + } + + pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex)); + + return max_fd + 1; +} + + +void handle_reads(pcep_socket_comm_handle *socket_comm_handle) +{ + + /* + * iterate all the socket_fd's in the read_list. it may be that not + * all of them have something to read. dont remove the socket_fd + * from the read_list since messages could come at any time. + */ + + /* Notice: Only locking the mutex when accessing the read_list, + * since the read callbacks may end up calling back into the socket + * comm module to write messages which could be a deadlock. */ + pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex)); + ordered_list_node *node = socket_comm_handle->read_list->head; + pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex)); + + while (node != NULL) { + pcep_socket_comm_session *comm_session = + (pcep_socket_comm_session *)node->data; + + pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex)); + node = node->next_node; + if (!comm_session_exists(socket_comm_handle, comm_session)) { + /* This comm_session has been deleted, move on to the + * next one */ + pthread_mutex_unlock( + &(socket_comm_handle->socket_comm_mutex)); + continue; + } + + int is_set = FD_ISSET(comm_session->socket_fd, + &(socket_comm_handle->read_master_set)); + /* Upon read failure, the comm_session might be free'd, so we + * cant store the received_bytes in the comm_session, until we + * know the read was successful. */ + int received_bytes = 0; + pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex)); + + if (is_set) { + FD_CLR(comm_session->socket_fd, + &(socket_comm_handle->read_master_set)); + + /* either read the message locally, or call the + * message_ready_handler to read it */ + if (comm_session->message_handler != NULL) { + received_bytes = read_message( + comm_session->socket_fd, + comm_session->received_message, + MAX_RECVD_MSG_SIZE); + if (received_bytes > 0) { + /* Send the received message to the + * handler */ + comm_session->received_bytes = + received_bytes; + comm_session->message_handler( + comm_session->session_data, + comm_session->received_message, + comm_session->received_bytes); + } + } else { + /* Tell the handler a message is ready to be + * read. The comm_session may be destroyed in + * this call, if + * there is an error reading or if the socket is + * closed. */ + received_bytes = + comm_session + ->message_ready_to_read_handler( + comm_session + ->session_data, + comm_session + ->socket_fd); + } + + /* handle the read results */ + if (received_bytes == 0) { + if (comm_session_exists_locking( + socket_comm_handle, comm_session)) { + comm_session->received_bytes = 0; + /* the socket was closed */ + /* TODO should we define a socket except + * enum? or will the only time we call + * this is when the socket is closed?? + */ + if (comm_session->conn_except_notifier + != NULL) { + comm_session->conn_except_notifier( + comm_session + ->session_data, + comm_session + ->socket_fd); + } + + /* stop reading from the socket if its + * closed */ + pthread_mutex_lock( + &(socket_comm_handle + ->socket_comm_mutex)); + ordered_list_remove_first_node_equals( + socket_comm_handle->read_list, + comm_session); + pthread_mutex_unlock( + &(socket_comm_handle + ->socket_comm_mutex)); + } + } else if (received_bytes < 0) { + /* TODO should we call conn_except_notifier() + * here ? */ + pcep_log( + LOG_WARNING, + "%s: Error on socket fd [%d] : errno [%d][%s]", + __func__, comm_session->socket_fd, + errno, strerror(errno)); + } else { + comm_session->received_bytes = received_bytes; + } + } + } +} + + +void handle_writes(pcep_socket_comm_handle *socket_comm_handle) +{ + pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex)); + + /* + * iterate all the socket_fd's in the write_list. it may be that not + * all of them are ready to be written to. only remove the socket_fd + * from the list if it is ready to be written to. + */ + + ordered_list_node *node = socket_comm_handle->write_list->head; + pcep_socket_comm_session *comm_session; + bool msg_written; + while (node != NULL) { + comm_session = (pcep_socket_comm_session *)node->data; + node = node->next_node; + msg_written = false; + + if (!comm_session_exists(socket_comm_handle, comm_session)) { + /* This comm_session has been deleted, move on to the + * next one */ + continue; + } + + if (FD_ISSET(comm_session->socket_fd, + &(socket_comm_handle->write_master_set))) { + /* only remove the entry from the list, if it is written + * to */ + ordered_list_remove_first_node_equals( + socket_comm_handle->write_list, comm_session); + FD_CLR(comm_session->socket_fd, + &(socket_comm_handle->write_master_set)); + + /* dequeue all the comm_session messages and send them + */ + pcep_socket_comm_queued_message *queued_message = + queue_dequeue(comm_session->message_queue); + while (queued_message != NULL) { + msg_written = true; + write_message(comm_session->socket_fd, + queued_message->encoded_message, + queued_message->msg_length); + if (queued_message->free_after_send) { + pceplib_free(PCEPLIB_MESSAGES, + (void *)queued_message + ->encoded_message); + } + pceplib_free(PCEPLIB_MESSAGES, queued_message); + queued_message = queue_dequeue( + comm_session->message_queue); + } + } + + /* check if the socket should be closed after writing */ + if (comm_session->close_after_write == true) { + if (comm_session->message_queue->num_entries == 0) { + /* TODO check to make sure modifying the + * write_list while iterating it doesn't cause + * problems. */ + pcep_log( + LOG_DEBUG, + "%s: handle_writes close() socket fd [%d]", + __func__, comm_session->socket_fd); + ordered_list_remove_first_node_equals( + socket_comm_handle->read_list, + comm_session); + ordered_list_remove_first_node_equals( + socket_comm_handle->write_list, + comm_session); + close(comm_session->socket_fd); + comm_session->socket_fd = -1; + } + } + + if (comm_session->message_sent_handler != NULL + && msg_written == true) { + /* Unlocking to allow the message_sent_handler to + * make calls like destroy_socket_comm_session */ + pthread_mutex_unlock( + &(socket_comm_handle->socket_comm_mutex)); + comm_session->message_sent_handler( + comm_session->session_data, + comm_session->socket_fd); + pthread_mutex_lock( + &(socket_comm_handle->socket_comm_mutex)); + } + } + + pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex)); +} + + +void handle_excepts(pcep_socket_comm_handle *socket_comm_handle) +{ + /* TODO finish this */ + (void)socket_comm_handle; +} + + +/* pcep_socket_comm::initialize_socket_comm_loop() will create a thread and + * invoke this method */ +void *socket_comm_loop(void *data) +{ + if (data == NULL) { + pcep_log( + LOG_WARNING, + "%s: Cannot start socket_comm_loop with NULL pcep_socketcomm_handle", + __func__); + return NULL; + } + + pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting socket_comm_loop thread", + __func__, time(NULL), pthread_self()); + + pcep_socket_comm_handle *socket_comm_handle = + (pcep_socket_comm_handle *)data; + struct timeval timer; + int max_fd; + + while (socket_comm_handle->active) { + /* check the FD's every 1/4 sec, 250 milliseconds */ + timer.tv_sec = 0; + timer.tv_usec = 250000; + max_fd = build_fd_sets(socket_comm_handle); + + if (select(max_fd, &(socket_comm_handle->read_master_set), + &(socket_comm_handle->write_master_set), + &(socket_comm_handle->except_master_set), &timer) + < 0) { + /* TODO handle the error */ + pcep_log( + LOG_WARNING, + "%s: ERROR socket_comm_loop on select : errno [%d][%s]", + __func__, errno, strerror(errno)); + } + + handle_reads(socket_comm_handle); + handle_writes(socket_comm_handle); + handle_excepts(socket_comm_handle); + } + + pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Finished socket_comm_loop thread", + __func__, time(NULL), pthread_self()); + + return NULL; +} + +int pceplib_external_socket_read(int fd, void *payload) +{ + pcep_socket_comm_handle *socket_comm_handle = + (pcep_socket_comm_handle *)payload; + if (socket_comm_handle == NULL) { + return -1; + } + + pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex)); + FD_SET(fd, &(socket_comm_handle->read_master_set)); + pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex)); + + handle_reads(socket_comm_handle); + + /* Get the socket_comm_session */ + pcep_socket_comm_session find_session = {.socket_fd = fd}; + pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex)); + ordered_list_node *node = + ordered_list_find(socket_comm_handle->read_list, &find_session); + + /* read again */ + if (node != NULL) { + socket_comm_handle->socket_read_func( + socket_comm_handle->external_infra_data, + &((pcep_socket_comm_session *)node) + ->external_socket_data, + fd, socket_comm_handle); + } + pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex)); + + return 0; +} + +int pceplib_external_socket_write(int fd, void *payload) +{ + pcep_socket_comm_handle *socket_comm_handle = + (pcep_socket_comm_handle *)payload; + if (socket_comm_handle == NULL) { + return -1; + } + + pthread_mutex_lock(&(socket_comm_handle->socket_comm_mutex)); + FD_SET(fd, &(socket_comm_handle->write_master_set)); + pthread_mutex_unlock(&(socket_comm_handle->socket_comm_mutex)); + + handle_writes(socket_comm_handle); + + /* TODO do we need to cancel this FD from writing?? */ + + return 0; +} diff --git a/pceplib/pcep_socket_comm_loop.h b/pceplib/pcep_socket_comm_loop.h new file mode 100644 index 0000000..3ca2c03 --- /dev/null +++ b/pceplib/pcep_socket_comm_loop.h @@ -0,0 +1,32 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEPSOCKETCOMMLOOP_H_ +#define PCEPSOCKETCOMMLOOP_H_ + +void handle_reads(pcep_socket_comm_handle *socket_comm_handle); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/pcep_socket_comm_mock.c b/pceplib/pcep_socket_comm_mock.c new file mode 100644 index 0000000..7a9511e --- /dev/null +++ b/pceplib/pcep_socket_comm_mock.c @@ -0,0 +1,367 @@ +/* + * 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> + * + */ + + +/* + * This module is built into a separate library, and is used by several + * other modules for unit testing, so that real sockets dont have to be + * created. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <netinet/in.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include <CUnit/CUnit.h> + +#include "pcep_socket_comm.h" +#include "pcep_socket_comm_mock.h" +#include "pcep_utils_queue.h" + +/* reset_mock_socket_comm_info() should be used before each test */ +mock_socket_comm_info mock_socket_metadata; + +void setup_mock_socket_comm_info(void) +{ + mock_socket_metadata.socket_comm_session_initialize_times_called = 0; + mock_socket_metadata.socket_comm_session_initialize_src_times_called = + 0; + mock_socket_metadata.socket_comm_session_teardown_times_called = 0; + mock_socket_metadata.socket_comm_session_connect_tcp_times_called = 0; + mock_socket_metadata.socket_comm_session_send_message_times_called = 0; + mock_socket_metadata + .socket_comm_session_close_tcp_after_write_times_called = 0; + mock_socket_metadata.socket_comm_session_close_tcp_times_called = 0; + mock_socket_metadata.destroy_socket_comm_loop_times_called = 0; + mock_socket_metadata.send_message_save_message = false; + mock_socket_metadata.sent_message_list = dll_initialize(); +} + +void teardown_mock_socket_comm_info(void) +{ + dll_destroy(mock_socket_metadata.sent_message_list); +} + +void reset_mock_socket_comm_info(void) +{ + teardown_mock_socket_comm_info(); + setup_mock_socket_comm_info(); +} + +mock_socket_comm_info *get_mock_socket_comm_info(void) +{ + return &mock_socket_metadata; +} + +void verify_socket_comm_times_called(int initialized, int teardown, int connect, + int send_message, + int close_tcp_after_write, int close_tcp, + int destroy) +{ + CU_ASSERT_EQUAL(initialized, + mock_socket_metadata + .socket_comm_session_initialize_times_called); + CU_ASSERT_EQUAL( + teardown, + mock_socket_metadata.socket_comm_session_teardown_times_called); + CU_ASSERT_EQUAL(connect, + mock_socket_metadata + .socket_comm_session_connect_tcp_times_called); + CU_ASSERT_EQUAL(send_message, + mock_socket_metadata + .socket_comm_session_send_message_times_called); + CU_ASSERT_EQUAL( + close_tcp_after_write, + mock_socket_metadata + .socket_comm_session_close_tcp_after_write_times_called); + CU_ASSERT_EQUAL(close_tcp, + mock_socket_metadata + .socket_comm_session_close_tcp_times_called); + CU_ASSERT_EQUAL( + destroy, + mock_socket_metadata.destroy_socket_comm_loop_times_called); +} + + +/* + * Mock the socket_comm functions used by session_logic for Unit Testing + */ + +bool initialize_socket_comm_external_infra( + void *external_infra_data, ext_socket_read socket_read_cb, + ext_socket_write socket_write_cb, + ext_socket_pthread_create_callback thread_create_func) +{ + (void)external_infra_data; + (void)socket_read_cb; + (void)socket_write_cb; + (void)thread_create_func; + + mock_socket_metadata + .socket_comm_initialize_external_infra_times_called++; + + return true; +} + +bool destroy_socket_comm_loop() +{ + mock_socket_metadata.destroy_socket_comm_loop_times_called++; + + return false; +} + +pcep_socket_comm_session * +socket_comm_session_initialize(message_received_handler msg_rcv_handler, + message_ready_to_read_handler msg_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, + struct in_addr *dst_ip, short dst_port, + uint32_t connect_timeout_millis, + const char *tcp_authentication_str, + bool is_tcp_auth_md5, void *session_data) +{ + (void)msg_sent_notifier; + (void)tcp_authentication_str; + (void)is_tcp_auth_md5; + + mock_socket_metadata.socket_comm_session_initialize_times_called++; + + pcep_socket_comm_session *comm_session = + malloc(sizeof(pcep_socket_comm_session)); + memset(comm_session, 0, sizeof(pcep_socket_comm_session)); + + comm_session->message_handler = msg_rcv_handler; + comm_session->message_ready_to_read_handler = msg_ready_handler; + comm_session->conn_except_notifier = notifier; + comm_session->message_queue = queue_initialize(); + comm_session->session_data = session_data; + comm_session->close_after_write = false; + comm_session->connect_timeout_millis = connect_timeout_millis; + comm_session->is_ipv6 = false; + comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family = AF_INET; + comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port = + htons(dst_port); + comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr.s_addr = + dst_ip->s_addr; + + return comm_session; +} + +pcep_socket_comm_session *socket_comm_session_initialize_ipv6( + message_received_handler msg_rcv_handler, + message_ready_to_read_handler msg_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, struct in6_addr *dst_ip, + short dst_port, uint32_t connect_timeout_millis, + const char *tcp_authentication_str, bool is_tcp_auth_md5, + void *session_data) +{ + (void)msg_sent_notifier; + (void)tcp_authentication_str; + (void)is_tcp_auth_md5; + + mock_socket_metadata.socket_comm_session_initialize_times_called++; + + pcep_socket_comm_session *comm_session = + malloc(sizeof(pcep_socket_comm_session)); + memset(comm_session, 0, sizeof(pcep_socket_comm_session)); + + comm_session->message_handler = msg_rcv_handler; + comm_session->message_ready_to_read_handler = msg_ready_handler; + comm_session->conn_except_notifier = notifier; + comm_session->message_queue = queue_initialize(); + comm_session->session_data = session_data; + comm_session->close_after_write = false; + comm_session->connect_timeout_millis = connect_timeout_millis; + comm_session->is_ipv6 = true; + comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family = AF_INET6; + comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port = + htons(dst_port); + memcpy(&comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_addr, + dst_ip, sizeof(struct in6_addr)); + + return comm_session; +} + +pcep_socket_comm_session *socket_comm_session_initialize_with_src( + message_received_handler msg_rcv_handler, + message_ready_to_read_handler msg_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, struct in_addr *src_ip, + short src_port, struct in_addr *dst_ip, short dst_port, + uint32_t connect_timeout_millis, const char *tcp_authentication_str, + bool is_tcp_auth_md5, void *session_data) +{ + (void)msg_sent_notifier; + (void)tcp_authentication_str; + (void)is_tcp_auth_md5; + + mock_socket_metadata.socket_comm_session_initialize_src_times_called++; + + pcep_socket_comm_session *comm_session = + malloc(sizeof(pcep_socket_comm_session)); + memset(comm_session, 0, sizeof(pcep_socket_comm_session)); + + comm_session->message_handler = msg_rcv_handler; + comm_session->message_ready_to_read_handler = msg_ready_handler; + comm_session->conn_except_notifier = notifier; + comm_session->message_queue = queue_initialize(); + comm_session->session_data = session_data; + comm_session->close_after_write = false; + comm_session->connect_timeout_millis = connect_timeout_millis; + comm_session->is_ipv6 = false; + comm_session->src_sock_addr.src_sock_addr_ipv4.sin_family = AF_INET; + comm_session->src_sock_addr.src_sock_addr_ipv4.sin_port = + htons(src_port); + comm_session->src_sock_addr.src_sock_addr_ipv4.sin_addr.s_addr = + ((src_ip == NULL) ? INADDR_ANY : src_ip->s_addr); + comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_family = AF_INET; + comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_port = + htons(dst_port); + comm_session->dest_sock_addr.dest_sock_addr_ipv4.sin_addr.s_addr = + dst_ip->s_addr; + + return comm_session; +} + +pcep_socket_comm_session *socket_comm_session_initialize_with_src_ipv6( + message_received_handler msg_rcv_handler, + message_ready_to_read_handler msg_ready_handler, + message_sent_notifier msg_sent_notifier, + connection_except_notifier notifier, struct in6_addr *src_ip, + short src_port, struct in6_addr *dst_ip, short dst_port, + uint32_t connect_timeout_millis, const char *tcp_authentication_str, + bool is_tcp_auth_md5, void *session_data) +{ + (void)msg_sent_notifier; + (void)tcp_authentication_str; + (void)is_tcp_auth_md5; + + mock_socket_metadata.socket_comm_session_initialize_src_times_called++; + + pcep_socket_comm_session *comm_session = + malloc(sizeof(pcep_socket_comm_session)); + memset(comm_session, 0, sizeof(pcep_socket_comm_session)); + + comm_session->message_handler = msg_rcv_handler; + comm_session->message_ready_to_read_handler = msg_ready_handler; + comm_session->conn_except_notifier = notifier; + comm_session->message_queue = queue_initialize(); + comm_session->session_data = session_data; + comm_session->close_after_write = false; + comm_session->connect_timeout_millis = connect_timeout_millis; + comm_session->is_ipv6 = true; + comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_family = AF_INET6; + comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_port = + htons(src_port); + if (src_ip == NULL) { + comm_session->src_sock_addr.src_sock_addr_ipv6.sin6_addr = + in6addr_any; + } else { + memcpy(&comm_session->src_sock_addr.src_sock_addr_ipv6 + .sin6_addr, + src_ip, sizeof(struct in6_addr)); + } + comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_family = AF_INET6; + comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_port = + htons(dst_port); + memcpy(&comm_session->dest_sock_addr.dest_sock_addr_ipv6.sin6_addr, + dst_ip, sizeof(struct in6_addr)); + + return comm_session; +} + +bool socket_comm_session_teardown(pcep_socket_comm_session *socket_comm_session) +{ + mock_socket_metadata.socket_comm_session_teardown_times_called++; + + if (socket_comm_session != NULL) { + queue_destroy(socket_comm_session->message_queue); + free(socket_comm_session); + } + + return true; +} + + +bool socket_comm_session_connect_tcp( + pcep_socket_comm_session *socket_comm_session) +{ + (void)socket_comm_session; + + mock_socket_metadata.socket_comm_session_connect_tcp_times_called++; + + return true; +} + + +void socket_comm_session_send_message( + pcep_socket_comm_session *socket_comm_session, + const char *encoded_message, unsigned int msg_length, + bool delete_after_send) +{ + (void)socket_comm_session; + (void)msg_length; + + mock_socket_metadata.socket_comm_session_send_message_times_called++; + + if (mock_socket_metadata.send_message_save_message == true) { + /* the caller/test case is responsible for freeing the message + */ + dll_append(mock_socket_metadata.sent_message_list, + (char *)encoded_message); + } else { + if (delete_after_send == true) { + free((void *)encoded_message); + } + } + + return; +} + + +bool socket_comm_session_close_tcp_after_write( + pcep_socket_comm_session *socket_comm_session) +{ + (void)socket_comm_session; + + mock_socket_metadata + .socket_comm_session_close_tcp_after_write_times_called++; + + return true; +} + + +bool socket_comm_session_close_tcp( + pcep_socket_comm_session *socket_comm_session) +{ + (void)socket_comm_session; + + mock_socket_metadata.socket_comm_session_close_tcp_times_called++; + + return true; +} diff --git a/pceplib/pcep_socket_comm_mock.h b/pceplib/pcep_socket_comm_mock.h new file mode 100644 index 0000000..ca0e38b --- /dev/null +++ b/pceplib/pcep_socket_comm_mock.h @@ -0,0 +1,67 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + +/* + * This module is built into a separate library, and is used by several + * other modules for unit testing, so that real sockets dont have to be + * created. + */ + +#ifndef PCEP_SOCKET_COMM_MOCK_SOCKET_COMM_H_ +#define PCEP_SOCKET_COMM_MOCK_SOCKET_COMM_H_ + +#include <stdbool.h> + +#include "pcep_utils_double_linked_list.h" + +typedef struct mock_socket_comm_info_ { + int socket_comm_initialize_external_infra_times_called; + int socket_comm_session_initialize_times_called; + int socket_comm_session_initialize_src_times_called; + int socket_comm_session_teardown_times_called; + int socket_comm_session_connect_tcp_times_called; + int socket_comm_session_send_message_times_called; + int socket_comm_session_close_tcp_after_write_times_called; + int socket_comm_session_close_tcp_times_called; + int destroy_socket_comm_loop_times_called; + + /* TODO later if necessary, we can add return values for + * those functions that return something */ + + /* Used to access messages sent with socket_comm_session_send_message() + */ + bool send_message_save_message; + double_linked_list *sent_message_list; + +} mock_socket_comm_info; + +void setup_mock_socket_comm_info(void); +void teardown_mock_socket_comm_info(void); +void reset_mock_socket_comm_info(void); +bool destroy_socket_comm_loop(void); + +mock_socket_comm_info *get_mock_socket_comm_info(void); +void verify_socket_comm_times_called(int initialized, int teardown, int connect, + int send_message, int close_after_write, + int close, int destroy); + +#endif /* PCEP_SOCKET_COMM_MOCK_SOCKET_COMM_H_ */ diff --git a/pceplib/pcep_timer_internals.h b/pceplib/pcep_timer_internals.h new file mode 100644 index 0000000..8221c78 --- /dev/null +++ b/pceplib/pcep_timer_internals.h @@ -0,0 +1,76 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEPTIMERINTERNALS_H_ +#define PCEPTIMERINTERNALS_H_ + +#include <stdint.h> +#include <pthread.h> + +#include "pcep_utils_ordered_list.h" + +/* Function pointer to be called when timers expire. + * Parameters: + * void *data - passed into create_timer + * int timer_id - the timer_id returned by create_timer + */ +typedef void (*timer_expire_handler)(void *, int); + +/* Function pointer when an external timer infrastructure is used */ +typedef void (*ext_timer_create)(void *infra_data, void **timer, int seconds, + void *data); +typedef void (*ext_timer_cancel)(void **timer); +typedef int (*ext_pthread_create_callback)(pthread_t *pthread_id, + const pthread_attr_t *attr, + void *(*start_routine)(void *), + void *data, const char *thread_name); + +typedef struct pcep_timer_ { + time_t expire_time; + uint16_t sleep_seconds; + int timer_id; + void *data; + void *external_timer; + +} pcep_timer; + +typedef struct pcep_timers_context_ { + ordered_list_handle *timer_list; + bool active; + timer_expire_handler expire_handler; + pthread_t event_loop_thread; + pthread_mutex_t timer_list_lock; + void *external_timer_infra_data; + ext_timer_create timer_create_func; + ext_timer_cancel timer_cancel_func; + +} pcep_timers_context; + +/* functions implemented in pcep_timers_loop.c */ +void *event_loop(void *context); + + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/pcep_timers.c b/pceplib/pcep_timers.c new file mode 100644 index 0000000..b0f3e70 --- /dev/null +++ b/pceplib/pcep_timers.c @@ -0,0 +1,489 @@ +/* + * 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> + * + */ + + +/* + * Implementation of public API timer functions. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <limits.h> +#include <pthread.h> +#include <stddef.h> +#include <stdbool.h> +#include <string.h> + +#include "pcep_timers.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" +#include "pcep_utils_ordered_list.h" + +static pcep_timers_context *timers_context_ = NULL; +static int timer_id_ = 0; + + +/* simple compare method callback used by pcep_utils_ordered_list + * for ordered list insertion. */ +int timer_list_node_compare(void *list_entry, void *new_entry) +{ + /* return: + * < 0 if new_entry < list_entry + * == 0 if new_entry == list_entry (new_entry will be inserted after + * list_entry) > 0 if new_entry > list_entry */ + return ((pcep_timer *)new_entry)->expire_time + - ((pcep_timer *)list_entry)->expire_time; +} + + +/* simple compare method callback used by pcep_utils_ordered_list + * ordered_list_remove_first_node_equals2 to remove a timer based on + * its timer_id. */ +int timer_list_node_timer_id_compare(void *list_entry, void *new_entry) +{ + return ((pcep_timer *)new_entry)->timer_id + - ((pcep_timer *)list_entry)->timer_id; +} + +/* simple compare method callback used by pcep_utils_ordered_list + * ordered_list_remove_first_node_equals2 to remove a timer based on + * its address. */ +int timer_list_node_timer_ptr_compare(void *list_entry, void *new_entry) +{ + return ((char *)new_entry - (char *)list_entry); +} + +/* internal util method */ +static pcep_timers_context *create_timers_context_(void) +{ + if (timers_context_ == NULL) { + timers_context_ = pceplib_malloc(PCEPLIB_INFRA, + sizeof(pcep_timers_context)); + memset(timers_context_, 0, sizeof(pcep_timers_context)); + timers_context_->active = false; + } + + return timers_context_; +} + + +/* Internal util function */ +static bool initialize_timers_common(timer_expire_handler expire_handler) +{ + if (expire_handler == NULL) { + /* Cannot have a NULL handler function */ + return false; + } + + timers_context_ = create_timers_context_(); + + if (timers_context_->active == true) { + /* already initialized */ + return false; + } + + timers_context_->active = true; + timers_context_->timer_list = + ordered_list_initialize(timer_list_node_compare); + timers_context_->expire_handler = expire_handler; + + if (pthread_mutex_init(&(timers_context_->timer_list_lock), NULL) + != 0) { + pcep_log( + LOG_ERR, + "%s: ERROR initializing timers, cannot initialize the mutex", + __func__); + return false; + } + + return true; +} + +bool initialize_timers(timer_expire_handler expire_handler) +{ + if (initialize_timers_common(expire_handler) == false) { + return false; + } + + if (pthread_create(&(timers_context_->event_loop_thread), NULL, + event_loop, timers_context_)) { + pcep_log( + LOG_ERR, + "%s: ERROR initializing timers, cannot initialize the thread", + __func__); + return false; + } + + return true; +} + +bool initialize_timers_external_infra( + timer_expire_handler expire_handler, void *external_timer_infra_data, + ext_timer_create timer_create_func, ext_timer_cancel timer_cancel_func, + ext_pthread_create_callback thread_create_func) +{ + if (initialize_timers_common(expire_handler) == false) { + return false; + } + + if (thread_create_func != NULL) { + if (thread_create_func(&(timers_context_->event_loop_thread), + NULL, event_loop, timers_context_, + "pceplib_timers")) { + pcep_log( + LOG_ERR, + "%s: Cannot initialize external timers thread.", + __func__); + return false; + } + } else { + if (pthread_create(&(timers_context_->event_loop_thread), NULL, + event_loop, timers_context_)) { + pcep_log( + LOG_ERR, + "%s: ERROR initializing timers, cannot initialize the thread", + __func__); + return false; + } + } + + timers_context_->external_timer_infra_data = external_timer_infra_data; + timers_context_->timer_create_func = timer_create_func; + timers_context_->timer_cancel_func = timer_cancel_func; + + return true; +} + +/* + * This function is only used to tear_down the timer data. + * Only the timer data is deleted, not the list itself, + * which is deleted by ordered_list_destroy(). + */ +void free_all_timers(pcep_timers_context *timers_context) +{ + pthread_mutex_lock(&timers_context->timer_list_lock); + + ordered_list_node *timer_node = timers_context->timer_list->head; + + while (timer_node != NULL) { + if (timer_node->data != NULL) { + pceplib_free(PCEPLIB_INFRA, timer_node->data); + } + timer_node = timer_node->next_node; + } + + pthread_mutex_unlock(&timers_context->timer_list_lock); +} + + +bool teardown_timers() +{ + if (timers_context_ == NULL) { + pcep_log( + LOG_WARNING, + "%s: Trying to teardown the timers, but they are not initialized", + __func__); + return false; + } + + if (timers_context_->active == false) { + pcep_log( + LOG_WARNING, + "%s: Trying to teardown the timers, but they are not active", + __func__); + return false; + } + + timers_context_->active = false; + if (timers_context_->event_loop_thread != 0) { + /* TODO this does not build + * Instead of calling pthread_join() which could block if the + thread + * is blocked, try joining for at most 1 second. + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += 1; + int retval = + pthread_timedjoin_np(timers_context_->event_loop_thread, NULL, + &ts); if (retval != 0) + { + pcep_log(LOG_WARNING, "%s: thread did not stop after 1 + second waiting on it.", __func__); + } + */ + pthread_join(timers_context_->event_loop_thread, NULL); + } + + free_all_timers(timers_context_); + ordered_list_destroy(timers_context_->timer_list); + + if (pthread_mutex_destroy(&(timers_context_->timer_list_lock)) != 0) { + pcep_log( + LOG_WARNING, + "%s: Trying to teardown the timers, cannot destroy the mutex", + __func__); + } + + pceplib_free(PCEPLIB_INFRA, timers_context_); + timers_context_ = NULL; + + return true; +} + + +int get_next_timer_id() +{ + if (timer_id_ == INT_MAX) { + timer_id_ = 0; + } + + return timer_id_++; +} + +int create_timer(uint16_t sleep_seconds, void *data) +{ + if (timers_context_ == NULL) { + pcep_log( + LOG_WARNING, + "%s: Trying to create a timer: the timers have not been initialized", + __func__); + return -1; + } + + pcep_timer *timer = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timer)); + memset(timer, 0, sizeof(pcep_timer)); + timer->data = data; + timer->sleep_seconds = sleep_seconds; + timer->expire_time = time(NULL) + sleep_seconds; + + pthread_mutex_lock(&timers_context_->timer_list_lock); + timer->timer_id = get_next_timer_id(); + + /* implemented in pcep_utils_ordered_list.c */ + if (ordered_list_add_node(timers_context_->timer_list, timer) == NULL) { + pceplib_free(PCEPLIB_INFRA, timer); + pthread_mutex_unlock(&timers_context_->timer_list_lock); + pcep_log( + LOG_WARNING, + "%s: Trying to create a timer, cannot add the timer to the timer list", + __func__); + + return -1; + } + + pthread_mutex_unlock(&timers_context_->timer_list_lock); + + if (timers_context_->timer_create_func) { + timers_context_->timer_create_func( + timers_context_->external_timer_infra_data, + &timer->external_timer, sleep_seconds, timer); + } + + return timer->timer_id; +} + + +bool cancel_timer(int timer_id) +{ + static pcep_timer compare_timer; + + if (timers_context_ == NULL) { + pcep_log( + LOG_WARNING, + "%s: Trying to cancel a timer: the timers have not been initialized", + __func__); + return false; + } + + pthread_mutex_lock(&timers_context_->timer_list_lock); + + compare_timer.timer_id = timer_id; + pcep_timer *timer_toRemove = ordered_list_remove_first_node_equals2( + timers_context_->timer_list, &compare_timer, + timer_list_node_timer_id_compare); + if (timer_toRemove == NULL) { + pthread_mutex_unlock(&timers_context_->timer_list_lock); + pcep_log( + LOG_WARNING, + "%s: Trying to cancel a timer [%d] that does not exist", + __func__, timer_id); + return false; + } + + pthread_mutex_unlock(&timers_context_->timer_list_lock); + + if (timers_context_->timer_cancel_func) { + timers_context_->timer_cancel_func( + &timer_toRemove->external_timer); + } + + pceplib_free(PCEPLIB_INFRA, timer_toRemove); + + return true; +} + + +bool reset_timer(int timer_id) +{ + static pcep_timer compare_timer; + + if (timers_context_ == NULL) { + pcep_log( + LOG_WARNING, + "%s: Trying to reset a timer: the timers have not been initialized", + __func__); + + return false; + } + + pthread_mutex_lock(&timers_context_->timer_list_lock); + + compare_timer.timer_id = timer_id; + ordered_list_node *timer_to_reset_node = + ordered_list_find2(timers_context_->timer_list, &compare_timer, + timer_list_node_timer_id_compare); + if (timer_to_reset_node == NULL) { + pthread_mutex_unlock(&timers_context_->timer_list_lock); + pcep_log(LOG_WARNING, + "%s: Trying to reset a timer node that does not exist", + __func__); + + return false; + } + + pcep_timer *timer_to_reset = timer_to_reset_node->data; + if (timer_to_reset == NULL) { + pthread_mutex_unlock(&timers_context_->timer_list_lock); + pcep_log(LOG_WARNING, + "%s: Trying to reset a timer that does not exist", + __func__); + + return false; + } + + /* First check if the timer to reset already has the same expire time, + * which means multiple reset_timer() calls were made on the same timer + * in the same second */ + time_t expire_time = time(NULL) + timer_to_reset->sleep_seconds; + if (timer_to_reset->expire_time == expire_time) { + pthread_mutex_unlock(&timers_context_->timer_list_lock); + return true; + } + + ordered_list_remove_node2(timers_context_->timer_list, + timer_to_reset_node); + + timer_to_reset->expire_time = expire_time; + if (ordered_list_add_node(timers_context_->timer_list, timer_to_reset) + == NULL) { + pceplib_free(PCEPLIB_INFRA, timer_to_reset); + pthread_mutex_unlock(&timers_context_->timer_list_lock); + pcep_log( + LOG_WARNING, + "%s: Trying to reset a timer, cannot add the timer to the timer list", + __func__); + + return false; + } + + pthread_mutex_unlock(&timers_context_->timer_list_lock); + + if (timers_context_->timer_cancel_func) { + /* Keeping this log for now, since in older versions of FRR the + * timer cancellation was blocking. This allows us to see how + * long the it takes.*/ + pcep_log(LOG_DEBUG, "%s: Resetting timer [%d] with callback", + __func__, timer_to_reset->timer_id); + timers_context_->timer_cancel_func( + &timer_to_reset->external_timer); + timer_to_reset->external_timer = NULL; + } + + if (timers_context_->timer_create_func) { + timers_context_->timer_create_func( + timers_context_->external_timer_infra_data, + &timer_to_reset->external_timer, + timer_to_reset->sleep_seconds, timer_to_reset); + /* Keeping this log for now, since in older versions of FRR the + * timer cancellation was blocking. This allows us to see how + * long the it takes.*/ + pcep_log(LOG_DEBUG, "%s: Reset timer [%d] with callback", + __func__, timer_to_reset->timer_id); + } + + return true; +} + + +void pceplib_external_timer_expire_handler(void *data) +{ + if (timers_context_ == NULL) { + pcep_log( + LOG_WARNING, + "%s: External timer expired but timers_context is not initialized", + __func__); + return; + } + + if (timers_context_->expire_handler == NULL) { + pcep_log( + LOG_WARNING, + "%s: External timer expired but expire_handler is not initialized", + __func__); + return; + } + + if (data == NULL) { + pcep_log(LOG_WARNING, + "%s: External timer expired with NULL data", __func__); + return; + } + + pcep_timer *timer = (pcep_timer *)data; + + pthread_mutex_lock(&timers_context_->timer_list_lock); + ordered_list_node *timer_node = + ordered_list_find2(timers_context_->timer_list, timer, + timer_list_node_timer_ptr_compare); + + /* Remove timer from list */ + if (timer_node) + ordered_list_remove_node2(timers_context_->timer_list, + timer_node); + + pthread_mutex_unlock(&timers_context_->timer_list_lock); + + /* Cannot continue if the timer does not exist */ + if (timer_node == NULL) { + pcep_log( + LOG_WARNING, + "%s: pceplib_external_timer_expire_handler timer [%p] id [%d] does not exist", + __func__, timer, timer->timer_id); + return; + } + + timers_context_->expire_handler(timer->data, timer->timer_id); + + pceplib_free(PCEPLIB_INFRA, timer); +} diff --git a/pceplib/pcep_timers.h b/pceplib/pcep_timers.h new file mode 100644 index 0000000..b2cc6ec --- /dev/null +++ b/pceplib/pcep_timers.h @@ -0,0 +1,92 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +/* + * Public API for pcep_timers + */ + +#ifndef PCEPTIMERS_H_ +#define PCEPTIMERS_H_ + +#include <pthread.h> +#include <stdbool.h> + +#include "pcep_timer_internals.h" + +#define TIMER_ID_NOT_SET -1 + +/* + * Initialize the timers module. + * The timer_expire_handler function pointer will be called each time a timer + * expires. Return true for successful initialization, false otherwise. + */ +bool initialize_timers(timer_expire_handler expire_handler); + +/* + * Initialize the timers module with an external back-end infrastructure, like + * FRR. + */ +bool initialize_timers_external_infra( + timer_expire_handler expire_handler, void *external_timer_infra_data, + ext_timer_create timer_create_func, ext_timer_cancel timer_cancel_func, + ext_pthread_create_callback thread_create_func); + +/* + * Teardown the timers module. + */ +bool teardown_timers(void); + +/* + * Create a new timer for "sleep_seconds" seconds. + * If the timer expires before being cancelled, the timer_expire_handler + * passed to initialize_timers() will be called with the pointer to "data". + * Returns a timer_id <= 0 that can be used to cancel_timer. + * Returns < 0 on error. + */ +int create_timer(uint16_t sleep_seconds, void *data); + +/* + * Cancel a timer created with create_timer(). + * Returns true if the timer was found and cancelled, false otherwise. + */ +bool cancel_timer(int timer_id); + +/* + * Reset an previously created timer, maintaining the same timer_id. + * Returns true if the timer was found and reset, false otherwise. + */ +bool reset_timer(int timer_id); + +/* + * If an external timer infra like FRR is used, then this function + * will be called when the timers expire in the external infra. + */ +void pceplib_external_timer_expire_handler(void *data); + +int timer_list_node_compare(void *list_entry, void *new_entry); +int timer_list_node_timer_id_compare(void *list_entry, void *new_entry); +int timer_list_node_timer_ptr_compare(void *list_entry, void *new_entry); +void free_all_timers(pcep_timers_context *timers_context); +int get_next_timer_id(void); + +#endif /* PCEPTIMERS_H_ */ diff --git a/pceplib/pcep_timers_event_loop.c b/pceplib/pcep_timers_event_loop.c new file mode 100644 index 0000000..8984496 --- /dev/null +++ b/pceplib/pcep_timers_event_loop.c @@ -0,0 +1,110 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <stddef.h> +#include <stdbool.h> +#include <stdio.h> +#include <sys/select.h> + +#include "pcep_timers_event_loop.h" +#include "pcep_timer_internals.h" +#include "pcep_utils_ordered_list.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +/* For each expired timer: remove the timer from the list, call the + * expire_handler, and free the timer. */ +void walk_and_process_timers(pcep_timers_context *timers_context) +{ + pthread_mutex_lock(&timers_context->timer_list_lock); + + bool keep_walking = true; + ordered_list_node *timer_node = timers_context->timer_list->head; + time_t now = time(NULL); + pcep_timer *timer_data; + + /* the timers are sorted by expire_time, so we will only + * remove the top node each time through the loop */ + while (timer_node != NULL && keep_walking) { + timer_data = (pcep_timer *)timer_node->data; + if (timer_data->expire_time <= now) { + timer_node = timer_node->next_node; + ordered_list_remove_first_node( + timers_context->timer_list); + /* call the timer expired handler */ + timers_context->expire_handler(timer_data->data, + timer_data->timer_id); + pceplib_free(PCEPLIB_INFRA, timer_data); + } else { + keep_walking = false; + } + } + + pthread_mutex_unlock(&timers_context->timer_list_lock); +} + + +/* pcep_timers::initialize() will create a thread and invoke this method */ +void *event_loop(void *context) +{ + if (context == NULL) { + pcep_log( + LOG_WARNING, + "%s: pcep_timers_event_loop cannot start event_loop with NULL data", + __func__); + return NULL; + } + + pcep_log(LOG_NOTICE, "%s: [%ld-%ld] Starting timers_event_loop thread", + __func__, time(NULL), pthread_self()); + + pcep_timers_context *timers_context = (pcep_timers_context *)context; + struct timeval timer; + int retval; + + while (timers_context->active) { + /* check the timers every half second */ + timer.tv_sec = 0; + timer.tv_usec = 500000; + + do { + /* if the select() call gets interrupted, select() will + * set the remaining time in timer, so we need to call + * it again. + */ + retval = select(0, NULL, NULL, NULL, &timer); + } while (retval != 0 && errno == EINTR); + + walk_and_process_timers(timers_context); + } + + pcep_log(LOG_WARNING, "%s: [%ld-%ld] Finished timers_event_loop thread", + __func__, time(NULL), pthread_self()); + + return NULL; +} diff --git a/pceplib/pcep_timers_event_loop.h b/pceplib/pcep_timers_event_loop.h new file mode 100644 index 0000000..c4a7264 --- /dev/null +++ b/pceplib/pcep_timers_event_loop.h @@ -0,0 +1,34 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_TIMERS_EVENT_LOOP_H_ +#define PCEP_TIMERS_EVENT_LOOP_H_ + +#include "pcep_timer_internals.h" + +void walk_and_process_timers(pcep_timers_context *timers_context); + +#endif diff --git a/pceplib/pcep_utils_counters.c b/pceplib/pcep_utils_counters.c new file mode 100644 index 0000000..d8078f6 --- /dev/null +++ b/pceplib/pcep_utils_counters.c @@ -0,0 +1,475 @@ +/* + * 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> + * + */ + + +/* + * Implementation of PCEP Counters. + */ + +#include <zebra.h> + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "pcep_utils_counters.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +struct counters_group *create_counters_group(const char *group_name, + uint16_t max_subgroups) +{ + if (group_name == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot create counters group: group_name is NULL.", + __func__); + return NULL; + } + + if (max_subgroups > MAX_COUNTER_GROUPS) { + pcep_log( + LOG_INFO, + "%s: Cannot create counters group: max_subgroups [%d] is larger than max the [%d].", + __func__, max_subgroups, MAX_COUNTER_GROUPS); + return NULL; + } + + struct counters_group *group = + pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counters_group)); + memset(group, 0, sizeof(struct counters_group)); + group->subgroups = + pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counters_subgroup *) + * (max_subgroups + 1)); + memset(group->subgroups, 0, + sizeof(struct counters_subgroup *) * (max_subgroups + 1)); + + strlcpy(group->counters_group_name, group_name, + sizeof(group->counters_group_name)); + group->max_subgroups = max_subgroups; + group->start_time = time(NULL); + + return group; +} + +struct counters_subgroup *create_counters_subgroup(const char *subgroup_name, + uint16_t subgroup_id, + uint16_t max_counters) +{ + if (subgroup_name == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot create counters subgroup: subgroup_name is NULL.", + __func__); + return NULL; + } + + if (max_counters > MAX_COUNTERS) { + pcep_log( + LOG_INFO, + "%s: Cannot create counters subgroup: max_counters [%d] is larger than the max [%d].", + __func__, max_counters, MAX_COUNTERS); + return NULL; + } + + if (subgroup_id > MAX_COUNTER_GROUPS) { + pcep_log( + LOG_INFO, + "%s: Cannot create counters subgroup: subgroup_id [%d] is larger than max the [%d].", + __func__, subgroup_id, MAX_COUNTER_GROUPS); + return NULL; + } + + struct counters_subgroup *subgroup = + pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counters_subgroup)); + memset(subgroup, 0, sizeof(struct counters_subgroup)); + subgroup->counters = pceplib_malloc( + PCEPLIB_INFRA, sizeof(struct counter *) * (max_counters + 1)); + memset(subgroup->counters, 0, + sizeof(struct counter *) * (max_counters + 1)); + + strlcpy(subgroup->counters_subgroup_name, subgroup_name, + sizeof(subgroup->counters_subgroup_name)); + subgroup->subgroup_id = subgroup_id; + subgroup->max_counters = max_counters; + + return subgroup; +} + +struct counters_subgroup * +clone_counters_subgroup(struct counters_subgroup *subgroup, + const char *subgroup_name, uint16_t subgroup_id) +{ + if (subgroup == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot clone counters subgroup: input counters_subgroup is NULL.", + __func__); + return NULL; + } + + if (subgroup_name == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot clone counters subgroup: subgroup_name is NULL.", + __func__); + return NULL; + } + + if (subgroup_id > MAX_COUNTER_GROUPS) { + pcep_log( + LOG_INFO, + "%s: Cannot clone counters subgroup: subgroup_id [%d] is larger than max the [%d].", + __func__, subgroup_id, MAX_COUNTER_GROUPS); + return NULL; + } + + struct counters_subgroup *cloned_subgroup = create_counters_subgroup( + subgroup_name, subgroup_id, subgroup->max_counters); + int i = 0; + for (; i <= subgroup->max_counters; i++) { + struct counter *counter = subgroup->counters[i]; + if (counter != NULL) { + create_subgroup_counter(cloned_subgroup, + counter->counter_id, + counter->counter_name); + } + } + + return cloned_subgroup; +} + +bool add_counters_subgroup(struct counters_group *group, + struct counters_subgroup *subgroup) +{ + if (group == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot add counters subgroup: counters_group is NULL.", + __func__); + return false; + } + + if (subgroup == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot add counters subgroup: counters_subgroup is NULL.", + __func__); + return false; + } + + if (subgroup->subgroup_id >= group->max_subgroups) { + pcep_log( + LOG_INFO, + "%s: Cannot add counters subgroup: counters_subgroup id [%d] is larger than the group max_subgroups [%d].", + __func__, subgroup->subgroup_id, group->max_subgroups); + return false; + } + + group->num_subgroups++; + group->subgroups[subgroup->subgroup_id] = subgroup; + + return true; +} + +bool create_subgroup_counter(struct counters_subgroup *subgroup, + uint32_t counter_id, const char *counter_name) +{ + if (subgroup == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot create subgroup counter: counters_subgroup is NULL.", + __func__); + return false; + } + + if (counter_id >= subgroup->max_counters) { + pcep_log( + LOG_INFO, + "%s: Cannot create subgroup counter: counter_id [%d] is larger than the subgroup max_counters [%d].", + __func__, counter_id, subgroup->max_counters); + return false; + } + + if (counter_name == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot create subgroup counter: counter_name is NULL.", + __func__); + return NULL; + } + + struct counter *counter = + pceplib_malloc(PCEPLIB_INFRA, sizeof(struct counter)); + memset(counter, 0, sizeof(struct counter)); + counter->counter_id = counter_id; + strlcpy(counter->counter_name, counter_name, + sizeof(counter->counter_name)); + + subgroup->num_counters++; + subgroup->counters[counter->counter_id] = counter; + + return true; +} + +bool delete_counters_group(struct counters_group *group) +{ + if (group == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot delete group counters: counters_group is NULL.", + __func__); + return false; + } + + int i = 0; + for (; i <= group->max_subgroups; i++) { + struct counters_subgroup *subgroup = group->subgroups[i]; + if (subgroup != NULL) { + delete_counters_subgroup(subgroup); + } + } + + pceplib_free(PCEPLIB_INFRA, group->subgroups); + pceplib_free(PCEPLIB_INFRA, group); + + return true; +} + +bool delete_counters_subgroup(struct counters_subgroup *subgroup) +{ + if (subgroup == NULL || subgroup->counters == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot delete subgroup counters: counters_subgroup is NULL.", + __func__); + return false; + } + + int i = 0; + for (; i <= subgroup->max_counters; i++) { + struct counter *counter = subgroup->counters[i]; + if (counter != NULL) { + pceplib_free(PCEPLIB_INFRA, counter); + } + } + + pceplib_free(PCEPLIB_INFRA, subgroup->counters); + pceplib_free(PCEPLIB_INFRA, subgroup); + + return true; +} + +bool reset_group_counters(struct counters_group *group) +{ + if (group == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot reset group counters: counters_group is NULL.", + __func__); + return false; + } + + int i = 0; + for (; i <= group->max_subgroups; i++) { + struct counters_subgroup *subgroup = group->subgroups[i]; + if (subgroup != NULL) { + reset_subgroup_counters(subgroup); + } + } + + group->start_time = time(NULL); + + return true; +} + +bool reset_subgroup_counters(struct counters_subgroup *subgroup) +{ + if (subgroup == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot reset subgroup counters: counters_subgroup is NULL.", + __func__); + return false; + } + + int i = 0; + for (; i <= subgroup->max_counters; i++) { + struct counter *counter = subgroup->counters[i]; + if (counter != NULL) { + counter->counter_value = 0; + } + } + + return true; +} + +bool increment_counter(struct counters_group *group, uint16_t subgroup_id, + uint16_t counter_id) +{ + if (group == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot increment counter: counters_group is NULL.", + __func__); + return false; + } + + if (subgroup_id >= group->max_subgroups) { + pcep_log( + LOG_DEBUG, + "%s: Cannot increment counter: subgroup_id [%d] is larger than the group max_subgroups [%d].", + __func__, subgroup_id, group->max_subgroups); + return false; + } + + struct counters_subgroup *subgroup = group->subgroups[subgroup_id]; + if (subgroup == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot increment counter: counters_subgroup in counters_group is NULL.", + __func__); + return false; + } + + return increment_subgroup_counter(subgroup, counter_id); +} + +bool increment_subgroup_counter(struct counters_subgroup *subgroup, + uint16_t counter_id) +{ + if (subgroup == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot increment counter: counters_subgroup is NULL.", + __func__); + return false; + } + + if (counter_id >= subgroup->max_counters) { + pcep_log( + LOG_DEBUG, + "%s: Cannot increment counter: counter_id [%d] is larger than the subgroup max_counters [%d].", + __func__, counter_id, subgroup->max_counters); + return false; + } + + if (subgroup->counters[counter_id] == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot increment counter: No counter exists for counter_id [%d].", + __func__, counter_id); + return false; + } + + subgroup->counters[counter_id]->counter_value++; + + return true; +} + +bool dump_counters_group_to_log(struct counters_group *group) +{ + if (group == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot dump group counters to log: counters_group is NULL.", + __func__); + return false; + } + + time_t now = time(NULL); + pcep_log( + LOG_INFO, + "%s: PCEP Counters group:\n %s \n Sub-Groups [%d] \n Active for [%d seconds]", + __func__, group->counters_group_name, group->num_subgroups, + (now - group->start_time)); + + int i = 0; + for (; i <= group->max_subgroups; i++) { + struct counters_subgroup *subgroup = group->subgroups[i]; + if (subgroup != NULL) { + dump_counters_subgroup_to_log(subgroup); + } + } + + return true; +} + +bool dump_counters_subgroup_to_log(struct counters_subgroup *subgroup) +{ + if (subgroup == NULL) { + pcep_log( + LOG_INFO, + "%s: Cannot dump subgroup counters to log: counters_subgroup is NULL.", + __func__); + return false; + } + + pcep_log(LOG_INFO, + "%s: \tPCEP Counters sub-group [%s] with [%d] counters", + __func__, subgroup->counters_subgroup_name, + subgroup->num_counters); + + int i = 0; + for (; i <= subgroup->max_counters; i++) { + struct counter *counter = subgroup->counters[i]; + if (counter != NULL) { + pcep_log(LOG_INFO, "%s: \t\t%s %d", __func__, + counter->counter_name, counter->counter_value); + } + } + + return true; +} + +struct counters_subgroup *find_subgroup(const struct counters_group *group, + uint16_t subgroup_id) +{ + int i = 0; + for (; i <= group->max_subgroups; i++) { + struct counters_subgroup *subgroup = group->subgroups[i]; + if (subgroup != NULL) { + if (subgroup->subgroup_id == subgroup_id) { + return subgroup; + } + } + } + + return NULL; +} + +uint32_t subgroup_counters_total(struct counters_subgroup *subgroup) +{ + if (subgroup == NULL) { + return 0; + } + uint32_t counter_total = 0; + int i = 0; + for (; i <= subgroup->max_counters; i++) { + struct counter *counter = subgroup->counters[i]; + if (counter != NULL) { + counter_total += counter->counter_value; + } + } + + return counter_total; +} diff --git a/pceplib/pcep_utils_counters.h b/pceplib/pcep_utils_counters.h new file mode 100644 index 0000000..b10ced7 --- /dev/null +++ b/pceplib/pcep_utils_counters.h @@ -0,0 +1,233 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +/* + * Definitions of PCEP Counters. + */ + +#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_COUNTERS_H_ +#define PCEP_UTILS_INCLUDE_PCEP_UTILS_COUNTERS_H_ + +#include <stdbool.h> +#include <stdint.h> +#include <time.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Example Counter group with sub-groups and counters + * + * pcep_counters { + * counters_group_rx { + * message_open; + * message_keepalive; + * message_pcreq; + * } + * counters_group_tx { + * message_open; + * message_keepalive; + * message_pcreq; + * } + * counters_group_events { + * pcc_connect; + * pce_connect; + * pcc_disconnect; + * pce_disconnect; + * } + * } + * + * To create the above structure of groups, sub-groups, and counters, do the + * following: + * + * struct counters_subgroup *rx_subgroup = create_counters_subgroup("rx + * counters", 3); struct counters_subgroup *tx_subgroup = + * create_counters_subgroup("tx counters", 3); struct counters_subgroup + * *events_subgroup = create_counters_subgroup("events counters", 4); + * + * Use message_id: PCEP_TYPE_OPEN=1 + * create_subgroup_counter(rx_subgroup, 1, "Message Open"); + * create_subgroup_counter(rx_subgroup, 2, "Message KeepAlive"); + * create_subgroup_counter(rx_subgroup, 3, "Message PcReq"); + * + * create_subgroup_counter(tx_subgroup, 1, "Message Open"); + * create_subgroup_counter(tx_subgroup, 2, "Message KeepAlive"); + * create_subgroup_counter(tx_subgroup, 3, "Message PcReq"); + * + * create_subgroup_counter(events_subgroup, 1, "PCC Connect"); + * create_subgroup_counter(events_subgroup, 2, "PCE Connect"); + * create_subgroup_counter(events_subgroup, 3, "PCC Disconnect"); + * create_subgroup_counter(events_subgroup, 4, "PCE Disconnect"); + * + * struct counters_group *cntrs_group = create_counters_group("PCEP Counters", + * 3); add_counters_subgroup(cntrs_group, rx_subgroup); + * add_counters_subgroup(cntrs_group, tx_subgroup); + * add_counters_subgroup(cntrs_group, events_subgroup); + */ + +#define MAX_COUNTER_STR_LENGTH 128 +#define MAX_COUNTER_GROUPS 500 +#define MAX_COUNTERS 500 + +struct counter { + uint16_t counter_id; + char counter_name[MAX_COUNTER_STR_LENGTH]; + uint32_t counter_value; +}; + +struct counters_subgroup { + char counters_subgroup_name[MAX_COUNTER_STR_LENGTH]; + uint16_t subgroup_id; + uint16_t num_counters; + uint16_t max_counters; + /* Array of (struct counter *) allocated when the subgroup is created. + * The array is indexed by the struct counter->counter_id */ + struct counter **counters; +}; + +struct counters_group { + char counters_group_name[MAX_COUNTER_STR_LENGTH]; + uint16_t num_subgroups; + uint16_t max_subgroups; + time_t start_time; + /* Array of (struct counters_subgroup *) allocated when the group is + * created. The subgroup is indexed by the (struct counters_subgroup + * *)->subgroup_id */ + struct counters_subgroup **subgroups; +}; + +/* + * Create a counters group with the given group_name and number of subgroups. + * Subgroup_ids are 0-based, so take that into account when setting + * max_subgroups. Return true on success or false if group_name is NULL or + * max_subgroups >= MAX_COUNTER_GROUPS. + */ +struct counters_group *create_counters_group(const char *group_name, + uint16_t max_subgroups); + +/* + * Create a counters subgroup with the given subgroup_name, subgroup_id and + * number of counters. The subgroup_id is 0-based. counter_ids are 0-based, so + * take that into account when setting max_counters. Return true on success or + * false if subgroup_name is NULL, subgroup_id >= MAX_COUNTER_GROUPS, or + * max_counters >= MAX_COUNTERS. + */ +struct counters_subgroup *create_counters_subgroup(const char *subgroup_name, + uint16_t subgroup_id, + uint16_t max_counters); + +/* + * Add a counter_subgroup to a counter_group. + * Return true on success or false if group is NULL or if subgroup is NULL. + */ +bool add_counters_subgroup(struct counters_group *group, + struct counters_subgroup *subgroup); + +/* + * Clone a subgroup and set a new name and subgroup_id for the new subgroup. + * This is useful for RX and TX counters: just create the RX counters and clone + * it for the TX counters. + */ +struct counters_subgroup * +clone_counters_subgroup(struct counters_subgroup *subgroup, + const char *subgroup_name, uint16_t subgroup_id); + +/* + * Create a counter in a subgroup with the given counter_id and counter_name. + * The counter_id is 0-based. + * Return true on success or false if subgroup is NULL, counter_id >= + * MAX_COUNTERS, or if counter_name is NULL. + */ +bool create_subgroup_counter(struct counters_subgroup *subgroup, + uint32_t counter_id, const char *counter_name); + +/* + * Delete the counters_group and recursively delete all subgroups and their + * counters. Return true on success or false if group is NULL. + */ +bool delete_counters_group(struct counters_group *group); + +/* + * Delete the counters_subgroup and all its counters counters. + * Return true on success or false if subgroup is NULL. + */ +bool delete_counters_subgroup(struct counters_subgroup *subgroup); + +/* + * Reset all the counters in all sub-groups contained in this group. + * Return true on success or false if group is NULL. + */ +bool reset_group_counters(struct counters_group *group); + +/* + * Reset all the counters in this subgroup. + * Return true on success or false if subgroup is NULL. + */ +bool reset_subgroup_counters(struct counters_subgroup *subgroup); + +/* + * Increment a counter given a counter_group, subgroup_id, and counter_id. + * Return true on success or false if group is NULL, subgroup_id >= + * MAX_COUNTER_GROUPS, or counter_id >= MAX_COUNTERS. + */ +bool increment_counter(struct counters_group *group, uint16_t subgroup_id, + uint16_t counter_id); + +/* + * Increment a counter given the counter_subgroup and counter_id. + * Return true on success or false if subgroup is NULL or counter_id >= + * MAX_COUNTERS. + */ +bool increment_subgroup_counter(struct counters_subgroup *subgroup, + uint16_t counter_id); + +/* + * Dump the counter_group info and all its counter_subgroups. + * Return true on success or false if group is NULL. + */ +bool dump_counters_group_to_log(struct counters_group *group); + +/* + * Dump all the counters in a counter_subgroup. + * Return true on success or false if subgroup is NULL. + */ +bool dump_counters_subgroup_to_log(struct counters_subgroup *subgroup); + +/* + * Search for a counters_subgroup by subgroup_id in a counters_group + * and return it, if found, else return NULL. + */ +struct counters_subgroup *find_subgroup(const struct counters_group *group, + uint16_t subgroup_id); + +/* + * Given a counters_subgroup, return the sum of all the counters. + */ +uint32_t subgroup_counters_total(struct counters_subgroup *subgroup); + +#ifdef __cplusplus +} +#endif + +#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_COUNTERS_H_ */ diff --git a/pceplib/pcep_utils_double_linked_list.c b/pceplib/pcep_utils_double_linked_list.c new file mode 100644 index 0000000..696e466 --- /dev/null +++ b/pceplib/pcep_utils_double_linked_list.c @@ -0,0 +1,266 @@ +/* + * 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> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stddef.h> +#include <string.h> + +#include "pcep_utils_double_linked_list.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +double_linked_list *dll_initialize() +{ + double_linked_list *handle = + pceplib_malloc(PCEPLIB_INFRA, sizeof(double_linked_list)); + if (handle != NULL) { + memset(handle, 0, sizeof(double_linked_list)); + handle->num_entries = 0; + handle->head = NULL; + handle->tail = NULL; + } else { + pcep_log(LOG_WARNING, + "%s: dll_initialize cannot allocate memory for handle", + __func__); + return NULL; + } + + return handle; +} + + +void dll_destroy(double_linked_list *handle) +{ + if (handle == NULL) { + pcep_log(LOG_WARNING, + "%s: dll_destroy cannot destroy NULL handle", + __func__); + return; + } + + double_linked_list_node *node = handle->head; + while (node != NULL) { + double_linked_list_node *node_to_delete = node; + node = node->next_node; + pceplib_free(PCEPLIB_INFRA, node_to_delete); + } + + pceplib_free(PCEPLIB_INFRA, handle); +} + + +void dll_destroy_with_data_memtype(double_linked_list *handle, + void *data_memory_type) +{ + if (handle == NULL) { + pcep_log(LOG_WARNING, + "%s: dll_destroy_with_data cannot destroy NULL handle", + __func__); + return; + } + + double_linked_list_node *node = handle->head; + while (node != NULL) { + double_linked_list_node *node_to_delete = node; + pceplib_free(data_memory_type, node->data); + node = node->next_node; + pceplib_free(PCEPLIB_INFRA, node_to_delete); + } + + pceplib_free(PCEPLIB_INFRA, handle); +} + + +void dll_destroy_with_data(double_linked_list *handle) +{ + /* Default to destroying the data with the INFRA mem type */ + dll_destroy_with_data_memtype(handle, PCEPLIB_INFRA); +} + + +/* Creates a node and adds it as the first item in the list */ +double_linked_list_node *dll_prepend(double_linked_list *handle, void *data) +{ + if (handle == NULL) { + pcep_log(LOG_WARNING, "%s: dll_prepend_data NULL handle", + __func__); + return NULL; + } + + /* Create the new node */ + double_linked_list_node *new_node = + pceplib_malloc(PCEPLIB_INFRA, sizeof(double_linked_list_node)); + memset(new_node, 0, sizeof(double_linked_list_node)); + new_node->data = data; + + if (handle->head == NULL) { + handle->head = new_node; + handle->tail = new_node; + } else { + new_node->next_node = handle->head; + handle->head->prev_node = new_node; + handle->head = new_node; + } + + (handle->num_entries)++; + + return new_node; +} + + +/* Creates a node and adds it as the last item in the list */ +double_linked_list_node *dll_append(double_linked_list *handle, void *data) +{ + if (handle == NULL) { + pcep_log(LOG_WARNING, "%s: dll_append_data NULL handle", + __func__); + return NULL; + } + + /* Create the new node */ + double_linked_list_node *new_node = + pceplib_malloc(PCEPLIB_INFRA, sizeof(double_linked_list_node)); + memset(new_node, 0, sizeof(double_linked_list_node)); + new_node->data = data; + + if (handle->head == NULL) { + handle->head = new_node; + handle->tail = new_node; + } else { + new_node->prev_node = handle->tail; + handle->tail->next_node = new_node; + handle->tail = new_node; + } + + (handle->num_entries)++; + + return new_node; +} + + +/* Delete the first node in the list, and return the data */ +void *dll_delete_first_node(double_linked_list *handle) +{ + if (handle == NULL) { + pcep_log(LOG_WARNING, "%s: dll_delete_first_node NULL handle", + __func__); + return NULL; + } + + if (handle->head == NULL) { + return NULL; + } + + double_linked_list_node *delete_node = handle->head; + void *data = delete_node->data; + + if (delete_node->next_node == NULL) { + /* Its the last node in the list */ + handle->head = NULL; + handle->tail = NULL; + } else { + handle->head = delete_node->next_node; + handle->head->prev_node = NULL; + } + + pceplib_free(PCEPLIB_INFRA, delete_node); + (handle->num_entries)--; + + return data; +} + + +/* Delete the last node in the list, and return the data */ +void *dll_delete_last_node(double_linked_list *handle) +{ + if (handle == NULL) { + pcep_log(LOG_WARNING, "%s: dll_delete_last_node NULL handle", + __func__); + return NULL; + } + + if (handle->head == NULL) { + return NULL; + } + + double_linked_list_node *delete_node = handle->tail; + void *data = delete_node->data; + + if (delete_node->prev_node == NULL) { + /* Its the last node in the list */ + handle->head = NULL; + handle->tail = NULL; + } else { + handle->tail = delete_node->prev_node; + handle->tail->next_node = NULL; + } + + pceplib_free(PCEPLIB_INFRA, delete_node); + (handle->num_entries)--; + + return data; +} + + +/* Delete the designated node in the list, and return the data */ +void *dll_delete_node(double_linked_list *handle, double_linked_list_node *node) +{ + if (handle == NULL) { + pcep_log(LOG_WARNING, "%s: dll_delete_node NULL handle", + __func__); + return NULL; + } + + if (node == NULL) { + return NULL; + } + + if (handle->head == NULL) { + return NULL; + } + + void *data = node->data; + + if (handle->head == handle->tail) { + /* Its the last node in the list */ + handle->head = NULL; + handle->tail = NULL; + } else if (handle->head == node) { + handle->head = node->next_node; + handle->head->prev_node = NULL; + } else if (handle->tail == node) { + handle->tail = node->prev_node; + handle->tail->next_node = NULL; + } else { + /* Its somewhere in the middle of the list */ + node->next_node->prev_node = node->prev_node; + node->prev_node->next_node = node->next_node; + } + + pceplib_free(PCEPLIB_INFRA, node); + (handle->num_entries)--; + + return data; +} diff --git a/pceplib/pcep_utils_double_linked_list.h b/pceplib/pcep_utils_double_linked_list.h new file mode 100644 index 0000000..4fe0172 --- /dev/null +++ b/pceplib/pcep_utils_double_linked_list.h @@ -0,0 +1,72 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_DOUBLE_LINKED_LIST_H_ +#define PCEP_UTILS_INCLUDE_PCEP_UTILS_DOUBLE_LINKED_LIST_H_ + +typedef struct double_linked_list_node_ { + struct double_linked_list_node_ *prev_node; + struct double_linked_list_node_ *next_node; + void *data; + +} double_linked_list_node; + + +typedef struct double_linked_list_ { + double_linked_list_node *head; + double_linked_list_node *tail; + unsigned int num_entries; + +} double_linked_list; + + +/* Initialize a double linked list */ +double_linked_list *dll_initialize(void); + +/* Destroy a double linked list, by freeing the handle and nodes, + * user data will not be freed, and may be leaked if not handled + * externally. */ +void dll_destroy(double_linked_list *handle); +/* Destroy a double linked list, by freeing the handle and nodes, + * and the user data. */ +void dll_destroy_with_data(double_linked_list *handle); +void dll_destroy_with_data_memtype(double_linked_list *handle, + void *data_memory_type); + +/* Creates a node and adds it as the first item in the list */ +double_linked_list_node *dll_prepend(double_linked_list *handle, void *data); + +/* Creates a node and adds it as the last item in the list */ +double_linked_list_node *dll_append(double_linked_list *handle, void *data); + +/* Delete the first node in the list, and return the data */ +void *dll_delete_first_node(double_linked_list *handle); + +/* Delete the last node in the list, and return the data */ +void *dll_delete_last_node(double_linked_list *handle); + +/* Delete the designated node in the list, and return the data */ +void *dll_delete_node(double_linked_list *handle, + double_linked_list_node *node); + +#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_DOUBLE_LINKED_LIST_H_ */ diff --git a/pceplib/pcep_utils_logging.c b/pceplib/pcep_utils_logging.c new file mode 100644 index 0000000..0286c23 --- /dev/null +++ b/pceplib/pcep_utils_logging.c @@ -0,0 +1,86 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdarg.h> +#include <stdio.h> +#include "pcep_utils_logging.h" + +/* Forward declaration */ +int pcep_stdout_logger(int priority, const char *format, va_list args); + +static pcep_logger_func logger_func = pcep_stdout_logger; +static int logging_level_ = LOG_INFO; + +void register_logger(pcep_logger_func logger) +{ + logger_func = logger; +} + +void set_logging_level(int level) +{ + logging_level_ = level; +} + +int get_logging_level() +{ + return logging_level_; +} + +void pcep_log(int priority, const char *format, ...) +{ + va_list va; + va_start(va, format); + logger_func(priority, format, va); + va_end(va); +} + +void pcep_log_hexbytes(int priority, const char *message, const uint8_t *bytes, + uint8_t bytes_len) +{ + char byte_str[2048] = {0}; + int i = 0; + + snprintf(byte_str, 2048, "%s ", message); + for (; i < bytes_len; i++) { + snprintf(byte_str, 2048, "%02x ", bytes[i]); + } + snprintf(byte_str, 2048, "\n"); + + pcep_log(priority, "%s", byte_str); +} + +/* Defined with a return type to match the FRR logging signature. + * Assuming glibc printf() is thread-safe. */ +int pcep_stdout_logger(int priority, const char *format, va_list args) +{ + if (priority <= logging_level_) { + vprintf(format, args); + printf("\n"); + } + + return 0; +} diff --git a/pceplib/pcep_utils_logging.h b/pceplib/pcep_utils_logging.h new file mode 100644 index 0000000..24ea495 --- /dev/null +++ b/pceplib/pcep_utils_logging.h @@ -0,0 +1,66 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_LOGGING_H_ +#define PCEP_UTILS_INCLUDE_PCEP_UTILS_LOGGING_H_ + +#include <syslog.h> /* Logging levels */ +#include <stdarg.h> /* va_list */ +#include <stdint.h> /* uint8_t */ + +/* + * The logging defined here i intended to provide the infrastructure to + * be able to plug-in an external logger, primarily the FRR logger. There + * will be a default internal logger implemented that will write to stdout, + * but any other advanced logging features should be implemented externally. + */ + +/* Only the following logging levels from syslog.h should be used: + * + * LOG_DEBUG - For all messages that are enabled by optional debugging + * features, typically preceded by "if (IS...DEBUG...)" + * LOG_INFO - Information that may be of interest, but + * everything seems to be working properly. + * LOG_NOTICE - Only for message pertaining to daemon startup or shutdown. + * LOG_WARNING - Warning conditions: unexpected events, but the daemon + * believes it can continue to operate correctly. + * LOG_ERR - Error situations indicating malfunctions. + * Probably requires attention. + */ + + +/* The signature of this logger function is the same as the FRR logger */ +typedef int (*pcep_logger_func)(int, const char *, va_list); +void register_logger(pcep_logger_func logger); + +/* These functions only take affect when using the internal stdout logger */ +void set_logging_level(int level); +int get_logging_level(void); + +/* Log messages either to a previously registered + * logger or to the internal default stdout logger. */ +void pcep_log(int priority, const char *format, ...); +void pcep_log_hexbytes(int priority, const char *message, const uint8_t *bytes, + uint8_t bytes_len); + +#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_LOGGING_H_ */ diff --git a/pceplib/pcep_utils_memory.c b/pceplib/pcep_utils_memory.c new file mode 100644 index 0000000..c564705 --- /dev/null +++ b/pceplib/pcep_utils_memory.c @@ -0,0 +1,224 @@ +/* + * 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> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +/* Set default values for memory function pointers */ +static pceplib_malloc_func mfunc = NULL; +static pceplib_calloc_func cfunc = NULL; +static pceplib_realloc_func rfunc = NULL; +static pceplib_strdup_func sfunc = NULL; +static pceplib_free_func ffunc = NULL; + +/* Internal memory types */ +struct pceplib_memory_type pceplib_infra_mt = { + .memory_type_name = "PCEPlib Infrastructure memory", + .total_bytes_allocated = 0, + .num_allocates = 0, + .total_bytes_freed = 0, + .num_frees = 0}; +struct pceplib_memory_type pceplib_messages_mt = { + .memory_type_name = "PCEPlib Messages memory", + .total_bytes_allocated = 0, + .num_allocates = 0, + .total_bytes_freed = 0, + .num_frees = 0}; + +/* The memory type pointers default to the internal memory types */ +void *PCEPLIB_INFRA = &pceplib_infra_mt; +void *PCEPLIB_MESSAGES = &pceplib_messages_mt; + +/* Initialize memory function pointers and memory type pointers */ +bool pceplib_memory_initialize(void *pceplib_infra_mt, + void *pceplib_messages_mt, + pceplib_malloc_func mf, pceplib_calloc_func cf, + pceplib_realloc_func rf, pceplib_strdup_func sf, + pceplib_free_func ff) +{ + PCEPLIB_INFRA = (pceplib_infra_mt ? pceplib_infra_mt : PCEPLIB_INFRA); + PCEPLIB_MESSAGES = + (pceplib_messages_mt ? pceplib_messages_mt : PCEPLIB_MESSAGES); + + mfunc = (mf ? mf : mfunc); + cfunc = (cf ? cf : cfunc); + rfunc = (rf ? rf : rfunc); + sfunc = (sf ? sf : sfunc); + ffunc = (ff ? ff : ffunc); + + return true; +} + +void pceplib_memory_reset() +{ + pceplib_infra_mt.total_bytes_allocated = 0; + pceplib_infra_mt.num_allocates = 0; + pceplib_infra_mt.total_bytes_freed = 0; + pceplib_infra_mt.num_frees = 0; + + pceplib_messages_mt.total_bytes_allocated = 0; + pceplib_messages_mt.num_allocates = 0; + pceplib_messages_mt.total_bytes_freed = 0; + pceplib_messages_mt.num_frees = 0; +} + +void pceplib_memory_dump() +{ + if (PCEPLIB_INFRA) { + pcep_log( + LOG_INFO, + "%s: Memory Type [%s] Total [allocs, alloc bytes, frees] [%d, %d, %d]", + __func__, + ((struct pceplib_memory_type *)PCEPLIB_INFRA) + ->memory_type_name, + ((struct pceplib_memory_type *)PCEPLIB_INFRA) + ->num_allocates, + ((struct pceplib_memory_type *)PCEPLIB_INFRA) + ->total_bytes_allocated, + ((struct pceplib_memory_type *)PCEPLIB_INFRA) + ->num_frees); + } + + if (PCEPLIB_MESSAGES) { + pcep_log( + LOG_INFO, + "%s: Memory Type [%s] Total [allocs, alloc bytes, frees] [%d, %d, %d]", + __func__, + ((struct pceplib_memory_type *)PCEPLIB_MESSAGES) + ->memory_type_name, + ((struct pceplib_memory_type *)PCEPLIB_MESSAGES) + ->num_allocates, + ((struct pceplib_memory_type *)PCEPLIB_MESSAGES) + ->total_bytes_allocated, + ((struct pceplib_memory_type *)PCEPLIB_MESSAGES) + ->num_frees); + } +} + +/* PCEPlib memory functions: + * They either call the supplied function pointers, or use the internal + * implementations, which just increment simple counters and call the + * C stdlib memory implementations. */ + +void *pceplib_malloc(void *mem_type, size_t size) +{ + if (mfunc == NULL) { + if (mem_type != NULL) { + ((struct pceplib_memory_type *)mem_type) + ->total_bytes_allocated += size; + ((struct pceplib_memory_type *)mem_type) + ->num_allocates++; + } + + return malloc(size); + } else { + return mfunc(mem_type, size); + } +} + +void *pceplib_calloc(void *mem_type, size_t size) +{ + if (cfunc == NULL) { + if (mem_type != NULL) { + ((struct pceplib_memory_type *)mem_type) + ->total_bytes_allocated += size; + ((struct pceplib_memory_type *)mem_type) + ->num_allocates++; + } + + return calloc(1, size); + } else { + return cfunc(mem_type, size); + } +} + +void *pceplib_realloc(void *mem_type, void *ptr, size_t size) +{ + if (rfunc == NULL) { + if (mem_type != NULL) { + /* TODO should add previous allocated bytes to + * total_bytes_freed */ + ((struct pceplib_memory_type *)mem_type) + ->total_bytes_allocated += size; + ((struct pceplib_memory_type *)mem_type) + ->num_allocates++; + } + + return realloc(ptr, size); + } else { + return rfunc(mem_type, ptr, size); + } +} + +void *pceplib_strdup(void *mem_type, const char *str) +{ + if (sfunc == NULL) { + if (mem_type != NULL) { + ((struct pceplib_memory_type *)mem_type) + ->total_bytes_allocated += strlen(str); + ((struct pceplib_memory_type *)mem_type) + ->num_allocates++; + } + + return strdup(str); + } else { + return sfunc(mem_type, str); + } +} + +void pceplib_free(void *mem_type, void *ptr) +{ + if (ffunc == NULL) { + if (mem_type != NULL) { + /* TODO in order to increment total_bytes_freed, we need + * to keep track of the bytes allocated per pointer. + * Currently not implemented. */ + ((struct pceplib_memory_type *)mem_type)->num_frees++; + if (((struct pceplib_memory_type *)mem_type) + ->num_allocates + < ((struct pceplib_memory_type *)mem_type) + ->num_frees) { + pcep_log( + LOG_ERR, + "%s: pceplib_free MT N_Alloc < N_Free: MemType [%s] NumAllocates [%d] NumFrees [%d]", + __func__, + ((struct pceplib_memory_type *)mem_type) + ->memory_type_name, + ((struct pceplib_memory_type *)mem_type) + ->num_allocates, + ((struct pceplib_memory_type *)mem_type) + ->num_frees); + } + } + + return free(ptr); + } else { + return ffunc(mem_type, ptr); + } +} diff --git a/pceplib/pcep_utils_memory.h b/pceplib/pcep_utils_memory.h new file mode 100644 index 0000000..4624a91 --- /dev/null +++ b/pceplib/pcep_utils_memory.h @@ -0,0 +1,89 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + +#ifndef PCEP_UTILS_INCLUDE_PCEP_UTILS_MEMORY_H_ +#define PCEP_UTILS_INCLUDE_PCEP_UTILS_MEMORY_H_ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +/* This module is intended to be used primarily with FRR's memory module, + * which has memory groups and memory types, although any memory infrastructure + * can be used that has memory types or the memory types in this module can be + * set to NULL. The PCEPlib can be used stand-alone, in which case the simple + * internal memory type system will be used. + */ + +/* These memory function pointers are modeled after the memory functions + * in frr/lib/memory.h */ +typedef void *(*pceplib_malloc_func)(void *mem_type, size_t size); +typedef void *(*pceplib_calloc_func)(void *mem_type, size_t size); +typedef void *(*pceplib_realloc_func)(void *mem_type, void *ptr, size_t size); +typedef void *(*pceplib_strdup_func)(void *mem_type, const char *str); +typedef void (*pceplib_free_func)(void *mem_type, void *ptr); + +/* Either an internal pceplib_memory_type pointer + * or could be an FRR memory type pointer */ +extern void *PCEPLIB_INFRA; +extern void *PCEPLIB_MESSAGES; + +/* Internal PCEPlib memory type */ +struct pceplib_memory_type { + char memory_type_name[64]; + uint32_t total_bytes_allocated; + uint32_t num_allocates; + uint32_t total_bytes_freed; /* currently not used */ + uint32_t num_frees; +}; + +/* Initialize this module by passing in the 2 memory types used in the PCEPlib + * and by passing in the different memory allocation/free function pointers. + * Any of these parameters can be NULL, in which case an internal implementation + * will be used. + */ +bool pceplib_memory_initialize(void *pceplib_infra_mt, + void *pceplib_messages_mt, + pceplib_malloc_func mfunc, + pceplib_calloc_func cfunc, + pceplib_realloc_func rfunc, + pceplib_strdup_func sfunc, + pceplib_free_func ffunc); + +/* Reset the internal allocation/free counters. Used mainly for internal + * testing. */ +void pceplib_memory_reset(void); +void pceplib_memory_dump(void); + +/* Memory functions to be used throughout the PCEPlib. Internally, these + * functions will either used the function pointers passed in via + * pceplib_memory_initialize() or a simple internal implementation. The + * internal implementations just increment the internal memory type + * counters and call the C stdlib memory functions. + */ +void *pceplib_malloc(void *mem_type, size_t size); +void *pceplib_calloc(void *mem_type, size_t size); +void *pceplib_realloc(void *mem_type, void *ptr, size_t size); +void *pceplib_strdup(void *mem_type, const char *str); +void pceplib_free(void *mem_type, void *ptr); + +#endif /* PCEP_UTILS_INCLUDE_PCEP_UTILS_MEMORY_H_ */ diff --git a/pceplib/pcep_utils_ordered_list.c b/pceplib/pcep_utils_ordered_list.c new file mode 100644 index 0000000..81eb614 --- /dev/null +++ b/pceplib/pcep_utils_ordered_list.c @@ -0,0 +1,326 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> + +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" +#include "pcep_utils_ordered_list.h" + +/* Compare function that simply compares pointers. + * return: + * < 0 if new_entry < list_entry + * == 0 if new_entry == list_entry (new_entry will be inserted after + * list_entry) > 0 if new_entry > list_entry + */ +int pointer_compare_function(void *list_entry, void *new_entry) +{ + return (char *)new_entry - (char *)list_entry; +} + +ordered_list_handle *ordered_list_initialize(ordered_compare_function func_ptr) +{ + ordered_list_handle *handle = + pceplib_malloc(PCEPLIB_INFRA, sizeof(ordered_list_handle)); + memset(handle, 0, sizeof(ordered_list_handle)); + handle->head = NULL; + handle->num_entries = 0; + handle->compare_function = func_ptr; + + return handle; +} + + +/* free all the ordered_list_node resources and the ordered_list_handle. + * it is assumed that the user is responsible fore freeing the data + * pointed to by the nodes. + */ +void ordered_list_destroy(ordered_list_handle *handle) +{ + if (handle == NULL) { + return; + } + + ordered_list_node *node = handle->head; + ordered_list_node *next; + + while (node != NULL) { + next = node->next_node; + pceplib_free(PCEPLIB_INFRA, node); + node = next; + } + + pceplib_free(PCEPLIB_INFRA, handle); +} + + +ordered_list_node *ordered_list_add_node(ordered_list_handle *handle, + void *data) +{ + if (handle == NULL) { + pcep_log( + LOG_WARNING, + "%s: ordered_list_add_node, the list has not been initialized", + __func__); + return NULL; + } + handle->num_entries++; + + ordered_list_node *new_node = + pceplib_malloc(PCEPLIB_INFRA, sizeof(ordered_list_node)); + memset(new_node, 0, sizeof(ordered_list_node)); + new_node->data = data; + new_node->next_node = NULL; + + /* check if its an empty list */ + if (handle->head == NULL) { + handle->head = new_node; + + return new_node; + } + + ordered_list_node *prev_node = handle->head; + ordered_list_node *node = prev_node; + int compare_result; + + while (node != NULL) { + compare_result = handle->compare_function(node->data, data); + if (compare_result < 0) { + /* insert the node */ + new_node->next_node = node; + if (handle->head == node) { + /* add it at the beginning of the list */ + handle->head = new_node; + } else { + prev_node->next_node = new_node; + } + + return new_node; + } + + /* keep searching with the next node in the list */ + prev_node = node; + node = node->next_node; + } + + /* at the end of the list, add it here */ + prev_node->next_node = new_node; + + return new_node; +} + + +ordered_list_node *ordered_list_find2(ordered_list_handle *handle, void *data, + ordered_compare_function compare_func) +{ + if (handle == NULL) { + pcep_log( + LOG_WARNING, + "%s: ordered_list_find2, the list has not been initialized", + __func__); + return NULL; + } + + ordered_list_node *node = handle->head; + int compare_result; + + while (node != NULL) { + compare_result = compare_func(node->data, data); + if (compare_result == 0) { + return node; + } else { + node = node->next_node; + } + } + + return NULL; +} + + +ordered_list_node *ordered_list_find(ordered_list_handle *handle, void *data) +{ + if (handle == NULL) { + pcep_log( + LOG_WARNING, + "%s: ordered_list_find, the list has not been initialized", + __func__); + return NULL; + } + + return ordered_list_find2(handle, data, handle->compare_function); +} + + +void *ordered_list_remove_first_node(ordered_list_handle *handle) +{ + if (handle == NULL) { + pcep_log( + LOG_WARNING, + "%s: ordered_list_remove_first_node, the list has not been initialized", + __func__); + return NULL; + } + + if (handle->head == NULL) { + return NULL; + } + handle->num_entries--; + + void *data = handle->head->data; + ordered_list_node *next_node = handle->head->next_node; + pceplib_free(PCEPLIB_INFRA, handle->head); + handle->head = next_node; + + return data; +} + + +void * +ordered_list_remove_first_node_equals2(ordered_list_handle *handle, void *data, + ordered_compare_function compare_func) +{ + if (handle == NULL) { + pcep_log( + LOG_WARNING, + "%s: ordered_list_remove_first_node_equals2, the list has not been initialized", + __func__); + return NULL; + } + + if (handle->head == NULL) { + return NULL; + } + + ordered_list_node *prev_node = handle->head; + ordered_list_node *node = prev_node; + bool keep_walking = true; + void *return_data = NULL; + int compare_result; + + while (node != NULL && keep_walking) { + compare_result = compare_func(node->data, data); + if (compare_result == 0) { + return_data = node->data; + keep_walking = false; + handle->num_entries--; + + /* adjust the corresponding pointers accordingly */ + if (handle->head == node) { + /* its the first node in the list */ + handle->head = node->next_node; + } else { + prev_node->next_node = node->next_node; + } + + pceplib_free(PCEPLIB_INFRA, node); + } else { + prev_node = node; + node = node->next_node; + } + } + + return return_data; +} + + +void *ordered_list_remove_first_node_equals(ordered_list_handle *handle, + void *data) +{ + if (handle == NULL) { + pcep_log( + LOG_WARNING, + "%s: ordered_list_remove_first_node_equals, the list has not been initialized", + __func__); + return NULL; + } + + return ordered_list_remove_first_node_equals2(handle, data, + handle->compare_function); +} + + +void *ordered_list_remove_node(ordered_list_handle *handle, + ordered_list_node *prev_node, + ordered_list_node *node_toRemove) +{ + if (handle == NULL) { + pcep_log( + LOG_WARNING, + "%s: ordered_list_remove_node, the list has not been initialized", + __func__); + return NULL; + } + + if (handle->head == NULL) { + return NULL; + } + + void *return_data = node_toRemove->data; + handle->num_entries--; + + if (node_toRemove == handle->head) { + handle->head = node_toRemove->next_node; + } else { + prev_node->next_node = node_toRemove->next_node; + } + + pceplib_free(PCEPLIB_INFRA, node_toRemove); + + return return_data; +} + +void *ordered_list_remove_node2(ordered_list_handle *handle, + ordered_list_node *node_to_remove) +{ + if (handle == NULL) { + pcep_log( + LOG_WARNING, + "%s: ordered_list_remove_node2, the list has not been initialized", + __func__); + return NULL; + } + + if (handle->head == NULL) { + return NULL; + } + + ordered_list_node *node = handle->head; + ordered_list_node *prev_node = handle->head; + + while (node != NULL) { + if (node == node_to_remove) { + return (ordered_list_remove_node(handle, prev_node, + node)); + } else { + prev_node = node; + node = node->next_node; + } + } + + return NULL; +} diff --git a/pceplib/pcep_utils_ordered_list.h b/pceplib/pcep_utils_ordered_list.h new file mode 100644 index 0000000..ec132dc --- /dev/null +++ b/pceplib/pcep_utils_ordered_list.h @@ -0,0 +1,109 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +#ifndef INCLUDE_PCEPUTILSORDEREDLIST_H_ +#define INCLUDE_PCEPUTILSORDEREDLIST_H_ + +#include <stdbool.h> + +typedef struct ordered_list_node_ { + struct ordered_list_node_ *next_node; + void *data; + +} ordered_list_node; + +/* The implementation of this function will receive a pointer to the + * new data to be inserted and a pointer to the list_entry, and should + * return: + * < 0 if new_entry < list_entry + * == 0 if new_entry == list_entry (new_entry will be inserted after + * list_entry) > 0 if new_entry > list_entry + */ +typedef int (*ordered_compare_function)(void *list_entry, void *new_entry); + +/* Compare function that compares pointers */ +int pointer_compare_function(void *list_entry, void *new_entry); + +typedef struct ordered_list_handle_ { + ordered_list_node *head; + unsigned int num_entries; + ordered_compare_function compare_function; + +} ordered_list_handle; + +ordered_list_handle *ordered_list_initialize(ordered_compare_function func_ptr); +void ordered_list_destroy(ordered_list_handle *handle); + +/* Add a new ordered_list_node to the list, using the ordered_compare_function + * to determine where in the list to add it. The newly created ordered_list_node + * will be returned. + */ +ordered_list_node *ordered_list_add_node(ordered_list_handle *handle, + void *data); + +/* Find an entry in the ordered_list using the ordered_compare_function to + * compare the data passed in. + * Return the node if found, NULL otherwise. + */ +ordered_list_node *ordered_list_find(ordered_list_handle *handle, void *data); + +/* The same as the previous function, but with a specific orderedComparefunction + */ +ordered_list_node *ordered_list_find2(ordered_list_handle *handle, void *data, + ordered_compare_function compare_func); + +/* Remove the first entry in the list and return the data it points to. + * Will return NULL if the handle is NULL or if the list is empty. + */ +void *ordered_list_remove_first_node(ordered_list_handle *handle); + +/* Remove the first entry in the list that has the same data, using the + * ordered_compare_function, and return the data it points to. + * Will return NULL if the handle is NULL or if the list is empty or + * if no entry is found that equals data. + */ +void *ordered_list_remove_first_node_equals(ordered_list_handle *handle, + void *data); + +/* The same as the previous function, but with a specific orderedComparefunction + */ +void *ordered_list_remove_first_node_equals2(ordered_list_handle *handle, + void *data, + ordered_compare_function func_ptr); + +/* Remove the node "node_to_remove" and adjust the "prev_node" pointers + * accordingly, returning the data pointed to by "node_to_remove". Will return + * NULL if the handle is NULL or if the list is empty. + */ +void *ordered_list_remove_node(ordered_list_handle *handle, + ordered_list_node *prev_node, + ordered_list_node *node_to_remove); + +/* Remove the node "node_to_remove" by searching for it in the entire list, + * returning the data pointed to by "node_to_remove". + * Will return NULL if the handle is NULL or if the list is empty. + */ +void *ordered_list_remove_node2(ordered_list_handle *handle, + ordered_list_node *node_to_remove); + +#endif /* INCLUDE_PCEPUTILSORDEREDLIST_H_ */ diff --git a/pceplib/pcep_utils_queue.c b/pceplib/pcep_utils_queue.c new file mode 100644 index 0000000..627533d --- /dev/null +++ b/pceplib/pcep_utils_queue.c @@ -0,0 +1,154 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" +#include "pcep_utils_queue.h" + +queue_handle *queue_initialize() +{ + /* Set the max_entries to 0 to disable it */ + return queue_initialize_with_size(0); +} + + +queue_handle *queue_initialize_with_size(unsigned int max_entries) +{ + queue_handle *handle = + pceplib_malloc(PCEPLIB_INFRA, sizeof(queue_handle)); + memset(handle, 0, sizeof(queue_handle)); + handle->max_entries = max_entries; + + return handle; +} + + +void queue_destroy(queue_handle *handle) +{ + if (handle == NULL) { + pcep_log( + LOG_DEBUG, + "%s: queue_destroy, the queue has not been initialized", + __func__); + return; + } + + while (queue_dequeue(handle) != NULL) { + } + pceplib_free(PCEPLIB_INFRA, handle); +} + + +void queue_destroy_with_data(queue_handle *handle) +{ + if (handle == NULL) { + pcep_log( + LOG_DEBUG, + "%s: queue_destroy_with_data, the queue has not been initialized", + __func__); + return; + } + + void *data = queue_dequeue(handle); + while (data != NULL) { + pceplib_free(PCEPLIB_INFRA, data); + data = queue_dequeue(handle); + } + pceplib_free(PCEPLIB_INFRA, handle); +} + + +queue_node *queue_enqueue(queue_handle *handle, void *data) +{ + if (handle == NULL) { + pcep_log( + LOG_DEBUG, + "%s: queue_enqueue, the queue has not been initialized", + __func__); + return NULL; + } + + if (handle->max_entries > 0 + && handle->num_entries >= handle->max_entries) { + pcep_log( + LOG_DEBUG, + "%s: queue_enqueue, cannot enqueue: max entries hit [%u]", + handle->num_entries); + return NULL; + } + + queue_node *new_node = + pceplib_malloc(PCEPLIB_INFRA, sizeof(queue_node)); + memset(new_node, 0, sizeof(queue_node)); + new_node->data = data; + new_node->next_node = NULL; + + (handle->num_entries)++; + if (handle->head == NULL) { + /* its the first entry in the queue */ + handle->head = handle->tail = new_node; + } else { + handle->tail->next_node = new_node; + handle->tail = new_node; + } + + return new_node; +} + + +void *queue_dequeue(queue_handle *handle) +{ + if (handle == NULL) { + pcep_log( + LOG_DEBUG, + "%s: queue_dequeue, the queue has not been initialized", + __func__); + return NULL; + } + + if (handle->head == NULL) { + return NULL; + } + + void *node_data = handle->head->data; + queue_node *node = handle->head; + (handle->num_entries)--; + if (handle->head == handle->tail) { + /* its the last entry in the queue */ + handle->head = handle->tail = NULL; + } else { + handle->head = node->next_node; + } + + pceplib_free(PCEPLIB_INFRA, node); + + return node_data; +} diff --git a/pceplib/pcep_utils_queue.h b/pceplib/pcep_utils_queue.h new file mode 100644 index 0000000..8380676 --- /dev/null +++ b/pceplib/pcep_utils_queue.h @@ -0,0 +1,49 @@ +/* + * 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 General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +#ifndef INCLUDE_PCEPUTILSQUEUE_H_ +#define INCLUDE_PCEPUTILSQUEUE_H_ + +typedef struct queue_node_ { + struct queue_node_ *next_node; + void *data; + +} queue_node; + +typedef struct queue_handle_ { + queue_node *head; + queue_node *tail; + unsigned int num_entries; + /* Set to 0 to disable */ + unsigned int max_entries; + +} queue_handle; + +queue_handle *queue_initialize(void); +queue_handle *queue_initialize_with_size(unsigned int max_entries); +void queue_destroy(queue_handle *handle); +void queue_destroy_with_data(queue_handle *handle); +queue_node *queue_enqueue(queue_handle *handle, void *data); +void *queue_dequeue(queue_handle *handle); + +#endif /* INCLUDE_PCEPUTILSQUEUE_H_ */ diff --git a/pceplib/subdir.am b/pceplib/subdir.am new file mode 100644 index 0000000..2633f67 --- /dev/null +++ b/pceplib/subdir.am @@ -0,0 +1,62 @@ +if PATHD_PCEP + +noinst_LTLIBRARIES = pceplib/libpcep_pcc.la pceplib/libsocket_comm_mock.la +pceplib_libpcep_pcc_la_CFLAGS = $(AM_CFLAGS) -fPIC +pceplib_libpcep_pcc_la_SOURCES = pceplib/pcep_msg_messages.c \ + pceplib/pcep_msg_objects.c \ + pceplib/pcep_msg_tlvs.c \ + pceplib/pcep_msg_tools.c \ + pceplib/pcep_msg_messages_encoding.c \ + pceplib/pcep_msg_objects_encoding.c \ + pceplib/pcep_msg_tlvs_encoding.c \ + pceplib/pcep_msg_object_error_types.c \ + pceplib/pcep_pcc_api.c \ + pceplib/pcep_session_logic.c \ + pceplib/pcep_session_logic_loop.c \ + pceplib/pcep_session_logic_states.c \ + pceplib/pcep_session_logic_counters.c \ + pceplib/pcep_socket_comm_loop.c \ + pceplib/pcep_socket_comm.c \ + pceplib/pcep_timers_event_loop.c \ + pceplib/pcep_timers.c \ + pceplib/pcep_utils_counters.c \ + pceplib/pcep_utils_double_linked_list.c \ + pceplib/pcep_utils_logging.c \ + pceplib/pcep_utils_memory.c \ + pceplib/pcep_utils_ordered_list.c \ + pceplib/pcep_utils_queue.c + +if PATHD_PCEP_TEST +# SocketComm Mock library used for Unit Testing +pceplib_libsocket_comm_mock_la_SOURCES = pceplib/pcep_socket_comm_mock.c +endif + +noinst_HEADERS += pceplib/pcep.h \ + pceplib/pcep_msg_encoding.h \ + pceplib/pcep_msg_messages.h \ + pceplib/pcep_msg_object_error_types.h \ + pceplib/pcep_msg_objects.h \ + pceplib/pcep_msg_tlvs.h \ + pceplib/pcep_msg_tools.h \ + pceplib/pcep_pcc_api.h \ + pceplib/pcep_session_logic.h \ + pceplib/pcep_session_logic_internals.h \ + pceplib/pcep_socket_comm.h \ + pceplib/pcep_socket_comm_internals.h \ + pceplib/pcep_socket_comm_loop.h \ + pceplib/pcep_socket_comm_mock.h \ + pceplib/pcep_timer_internals.h \ + pceplib/pcep_timers.h \ + pceplib/pcep_timers_event_loop.h \ + pceplib/pcep_utils_counters.h \ + pceplib/pcep_utils_double_linked_list.h \ + pceplib/pcep_utils_logging.h \ + pceplib/pcep_utils_memory.h \ + pceplib/pcep_utils_ordered_list.h \ + pceplib/pcep_utils_queue.h + +noinst_PROGRAMS += pceplib/pcep_pcc +pceplib_pcep_pcc_SOURCES = pceplib/pcep_pcc.c +pceplib_pcep_pcc_LDADD = pceplib/libpcep_pcc.la lib/libfrr.la -lpthread + +endif diff --git a/pceplib/test/pcep_msg_messages_test.c b/pceplib/test/pcep_msg_messages_test.c new file mode 100644 index 0000000..e3a74f9 --- /dev/null +++ b/pceplib/test/pcep_msg_messages_test.c @@ -0,0 +1,560 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <assert.h> + +#include <CUnit/CUnit.h> + +#include "pcep_msg_encoding.h" +#include "pcep_msg_messages.h" +#include "pcep_msg_objects.h" +#include "pcep_msg_tools.h" +#include "pcep_utils_double_linked_list.h" +#include "pcep_utils_memory.h" +#include "pcep_msg_messages_test.h" + +/* + * Notice: + * All of these message Unit Tests encode the created messages by explicitly + * calling pcep_encode_message() thus testing the message creation and the + * message encoding. + */ + +static struct pcep_versioning *versioning = NULL; + +int pcep_messages_test_suite_setup(void) +{ + pceplib_memory_reset(); + return 0; +} + +int pcep_messages_test_suite_teardown(void) +{ + printf("\n"); + pceplib_memory_dump(); + return 0; +} + +void pcep_messages_test_setup() +{ + versioning = create_default_pcep_versioning(); +} + +void pcep_messages_test_teardown() +{ + destroy_pcep_versioning(versioning); +} + +void test_pcep_msg_create_open() +{ + uint8_t keepalive = 30; + uint8_t deadtimer = 60; + uint8_t sid = 255; + + struct pcep_message *message = + pcep_msg_create_open(keepalive, deadtimer, sid); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 1); + CU_ASSERT_EQUAL(message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + pcep_object_get_length(PCEP_OBJ_CLASS_OPEN, + PCEP_OBJ_TYPE_OPEN)); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_OPEN); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + + /* Just check the class and type, the rest of the hdr fields + * are verified in pcep-objects-test.c */ + struct pcep_object_open *open_obj = + (struct pcep_object_open *)message->obj_list->head->data; + CU_ASSERT_EQUAL(open_obj->header.object_class, PCEP_OBJ_CLASS_OPEN); + CU_ASSERT_EQUAL(open_obj->header.object_type, PCEP_OBJ_TYPE_OPEN); + + CU_ASSERT_EQUAL(open_obj->open_deadtimer, deadtimer); + CU_ASSERT_EQUAL(open_obj->open_keepalive, keepalive); + CU_ASSERT_EQUAL(open_obj->open_sid, sid); + CU_ASSERT_EQUAL(open_obj->open_version, PCEP_OBJECT_OPEN_VERSION); + pcep_msg_free_message(message); +} + + +void test_pcep_msg_create_request() +{ + /* First test with NULL objects */ + struct pcep_message *message = + pcep_msg_create_request(NULL, NULL, NULL); + CU_ASSERT_PTR_NULL(message); + + /* Test IPv4 */ + struct pcep_object_rp *rp_obj = + pcep_obj_create_rp(0, false, false, false, false, 10, NULL); + struct in_addr src_addr = {}, dst_addr = {}; + struct pcep_object_endpoints_ipv4 *ipv4_obj = + pcep_obj_create_endpoint_ipv4(&src_addr, &dst_addr); + message = pcep_msg_create_request(rp_obj, ipv4_obj, NULL); + + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 2); + CU_ASSERT_EQUAL( + message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + pcep_object_get_length_by_hdr(&rp_obj->header) + + pcep_object_get_length_by_hdr(&ipv4_obj->header)); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREQ); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + pcep_msg_free_message(message); + + /* Test IPv6 */ + rp_obj = pcep_obj_create_rp(0, false, false, false, false, 10, NULL); + struct in6_addr src_addr_ipv6 = {}, dst_addr_ipv6 = {}; + struct pcep_object_endpoints_ipv6 *ipv6_obj = + pcep_obj_create_endpoint_ipv6(&src_addr_ipv6, &dst_addr_ipv6); + message = pcep_msg_create_request_ipv6(rp_obj, ipv6_obj, NULL); + + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 2); + CU_ASSERT_EQUAL( + message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + pcep_object_get_length_by_hdr(&rp_obj->header) + + pcep_object_get_length_by_hdr(&ipv6_obj->header)); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREQ); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + pcep_msg_free_message(message); + + /* The objects get deleted with the message, so they need to be created + * again */ + rp_obj = pcep_obj_create_rp(0, false, false, false, false, 10, NULL); + ipv4_obj = pcep_obj_create_endpoint_ipv4(&src_addr, &dst_addr); + struct pcep_object_bandwidth *bandwidth_obj = + pcep_obj_create_bandwidth(4.2); + double_linked_list *obj_list = dll_initialize(); + dll_append(obj_list, bandwidth_obj); + message = pcep_msg_create_request(rp_obj, ipv4_obj, obj_list); + + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 3); + CU_ASSERT_EQUAL( + message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + pcep_object_get_length_by_hdr(&rp_obj->header) + + pcep_object_get_length_by_hdr(&ipv4_obj->header) + + pcep_object_get_length_by_hdr( + &bandwidth_obj->header)); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREQ); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + pcep_msg_free_message(message); +} + + +void test_pcep_msg_create_request_svec() +{ +} + + +void test_pcep_msg_create_reply_nopath() +{ + struct pcep_object_rp *rp_obj = + pcep_obj_create_rp(0, false, false, false, false, 10, NULL); + struct pcep_object_nopath *nopath_obj = pcep_obj_create_nopath( + false, false, PCEP_NOPATH_TLV_ERR_NO_TLV); + double_linked_list *obj_list = dll_initialize(); + dll_append(obj_list, nopath_obj); + + struct pcep_message *message = pcep_msg_create_reply(rp_obj, obj_list); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 2); + CU_ASSERT_EQUAL(message->encoded_message_length, + (MESSAGE_HEADER_LENGTH + + pcep_object_get_length_by_hdr(&rp_obj->header) + + pcep_object_get_length_by_hdr(&nopath_obj->header))); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREP); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + pcep_msg_free_message(message); +} + + +void test_pcep_msg_create_reply() +{ + /* First test with NULL ero and rp objects */ + struct pcep_message *message = pcep_msg_create_reply(NULL, NULL); + + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 0); + CU_ASSERT_EQUAL(message->encoded_message_length, MESSAGE_HEADER_LENGTH); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREP); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + pcep_msg_free_message(message); + + double_linked_list *ero_subobj_list = dll_initialize(); + struct pcep_object_ro_subobj *ero_subobj = + (struct pcep_object_ro_subobj *) + pcep_obj_create_ro_subobj_32label(true, 1, 10); + dll_append(ero_subobj_list, ero_subobj); + struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list); + + double_linked_list *object_list = dll_initialize(); + dll_append(object_list, ero); + struct pcep_object_rp *rp_obj = + pcep_obj_create_rp(0, false, false, false, false, 10, NULL); + message = pcep_msg_create_reply(rp_obj, object_list); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 2); + CU_ASSERT_EQUAL(message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + pcep_object_get_length_by_hdr(&rp_obj->header) + + OBJECT_HEADER_LENGTH + + OBJECT_RO_SUBOBJ_HEADER_LENGTH + + 6 /* size of the 32label */); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCREP); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + pcep_msg_free_message(message); +} + + +void test_pcep_msg_create_close() +{ + uint8_t reason = PCEP_CLOSE_REASON_UNREC_MSG; + + struct pcep_message *message = pcep_msg_create_close(reason); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 1); + CU_ASSERT_EQUAL(message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + pcep_object_get_length(PCEP_OBJ_CLASS_CLOSE, + PCEP_OBJ_TYPE_CLOSE)); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_CLOSE); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + + /* Just check the class and type, the rest of the hdr fields + * are verified in pcep-objects-test.c */ + struct pcep_object_close *close_obj = + (struct pcep_object_close *)message->obj_list->head->data; + assert(close_obj != NULL); + CU_ASSERT_EQUAL(close_obj->header.object_class, PCEP_OBJ_CLASS_CLOSE); + CU_ASSERT_EQUAL(close_obj->header.object_type, PCEP_OBJ_TYPE_CLOSE); + CU_ASSERT_EQUAL(close_obj->reason, reason); + pcep_msg_free_message(message); +} + + +void test_pcep_msg_create_error() +{ + uint8_t error_type = PCEP_ERRT_RECEPTION_OF_INV_OBJECT; + uint8_t error_value = PCEP_ERRV_KEEPALIVEWAIT_TIMED_OUT; + + struct pcep_message *message = + pcep_msg_create_error(error_type, error_value); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 1); + CU_ASSERT_EQUAL(message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + pcep_object_get_length(PCEP_OBJ_CLASS_ERROR, + PCEP_OBJ_TYPE_ERROR)); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_ERROR); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + + /* Just check the class and type, the rest of the hdr fields + * are verified in pcep-objects-test.c */ + struct pcep_object_error *error_obj = + (struct pcep_object_error *)message->obj_list->head->data; + CU_ASSERT_EQUAL(error_obj->header.object_class, PCEP_OBJ_CLASS_ERROR); + CU_ASSERT_EQUAL(error_obj->header.object_type, PCEP_OBJ_TYPE_ERROR); + + CU_ASSERT_EQUAL(error_obj->error_type, error_type); + CU_ASSERT_EQUAL(error_obj->error_value, error_value); + pcep_msg_free_message(message); +} + + +void test_pcep_msg_create_keepalive() +{ + struct pcep_message *message = pcep_msg_create_keepalive(); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 0); + CU_ASSERT_EQUAL(message->encoded_message_length, MESSAGE_HEADER_LENGTH); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_KEEPALIVE); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + pcep_msg_free_message(message); +} + +void test_pcep_msg_create_report() +{ + double_linked_list *obj_list = dll_initialize(); + + /* Should return NULL if obj_list is empty */ + struct pcep_message *message = pcep_msg_create_report(NULL); + CU_ASSERT_PTR_NULL(message); + + struct pcep_object_lsp *lsp = + pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true, + true, true, true, NULL); + dll_append(obj_list, lsp); + message = pcep_msg_create_report(obj_list); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 1); + CU_ASSERT_EQUAL(message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + lsp->header.encoded_object_length); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_REPORT); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + + pcep_msg_free_message(message); +} + +void test_pcep_msg_create_update() +{ + double_linked_list *obj_list = dll_initialize(); + double_linked_list *ero_subobj_list = dll_initialize(); + + struct pcep_message *message = pcep_msg_create_update(NULL); + CU_ASSERT_PTR_NULL(message); + + /* Should return NULL if obj_list is empty */ + message = pcep_msg_create_update(obj_list); + CU_ASSERT_PTR_NULL(message); + if (message != NULL) { + pcep_msg_free_message(message); + message = NULL; + } + + struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL); + struct pcep_object_lsp *lsp = + pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true, + true, true, true, NULL); + dll_append(ero_subobj_list, pcep_obj_create_ro_subobj_asn(0x0102)); + struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list); + + /* Should return NULL if obj_list does not have 3 entries */ + dll_append(obj_list, srp); + dll_append(obj_list, lsp); + message = pcep_msg_create_update(obj_list); + CU_ASSERT_PTR_NULL(message); + + dll_append(obj_list, ero); + if (message != NULL) { + pcep_msg_free_message(message); + message = NULL; + } + message = pcep_msg_create_update(obj_list); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 3); + CU_ASSERT_EQUAL(message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + srp->header.encoded_object_length + + lsp->header.encoded_object_length + + ero->header.encoded_object_length); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_UPDATE); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + + pcep_msg_free_message(message); +} + +void test_pcep_msg_create_initiate() +{ + double_linked_list *obj_list = dll_initialize(); + double_linked_list *ero_subobj_list = dll_initialize(); + + /* Should return NULL if obj_list is empty */ + struct pcep_message *message = pcep_msg_create_initiate(NULL); + CU_ASSERT_PTR_NULL(message); + if (message != NULL) { + pcep_msg_free_message(message); + message = NULL; + } + + struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL); + struct pcep_object_lsp *lsp = + pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true, + true, true, true, NULL); + dll_append(ero_subobj_list, pcep_obj_create_ro_subobj_asn(0x0102)); + struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list); + + /* Should return NULL if obj_list does not have 2 entries */ + dll_append(obj_list, srp); + message = pcep_msg_create_initiate(obj_list); + CU_ASSERT_PTR_NULL(message); + if (message != NULL) { + pcep_msg_free_message(message); + message = NULL; + } + + dll_append(obj_list, lsp); + dll_append(obj_list, ero); + message = pcep_msg_create_initiate(obj_list); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->msg_header); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 3); + CU_ASSERT_EQUAL(message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + srp->header.encoded_object_length + + lsp->header.encoded_object_length + + ero->header.encoded_object_length); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_INITIATE); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + + pcep_msg_free_message(message); +} + +void test_pcep_msg_create_notify(void) +{ + struct pcep_object_notify *notify_obj = pcep_obj_create_notify( + PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED, + PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST); + + /* Should return NULL if the notify obj is empty */ + struct pcep_message *message = pcep_msg_create_notify(NULL, NULL); + CU_ASSERT_PTR_NULL(message); + + message = pcep_msg_create_notify(notify_obj, NULL); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 1); + CU_ASSERT_EQUAL(message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + notify_obj->header.encoded_object_length); + assert(message->msg_header != NULL); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCNOTF); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + + pcep_msg_free_message(message); + + struct pcep_object_rp *rp_obj = + pcep_obj_create_rp(0, false, false, false, false, 10, NULL); + double_linked_list *obj_list = dll_initialize(); + dll_append(obj_list, rp_obj); + notify_obj = pcep_obj_create_notify( + PCEP_NOTIFY_TYPE_PENDING_REQUEST_CANCELLED, + PCEP_NOTIFY_VALUE_PCC_CANCELLED_REQUEST); + + message = pcep_msg_create_notify(notify_obj, obj_list); + CU_ASSERT_PTR_NOT_NULL(message); + pcep_encode_message(message, versioning); + assert(message != NULL); + CU_ASSERT_PTR_NOT_NULL(message->obj_list); + assert(message->obj_list != NULL); + CU_ASSERT_EQUAL(message->obj_list->num_entries, 2); + CU_ASSERT_EQUAL(message->encoded_message_length, + MESSAGE_HEADER_LENGTH + + notify_obj->header.encoded_object_length + + rp_obj->header.encoded_object_length); + CU_ASSERT_EQUAL(message->msg_header->type, PCEP_TYPE_PCNOTF); + CU_ASSERT_EQUAL(message->msg_header->pcep_version, + PCEP_MESSAGE_HEADER_VERSION); + + pcep_msg_free_message(message); +} diff --git a/pceplib/test/pcep_msg_messages_test.h b/pceplib/test/pcep_msg_messages_test.h new file mode 100644 index 0000000..a3295c7 --- /dev/null +++ b/pceplib/test/pcep_msg_messages_test.h @@ -0,0 +1,48 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_MSG_MSG_TEST_H_ +#define PCEP_MSG_MSG_TEST_H_ + +/* functions to be tested from pcep-messages.c */ +int pcep_messages_test_suite_setup(void); +int pcep_messages_test_suite_teardown(void); +void pcep_messages_test_setup(void); +void pcep_messages_test_teardown(void); +void test_pcep_msg_create_open(void); +void test_pcep_msg_create_request(void); +void test_pcep_msg_create_request_svec(void); +void test_pcep_msg_create_reply_nopath(void); +void test_pcep_msg_create_reply(void); +void test_pcep_msg_create_close(void); +void test_pcep_msg_create_error(void); +void test_pcep_msg_create_keepalive(void); +void test_pcep_msg_create_report(void); +void test_pcep_msg_create_update(void); +void test_pcep_msg_create_initiate(void); +void test_pcep_msg_create_notify(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_msg_messages_tests.c b/pceplib/test/pcep_msg_messages_tests.c new file mode 100644 index 0000000..f24a797 --- /dev/null +++ b/pceplib/test/pcep_msg_messages_tests.c @@ -0,0 +1,260 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <CUnit/Basic.h> +#include <CUnit/CUnit.h> +#include <CUnit/TestDB.h> + +#include "pcep_msg_messages_test.h" +#include "pcep_msg_tools_test.h" +#include "pcep_msg_object_error_types.h" +#include "pcep_msg_object_error_types_test.h" +#include "pcep_msg_tlvs_test.h" +#include "pcep_msg_objects_test.h" + + +int main(int argc, char **argv) +{ + /* Unused parameters cause compilation warnings */ + (void)argc; + (void)argv; + + CU_initialize_registry(); + + CU_pSuite messages_suite = CU_add_suite_with_setup_and_teardown( + "PCEP Messages Test Suite", pcep_messages_test_suite_setup, + pcep_messages_test_suite_teardown, /* suite setup and cleanup + function pointers */ + pcep_messages_test_setup, pcep_messages_test_teardown); + CU_add_test(messages_suite, "test_pcep_msg_create_open", + test_pcep_msg_create_open); + CU_add_test(messages_suite, "test_pcep_msg_create_request", + test_pcep_msg_create_request); + CU_add_test(messages_suite, "test_pcep_msg_create_request_svec", + test_pcep_msg_create_request_svec); + CU_add_test(messages_suite, "test_pcep_msg_create_reply_nopath", + test_pcep_msg_create_reply_nopath); + CU_add_test(messages_suite, "test_pcep_msg_create_reply", + test_pcep_msg_create_reply); + CU_add_test(messages_suite, "test_pcep_msg_create_close", + test_pcep_msg_create_close); + CU_add_test(messages_suite, "test_pcep_msg_create_error", + test_pcep_msg_create_error); + CU_add_test(messages_suite, "test_pcep_msg_create_keepalive", + test_pcep_msg_create_keepalive); + CU_add_test(messages_suite, "test_pcep_msg_create_report", + test_pcep_msg_create_report); + CU_add_test(messages_suite, "test_pcep_msg_create_update", + test_pcep_msg_create_update); + CU_add_test(messages_suite, "test_pcep_msg_create_initiate", + test_pcep_msg_create_initiate); + CU_add_test(messages_suite, "test_pcep_msg_create_notify", + test_pcep_msg_create_notify); + + CU_pSuite tlvs_suite = CU_add_suite_with_setup_and_teardown( + "PCEP TLVs Test Suite", pcep_tlvs_test_suite_setup, + pcep_tlvs_test_suite_teardown, /* suite setup and cleanup + function pointers */ + pcep_tlvs_test_setup, pcep_tlvs_test_teardown); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_stateful_pce_capability", + test_pcep_tlv_create_stateful_pce_capability); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_speaker_entity_id", + test_pcep_tlv_create_speaker_entity_id); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_lsp_db_version", + test_pcep_tlv_create_lsp_db_version); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_path_setup_type", + test_pcep_tlv_create_path_setup_type); + CU_add_test(tlvs_suite, + "test_pcep_tlv_create_path_setup_type_capability", + test_pcep_tlv_create_path_setup_type_capability); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_sr_pce_capability", + test_pcep_tlv_create_sr_pce_capability); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_symbolic_path_name", + test_pcep_tlv_create_symbolic_path_name); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_ipv4_lsp_identifiers", + test_pcep_tlv_create_ipv4_lsp_identifiers); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_ipv6_lsp_identifiers", + test_pcep_tlv_create_ipv6_lsp_identifiers); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_pol_id_ipv4", + test_pcep_tlv_create_srpag_pol_id_ipv4); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_pol_id_ipv6", + test_pcep_tlv_create_srpag_pol_id_ipv6); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_pol_name", + test_pcep_tlv_create_srpag_pol_name); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_cp_id", + test_pcep_tlv_create_srpag_cp_id); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_srpag_cp_pref", + test_pcep_tlv_create_srpag_cp_pref); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_lsp_error_code", + test_pcep_tlv_create_lsp_error_code); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_rsvp_ipv4_error_spec", + test_pcep_tlv_create_rsvp_ipv4_error_spec); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_rsvp_ipv6_error_spec", + test_pcep_tlv_create_rsvp_ipv6_error_spec); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_nopath_vector", + test_pcep_tlv_create_nopath_vector); + CU_add_test(tlvs_suite, "test_pcep_tlv_create_arbitrary", + test_pcep_tlv_create_arbitrary); + + CU_pSuite objects_suite = CU_add_suite_with_setup_and_teardown( + "PCEP Objects Test Suite", pcep_objects_test_suite_setup, + pcep_objects_test_suite_teardown, /* suite setup and cleanup + function pointers */ + pcep_objects_test_setup, pcep_objects_test_teardown); + CU_add_test(objects_suite, "test_pcep_obj_create_open", + test_pcep_obj_create_open); + CU_add_test(objects_suite, "test_pcep_obj_create_open", + test_pcep_obj_create_open_with_tlvs); + CU_add_test(objects_suite, "test_pcep_obj_create_rp", + test_pcep_obj_create_rp); + CU_add_test(objects_suite, "test_pcep_obj_create_nopath", + test_pcep_obj_create_nopath); + CU_add_test(objects_suite, "test_pcep_obj_create_enpoint_ipv4", + test_pcep_obj_create_endpoint_ipv4); + CU_add_test(objects_suite, "test_pcep_obj_create_enpoint_ipv6", + test_pcep_obj_create_endpoint_ipv6); + CU_add_test(objects_suite, "test_pcep_obj_create_association_ipv4", + test_pcep_obj_create_association_ipv4); + CU_add_test(objects_suite, "test_pcep_obj_create_association_ipv6", + test_pcep_obj_create_association_ipv6); + CU_add_test(objects_suite, "test_pcep_obj_create_bandwidth", + test_pcep_obj_create_bandwidth); + CU_add_test(objects_suite, "test_pcep_obj_create_metric", + test_pcep_obj_create_metric); + CU_add_test(objects_suite, "test_pcep_obj_create_lspa", + test_pcep_obj_create_lspa); + CU_add_test(objects_suite, "test_pcep_obj_create_svec", + test_pcep_obj_create_svec); + CU_add_test(objects_suite, "test_pcep_obj_create_error", + test_pcep_obj_create_error); + CU_add_test(objects_suite, "test_pcep_obj_create_close", + test_pcep_obj_create_close); + CU_add_test(objects_suite, "test_pcep_obj_create_srp", + test_pcep_obj_create_srp); + CU_add_test(objects_suite, "test_pcep_obj_create_lsp", + test_pcep_obj_create_lsp); + CU_add_test(objects_suite, "test_pcep_obj_create_vendor_info", + test_pcep_obj_create_vendor_info); + + CU_add_test(objects_suite, "test_pcep_obj_create_ero", + test_pcep_obj_create_ero); + CU_add_test(objects_suite, "test_pcep_obj_create_rro", + test_pcep_obj_create_rro); + CU_add_test(objects_suite, "test_pcep_obj_create_iro", + test_pcep_obj_create_iro); + CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_ipv4", + test_pcep_obj_create_ro_subobj_ipv4); + CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_ipv6", + test_pcep_obj_create_ro_subobj_ipv6); + CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_unnum", + test_pcep_obj_create_ro_subobj_unnum); + CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_32label", + test_pcep_obj_create_ro_subobj_32label); + CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_asn", + test_pcep_obj_create_ro_subobj_asn); + + CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_sr_nonai", + test_pcep_obj_create_ro_subobj_sr_nonai); + CU_add_test(objects_suite, + "test_pcep_obj_create_ro_subobj_sr_ipv4_node", + test_pcep_obj_create_ro_subobj_sr_ipv4_node); + CU_add_test(objects_suite, + "test_pcep_obj_create_ro_subobj_sr_ipv6_node", + test_pcep_obj_create_ro_subobj_sr_ipv6_node); + CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_sr_ipv4_adj", + test_pcep_obj_create_ro_subobj_sr_ipv4_adj); + CU_add_test(objects_suite, "test_pcep_obj_create_ro_subobj_sr_ipv6_adj", + test_pcep_obj_create_ro_subobj_sr_ipv6_adj); + CU_add_test(objects_suite, + "test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj", + test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj); + CU_add_test(objects_suite, + "test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj", + test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj); + + CU_pSuite tools_suite = CU_add_suite_with_setup_and_teardown( + "PCEP Tools Test Suite", pcep_tools_test_suite_setup, + pcep_tools_test_suite_teardown, pcep_tools_test_setup, + pcep_tools_test_teardown); + CU_add_test(tools_suite, "test_pcep_msg_read_pcep_initiate", + test_pcep_msg_read_pcep_initiate); + CU_add_test(tools_suite, "test_pcep_msg_read_pcep_initiate2", + test_pcep_msg_read_pcep_initiate2); + CU_add_test(tools_suite, "test_pcep_msg_read_pcep_update", + test_pcep_msg_read_pcep_update); + CU_add_test(tools_suite, "test_pcep_msg_read_pcep_open", + test_pcep_msg_read_pcep_open); + CU_add_test(tools_suite, "test_pcep_msg_read_pcep_open_initiate", + test_pcep_msg_read_pcep_open_initiate); + CU_add_test(tools_suite, "test_validate_message_header", + test_validate_message_header); + CU_add_test(tools_suite, "test_validate_message_objects", + test_validate_message_objects); + CU_add_test(tools_suite, "test_validate_message_objects_invalid", + test_validate_message_objects_invalid); + CU_add_test(tools_suite, "test_pcep_msg_read_pcep_open_cisco_pce", + test_pcep_msg_read_pcep_open_cisco_pce); + CU_add_test(tools_suite, "test_pcep_msg_read_pcep_update_cisco_pce", + test_pcep_msg_read_pcep_update_cisco_pce); + CU_add_test(tools_suite, "test_pcep_msg_read_pcep_report_cisco_pcc", + test_pcep_msg_read_pcep_report_cisco_pcc); + CU_add_test(tools_suite, "test_pcep_msg_read_pcep_initiate_cisco_pcc", + test_pcep_msg_read_pcep_initiate_cisco_pcc); + + CU_pSuite obj_errors_suite = CU_add_suite_with_setup_and_teardown( + "PCEP Object Error Types Test Suite", + pcep_object_error_types_test_suite_setup, + pcep_object_error_types_test_suite_teardown, + pcep_object_error_types_test_setup, + pcep_object_error_types_test_teardown); + CU_add_test(obj_errors_suite, "test_get_error_type_str", + test_get_error_type_str); + CU_add_test(obj_errors_suite, "test_get_error_value_str", + test_get_error_value_str); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_FailureRecord *failure_record = CU_get_failure_list(); + if (failure_record != NULL) { + printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n"); + do { + printf("\t [%s] [%s] [%s:%d]\n", + failure_record->pSuite->pName, + failure_record->pTest->pName, + failure_record->strFileName, + failure_record->uiLineNumber); + failure_record = failure_record->pNext; + + } while (failure_record != NULL); + } + + CU_pRunSummary run_summary = CU_get_run_summary(); + int result = run_summary->nTestsFailed; + CU_cleanup_registry(); + + return result; +} diff --git a/pceplib/test/pcep_msg_object_error_types_test.c b/pceplib/test/pcep_msg_object_error_types_test.c new file mode 100644 index 0000000..b146375 --- /dev/null +++ b/pceplib/test/pcep_msg_object_error_types_test.c @@ -0,0 +1,88 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#include <CUnit/CUnit.h> + +#include "pcep_msg_object_error_types.h" +#include "pcep_msg_object_error_types_test.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +int pcep_object_error_types_test_suite_setup(void) +{ + pceplib_memory_reset(); + set_logging_level(LOG_DEBUG); + return 0; +} + +int pcep_object_error_types_test_suite_teardown(void) +{ + printf("\n"); + pceplib_memory_dump(); + return 0; +} + +void pcep_object_error_types_test_setup(void) +{ +} + +void pcep_object_error_types_test_teardown(void) +{ +} + +void test_get_error_type_str() +{ + const char *error_type_str; + int i = 0; + for (; i < MAX_ERROR_TYPE; i++) { + error_type_str = get_error_type_str(i); + CU_ASSERT_PTR_NOT_NULL(error_type_str); + } + + CU_ASSERT_PTR_NULL(get_error_type_str(-1)); + CU_ASSERT_PTR_NULL(get_error_type_str(MAX_ERROR_TYPE)); +} + +void test_get_error_value_str() +{ + const char *error_value_str; + int i = 0, j = 0; + + for (; i < MAX_ERROR_TYPE; i++) { + for (; j < MAX_ERROR_VALUE; j++) { + error_value_str = get_error_value_str(i, j); + CU_ASSERT_PTR_NOT_NULL(error_value_str); + } + } + + CU_ASSERT_PTR_NULL(get_error_value_str(-1, 0)); + CU_ASSERT_PTR_NULL(get_error_value_str(MAX_ERROR_TYPE, 0)); + CU_ASSERT_PTR_NULL(get_error_value_str(1, -1)); + CU_ASSERT_PTR_NULL(get_error_value_str(1, MAX_ERROR_VALUE)); +} diff --git a/pceplib/test/pcep_msg_object_error_types_test.h b/pceplib/test/pcep_msg_object_error_types_test.h new file mode 100644 index 0000000..863517d --- /dev/null +++ b/pceplib/test/pcep_msg_object_error_types_test.h @@ -0,0 +1,37 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_MSG_OBJECT_ERROR_TYPES_TEST_ +#define PCEP_MSG_OBJECT_ERROR_TYPES_TEST_ + +int pcep_object_error_types_test_suite_setup(void); +int pcep_object_error_types_test_suite_teardown(void); +void pcep_object_error_types_test_setup(void); +void pcep_object_error_types_test_teardown(void); +void test_get_error_type_str(void); +void test_get_error_value_str(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_msg_objects_test.c b/pceplib/test/pcep_msg_objects_test.c new file mode 100644 index 0000000..a92bbe4 --- /dev/null +++ b/pceplib/test/pcep_msg_objects_test.c @@ -0,0 +1,1312 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> + +#include <CUnit/CUnit.h> + +#include "pcep_msg_encoding.h" +#include "pcep_msg_objects.h" +#include "pcep_msg_tools.h" +#include "pcep_utils_memory.h" +#include "pcep_msg_objects_test.h" + +/* + * Notice: + * All of these object Unit Tests encode the created objects by explicitly + * calling pcep_encode_object() thus testing the object creation and the object + * encoding. All APIs expect IPs to be in network byte order. + */ + +static struct pcep_versioning *versioning = NULL; +static uint8_t object_buf[2000]; + +void reset_objects_buffer(void); + +int pcep_objects_test_suite_setup(void) +{ + pceplib_memory_reset(); + return 0; +} + +int pcep_objects_test_suite_teardown(void) +{ + printf("\n"); + pceplib_memory_dump(); + return 0; +} + +void reset_objects_buffer() +{ + memset(object_buf, 0, 2000); +} + +void pcep_objects_test_setup() +{ + versioning = create_default_pcep_versioning(); + reset_objects_buffer(); +} + +void pcep_objects_test_teardown() +{ + destroy_pcep_versioning(versioning); +} + +/* Internal util verification function */ +static void verify_pcep_obj_header2(uint8_t obj_class, uint8_t obj_type, + uint16_t obj_length, const uint8_t *obj_buf) +{ + /* Object Header + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Object-Class | OT |Res|P|I| Object Length (bytes) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + /* Not using CU_ASSERT_EQUAL here, so that in case of failure, + * we can provide more info in the error message. */ + if (obj_buf[0] != obj_class) { + fprintf(stderr, + "Test failure obj_class expected [%d] found [%d]\n", + obj_class, obj_buf[0]); + CU_FAIL("Object Header Class"); + } + + uint8_t found8 = (obj_buf[1] >> 4) & 0x0f; + if (obj_type != found8) { + fprintf(stderr, + "Test failure obj_class [%d] obj_type expected [%d] found [%d]\n", + obj_class, obj_type, found8); + CU_FAIL("Object Header Type"); + } + + uint8_t exp8 = 0; + found8 = obj_buf[1] & 0x0f; + if (exp8 != found8) { + fprintf(stderr, + "Test failure obj_class [%d] flags expected [%d] found [%d]\n", + obj_class, exp8, found8); + CU_FAIL("Object Header Flags"); + } + + uint16_t found16 = ntohs(*((uint16_t *)(obj_buf + 2))); + if (obj_length != found16) { + fprintf(stderr, + "Test failure obj_class [%d] obj_length expected [%d] found [%d]\n", + obj_class, obj_length, found16); + CU_FAIL("Object Header Length"); + } +} + +/* Internal util verification function */ +static void verify_pcep_obj_header(uint8_t obj_class, uint8_t obj_type, + struct pcep_object_header *obj_hdr) +{ + assert(obj_hdr != NULL); + verify_pcep_obj_header2(obj_class, obj_type, + pcep_object_get_length_by_hdr(obj_hdr), + obj_hdr->encoded_object); +} + +void test_pcep_obj_create_open() +{ + uint8_t deadtimer = 60; + uint8_t keepalive = 30; + uint8_t sid = 1; + + struct pcep_object_open *open = + pcep_obj_create_open(keepalive, deadtimer, sid, NULL); + + CU_ASSERT_PTR_NOT_NULL(open); + pcep_encode_object(&open->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN, + &open->header); + + CU_ASSERT_EQUAL(open->header.encoded_object[4], + (PCEP_OBJECT_OPEN_VERSION << 5) & 0xe0); + CU_ASSERT_EQUAL(open->header.encoded_object[4] & 0x1f, 0); + CU_ASSERT_EQUAL(open->header.encoded_object[5], keepalive); + CU_ASSERT_EQUAL(open->header.encoded_object[6], deadtimer); + CU_ASSERT_EQUAL(open->header.encoded_object[7], sid); + + pcep_obj_free_object((struct pcep_object_header *)open); +} + +void test_pcep_obj_create_open_with_tlvs() +{ + uint8_t deadtimer = 60; + uint8_t keepalive = 30; + uint8_t sid = 1; + double_linked_list *tlv_list = dll_initialize(); + + struct pcep_object_tlv_stateful_pce_capability *tlv = + pcep_tlv_create_stateful_pce_capability(true, true, true, true, + true, true); + dll_append(tlv_list, tlv); + struct pcep_object_open *open = + pcep_obj_create_open(keepalive, deadtimer, sid, tlv_list); + + CU_ASSERT_PTR_NOT_NULL(open); + assert(open != NULL); + pcep_encode_object(&open->header, versioning, object_buf); + verify_pcep_obj_header2(PCEP_OBJ_CLASS_OPEN, PCEP_OBJ_TYPE_OPEN, + pcep_object_get_length_by_hdr(&open->header) + + sizeof(uint32_t) * 2, + open->header.encoded_object); + CU_ASSERT_PTR_NOT_NULL(open->header.tlv_list); + assert(open->header.tlv_list != NULL); + CU_ASSERT_EQUAL(open->header.tlv_list->num_entries, 1); + + CU_ASSERT_EQUAL(open->header.encoded_object[4], + (PCEP_OBJECT_OPEN_VERSION << 5) & 0xe0); + CU_ASSERT_EQUAL(open->header.encoded_object[4] & 0x1f, 0); + CU_ASSERT_EQUAL(open->header.encoded_object[5], keepalive); + CU_ASSERT_EQUAL(open->header.encoded_object[6], deadtimer); + CU_ASSERT_EQUAL(open->header.encoded_object[7], sid); + + pcep_obj_free_object((struct pcep_object_header *)open); +} + +void test_pcep_obj_create_rp() +{ + uint32_t reqid = 15; + uint8_t invalid_priority = 100; + uint8_t priority = 7; + + struct pcep_object_rp *rp = pcep_obj_create_rp( + invalid_priority, true, false, false, true, reqid, NULL); + CU_ASSERT_PTR_NULL(rp); + + rp = pcep_obj_create_rp(priority, true, false, false, true, reqid, + NULL); + CU_ASSERT_PTR_NOT_NULL(rp); + pcep_encode_object(&rp->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_RP, PCEP_OBJ_TYPE_RP, + &rp->header); + + CU_ASSERT_EQUAL(rp->header.encoded_object[4], 0); + CU_ASSERT_EQUAL(rp->header.encoded_object[5], 0); + CU_ASSERT_EQUAL(rp->header.encoded_object[6], 0); + CU_ASSERT_EQUAL((rp->header.encoded_object[7] & 0x07), priority); + CU_ASSERT_TRUE(rp->header.encoded_object[7] & OBJECT_RP_FLAG_R); + CU_ASSERT_TRUE(rp->header.encoded_object[7] & OBJECT_RP_FLAG_OF); + CU_ASSERT_TRUE(rp->header.encoded_object[7] & ~OBJECT_RP_FLAG_B); + CU_ASSERT_TRUE(rp->header.encoded_object[7] & ~OBJECT_RP_FLAG_O); + CU_ASSERT_EQUAL(*((uint32_t *)(rp->header.encoded_object + 8)), + htonl(reqid)); + + pcep_obj_free_object((struct pcep_object_header *)rp); +} + +void test_pcep_obj_create_nopath() +{ + uint8_t ni = 8; + uint32_t errorcode = 42; + + struct pcep_object_nopath *nopath = + pcep_obj_create_nopath(ni, true, errorcode); + + CU_ASSERT_PTR_NOT_NULL(nopath); + pcep_encode_object(&nopath->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_NOPATH, PCEP_OBJ_TYPE_NOPATH, + &nopath->header); + + CU_ASSERT_EQUAL(nopath->header.encoded_object[4], ni); + CU_ASSERT_TRUE(nopath->header.encoded_object[5] & OBJECT_NOPATH_FLAG_C); + CU_ASSERT_EQUAL(nopath->header.encoded_object[6], 0); + CU_ASSERT_EQUAL(nopath->header.encoded_object[7], 0); + + /* Verify the TLV */ + assert(nopath != NULL); + assert(nopath->header.tlv_list != NULL); + CU_ASSERT_PTR_NOT_NULL(nopath->header.tlv_list); + struct pcep_object_tlv_nopath_vector *tlv = + (struct pcep_object_tlv_nopath_vector *) + nopath->header.tlv_list->head->data; + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 4); + CU_ASSERT_EQUAL(tlv->header.type, 1); + CU_ASSERT_EQUAL(tlv->error_code, errorcode); + + CU_ASSERT_EQUAL(*((uint16_t *)(nopath->header.encoded_object + 8)), + htons(PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR)); + CU_ASSERT_EQUAL(*((uint16_t *)(nopath->header.encoded_object + 10)), + htons(4)); + CU_ASSERT_EQUAL(*((uint32_t *)(nopath->header.encoded_object + 12)), + htonl(errorcode)); + + pcep_obj_free_object((struct pcep_object_header *)nopath); +} +void test_pcep_obj_create_association_ipv4() +{ + + uint16_t all_assoc_groups = 0xffff; + struct in_addr src; + inet_pton(AF_INET, "192.168.1.2", &src); + + struct pcep_object_association_ipv4 *assoc = + pcep_obj_create_association_ipv4( + false, PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE, + all_assoc_groups, src); + CU_ASSERT_PTR_NOT_NULL(assoc); + assert(assoc != NULL); + CU_ASSERT_EQUAL(assoc->association_type, + PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE); + CU_ASSERT_EQUAL(assoc->association_id, all_assoc_groups); + CU_ASSERT_EQUAL(assoc->header.object_class, PCEP_OBJ_CLASS_ASSOCIATION); + CU_ASSERT_EQUAL(assoc->header.object_type, + PCEP_OBJ_TYPE_ASSOCIATION_IPV4); + CU_ASSERT_EQUAL(assoc->src.s_addr, src.s_addr); + + pcep_obj_free_object((struct pcep_object_header *)assoc); +} + +void test_pcep_obj_create_association_ipv6() +{ + uint32_t all_assoc_groups = 0xffff; + struct in6_addr src; + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &src); + + struct pcep_object_association_ipv6 *assoc = + pcep_obj_create_association_ipv6( + false, PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE, + all_assoc_groups, src); + CU_ASSERT_PTR_NOT_NULL(assoc); + assert(assoc != NULL); + CU_ASSERT_EQUAL(assoc->association_type, + PCEP_ASSOCIATION_TYPE_SR_POLICY_ASSOCIATION_TYPE); + CU_ASSERT_EQUAL(assoc->association_id, all_assoc_groups); + CU_ASSERT_EQUAL(assoc->header.object_class, PCEP_OBJ_CLASS_ASSOCIATION); + CU_ASSERT_EQUAL(assoc->header.object_type, + PCEP_OBJ_TYPE_ASSOCIATION_IPV6); + CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[0], + (src.__in6_u.__u6_addr32[0])); + CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[1], + (src.__in6_u.__u6_addr32[1])); + CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[2], + (src.__in6_u.__u6_addr32[2])); + CU_ASSERT_EQUAL(assoc->src.__in6_u.__u6_addr32[3], + (src.__in6_u.__u6_addr32[3])); + + pcep_obj_free_object((struct pcep_object_header *)assoc); +} + +void test_pcep_obj_create_endpoint_ipv4() +{ + struct in_addr src_ipv4, dst_ipv4; + inet_pton(AF_INET, "192.168.1.2", &src_ipv4); + inet_pton(AF_INET, "172.168.1.2", &dst_ipv4); + + struct pcep_object_endpoints_ipv4 *ipv4 = + pcep_obj_create_endpoint_ipv4(NULL, NULL); + CU_ASSERT_PTR_NULL(ipv4); + + ipv4 = pcep_obj_create_endpoint_ipv4(&src_ipv4, NULL); + CU_ASSERT_PTR_NULL(ipv4); + + ipv4 = pcep_obj_create_endpoint_ipv4(NULL, &dst_ipv4); + CU_ASSERT_PTR_NULL(ipv4); + + ipv4 = pcep_obj_create_endpoint_ipv4(&src_ipv4, &dst_ipv4); + CU_ASSERT_PTR_NOT_NULL(ipv4); + pcep_encode_object(&ipv4->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_ENDPOINTS, + PCEP_OBJ_TYPE_ENDPOINT_IPV4, &ipv4->header); + CU_ASSERT_EQUAL(*((uint32_t *)(ipv4->header.encoded_object + 4)), + src_ipv4.s_addr); + CU_ASSERT_EQUAL(*((uint32_t *)(ipv4->header.encoded_object + 8)), + dst_ipv4.s_addr); + + pcep_obj_free_object((struct pcep_object_header *)ipv4); +} + +void test_pcep_obj_create_endpoint_ipv6() +{ + struct in6_addr src_ipv6, dst_ipv6; + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &src_ipv6); + inet_pton(AF_INET6, "2001:db8::8a2e:370:8446", &dst_ipv6); + + struct pcep_object_endpoints_ipv6 *ipv6 = + pcep_obj_create_endpoint_ipv6(NULL, NULL); + CU_ASSERT_PTR_NULL(ipv6); + + ipv6 = pcep_obj_create_endpoint_ipv6(&src_ipv6, NULL); + CU_ASSERT_PTR_NULL(ipv6); + + ipv6 = pcep_obj_create_endpoint_ipv6(NULL, &dst_ipv6); + CU_ASSERT_PTR_NULL(ipv6); + + ipv6 = pcep_obj_create_endpoint_ipv6(&src_ipv6, &dst_ipv6); + CU_ASSERT_PTR_NOT_NULL(ipv6); + pcep_encode_object(&ipv6->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_ENDPOINTS, + PCEP_OBJ_TYPE_ENDPOINT_IPV6, &ipv6->header); + uint32_t *uint32_ptr = (uint32_t *)(ipv6->header.encoded_object + 4); + CU_ASSERT_EQUAL(uint32_ptr[0], src_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[1], src_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[2], src_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[3], src_ipv6.__in6_u.__u6_addr32[3]); + CU_ASSERT_EQUAL(uint32_ptr[4], dst_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[5], dst_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[6], dst_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[7], dst_ipv6.__in6_u.__u6_addr32[3]); + + pcep_obj_free_object((struct pcep_object_header *)ipv6); +} + +void test_pcep_obj_create_bandwidth() +{ + /* 1.8 => binary 1.11001101 + * exponent = 127 => 0111 1111 + * fraction = 1100 1101 0000 0000 0000 000 */ + float bandwidth = 1.8; + + struct pcep_object_bandwidth *bw = pcep_obj_create_bandwidth(bandwidth); + + CU_ASSERT_PTR_NOT_NULL(bw); + pcep_encode_object(&bw->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_BANDWIDTH, + PCEP_OBJ_TYPE_BANDWIDTH_REQ, &bw->header); + CU_ASSERT_EQUAL(bw->header.encoded_object[4], 0x3f); + CU_ASSERT_EQUAL(bw->header.encoded_object[5], 0xe6); + CU_ASSERT_EQUAL(bw->header.encoded_object[6], 0x66); + CU_ASSERT_EQUAL(bw->header.encoded_object[7], 0x66); + + pcep_obj_free_object((struct pcep_object_header *)bw); +} + +void test_pcep_obj_create_metric() +{ + uint8_t type = PCEP_METRIC_BORDER_NODE_COUNT; + /* https://en.wikipedia.org/wiki/IEEE_754-1985 + * 0.15625 = 1/8 + 1/32 = binary 0.00101 = 1.01 x 10^-3 + * Exponent bias = 127, so exponent = (127-3) = 124 = 0111 1100 + * Sign Exponent Fraction + * (8 bits) (23 bits) + * 0.15625 => 0 0111 1100 010 0000 ... 0000 */ + float value = 0.15625; + + struct pcep_object_metric *metric = + pcep_obj_create_metric(type, true, true, value); + + CU_ASSERT_PTR_NOT_NULL(metric); + pcep_encode_object(&metric->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_METRIC, PCEP_OBJ_TYPE_METRIC, + &metric->header); + CU_ASSERT_EQUAL(metric->header.encoded_object[4], 0); + CU_ASSERT_EQUAL(metric->header.encoded_object[5], 0); + CU_ASSERT_TRUE(metric->header.encoded_object[6] & OBJECT_METRIC_FLAC_B); + CU_ASSERT_TRUE(metric->header.encoded_object[6] & OBJECT_METRIC_FLAC_C); + CU_ASSERT_EQUAL(metric->header.encoded_object[7], type); + /* See comments above for explanation of these values */ + CU_ASSERT_EQUAL(metric->header.encoded_object[8], 0x3e); + CU_ASSERT_EQUAL(metric->header.encoded_object[9], 0x20); + CU_ASSERT_EQUAL(metric->header.encoded_object[10], 0x00); + CU_ASSERT_EQUAL(metric->header.encoded_object[11], 0x00); + + pcep_obj_free_object((struct pcep_object_header *)metric); +} + +void test_pcep_obj_create_lspa() +{ + uint32_t exclude_any = 10; + uint32_t include_any = 20; + uint32_t include_all = 30; + uint8_t prio = 0; + uint8_t hold_prio = 10; + + struct pcep_object_lspa *lspa = pcep_obj_create_lspa( + exclude_any, include_any, include_all, prio, hold_prio, true); + + CU_ASSERT_PTR_NOT_NULL(lspa); + pcep_encode_object(&lspa->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_LSPA, PCEP_OBJ_TYPE_LSPA, + &lspa->header); + uint32_t *uint32_ptr = (uint32_t *)(lspa->header.encoded_object + 4); + CU_ASSERT_EQUAL(uint32_ptr[0], htonl(exclude_any)); + CU_ASSERT_EQUAL(uint32_ptr[1], htonl(include_any)); + CU_ASSERT_EQUAL(uint32_ptr[2], htonl(include_all)); + CU_ASSERT_EQUAL(lspa->header.encoded_object[16], prio); + CU_ASSERT_EQUAL(lspa->header.encoded_object[17], hold_prio); + CU_ASSERT_TRUE(lspa->header.encoded_object[18] & OBJECT_LSPA_FLAG_L); + CU_ASSERT_EQUAL(lspa->header.encoded_object[19], 0); + + pcep_obj_free_object((struct pcep_object_header *)lspa); +} + +void test_pcep_obj_create_svec() +{ + struct pcep_object_svec *svec = + pcep_obj_create_svec(true, true, true, NULL); + CU_ASSERT_PTR_NULL(svec); + + double_linked_list *id_list = dll_initialize(); + uint32_t *uint32_ptr = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t)); + *uint32_ptr = 10; + dll_append(id_list, uint32_ptr); + + svec = pcep_obj_create_svec(true, true, true, id_list); + CU_ASSERT_PTR_NOT_NULL(svec); + assert(svec != NULL); + pcep_encode_object(&svec->header, versioning, object_buf); + verify_pcep_obj_header2(PCEP_OBJ_CLASS_SVEC, PCEP_OBJ_TYPE_SVEC, + (OBJECT_HEADER_LENGTH + sizeof(uint32_t) * 2), + svec->header.encoded_object); + CU_ASSERT_EQUAL(svec->header.encoded_object[4], 0); + CU_ASSERT_EQUAL(svec->header.encoded_object[5], 0); + CU_ASSERT_EQUAL(svec->header.encoded_object[6], 0); + CU_ASSERT_TRUE(svec->header.encoded_object[7] & OBJECT_SVEC_FLAG_S); + CU_ASSERT_TRUE(svec->header.encoded_object[7] & OBJECT_SVEC_FLAG_N); + CU_ASSERT_TRUE(svec->header.encoded_object[7] & OBJECT_SVEC_FLAG_L); + CU_ASSERT_EQUAL(*((uint32_t *)(svec->header.encoded_object + 8)), + htonl(*uint32_ptr)); + + pcep_obj_free_object((struct pcep_object_header *)svec); +} + +void test_pcep_obj_create_error() +{ + uint8_t error_type = PCEP_ERRT_SESSION_FAILURE; + uint8_t error_value = PCEP_ERRV_RECVD_INVALID_OPEN_MSG; + + struct pcep_object_error *error = + pcep_obj_create_error(error_type, error_value); + + CU_ASSERT_PTR_NOT_NULL(error); + pcep_encode_object(&error->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_ERROR, PCEP_OBJ_TYPE_ERROR, + &error->header); + CU_ASSERT_EQUAL(error->header.encoded_object[4], 0); + CU_ASSERT_EQUAL(error->header.encoded_object[5], 0); + CU_ASSERT_EQUAL(error->header.encoded_object[6], error_type); + CU_ASSERT_EQUAL(error->header.encoded_object[7], error_value); + + pcep_obj_free_object((struct pcep_object_header *)error); +} + +void test_pcep_obj_create_close() +{ + uint8_t reason = PCEP_CLOSE_REASON_DEADTIMER; + + struct pcep_object_close *close = pcep_obj_create_close(reason); + + CU_ASSERT_PTR_NOT_NULL(close); + pcep_encode_object(&close->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_CLOSE, PCEP_OBJ_TYPE_CLOSE, + &close->header); + CU_ASSERT_EQUAL(close->header.encoded_object[4], 0); + CU_ASSERT_EQUAL(close->header.encoded_object[5], 0); + CU_ASSERT_EQUAL(close->header.encoded_object[6], 0); + CU_ASSERT_EQUAL(close->header.encoded_object[7], reason); + + pcep_obj_free_object((struct pcep_object_header *)close); +} + +void test_pcep_obj_create_srp() +{ + bool lsp_remove = true; + uint32_t srp_id_number = 0x89674523; + struct pcep_object_srp *srp = + pcep_obj_create_srp(lsp_remove, srp_id_number, NULL); + + CU_ASSERT_PTR_NOT_NULL(srp); + pcep_encode_object(&srp->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_SRP, PCEP_OBJ_TYPE_SRP, + &srp->header); + CU_ASSERT_EQUAL(srp->header.encoded_object[4], 0); + CU_ASSERT_EQUAL(srp->header.encoded_object[5], 0); + CU_ASSERT_EQUAL(srp->header.encoded_object[6], 0); + CU_ASSERT_TRUE(srp->header.encoded_object[7] & OBJECT_SRP_FLAG_R); + CU_ASSERT_EQUAL(*((uint32_t *)(srp->header.encoded_object + 8)), + htonl(srp_id_number)); + + pcep_obj_free_object((struct pcep_object_header *)srp); +} + +void test_pcep_obj_create_lsp() +{ + uint32_t plsp_id = 0x000fffff; + enum pcep_lsp_operational_status status = PCEP_LSP_OPERATIONAL_ACTIVE; + bool c_flag = true; + bool a_flag = true; + bool r_flag = true; + bool s_flag = true; + bool d_flag = true; + + /* Should return for invalid plsp_id */ + struct pcep_object_lsp *lsp = + pcep_obj_create_lsp(0x001fffff, status, c_flag, a_flag, r_flag, + s_flag, d_flag, NULL); + CU_ASSERT_PTR_NULL(lsp); + + /* Should return for invalid status */ + lsp = pcep_obj_create_lsp(plsp_id, 8, c_flag, a_flag, r_flag, s_flag, + d_flag, NULL); + CU_ASSERT_PTR_NULL(lsp); + + lsp = pcep_obj_create_lsp(plsp_id, status, c_flag, a_flag, r_flag, + s_flag, d_flag, NULL); + + CU_ASSERT_PTR_NOT_NULL(lsp); + pcep_encode_object(&lsp->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_LSP, PCEP_OBJ_TYPE_LSP, + &lsp->header); + CU_ASSERT_EQUAL((ntohl(*((uint32_t *)(lsp->header.encoded_object + 4))) + >> 12) & 0x000fffff, + plsp_id); + CU_ASSERT_EQUAL((lsp->header.encoded_object[7] >> 4) & 0x07, status); + CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_A); + CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_C); + CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_D); + CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_R); + CU_ASSERT_TRUE(lsp->header.encoded_object[7] & OBJECT_LSP_FLAG_S); + + pcep_obj_free_object((struct pcep_object_header *)lsp); +} + +void test_pcep_obj_create_vendor_info() +{ + uint32_t enterprise_number = 0x01020304; + uint32_t enterprise_specific_info = 0x05060708; + + struct pcep_object_vendor_info *obj = pcep_obj_create_vendor_info( + enterprise_number, enterprise_specific_info); + + CU_ASSERT_PTR_NOT_NULL(obj); + pcep_encode_object(&obj->header, versioning, object_buf); + verify_pcep_obj_header(PCEP_OBJ_CLASS_VENDOR_INFO, + PCEP_OBJ_TYPE_VENDOR_INFO, &obj->header); + uint32_t *uint32_ptr = (uint32_t *)(obj->header.encoded_object + 4); + CU_ASSERT_EQUAL(uint32_ptr[0], htonl(enterprise_number)); + CU_ASSERT_EQUAL(uint32_ptr[1], htonl(enterprise_specific_info)); + + pcep_obj_free_object((struct pcep_object_header *)obj); +} + +/* Internal test function. The only difference between pcep_obj_create_ero(), + * pcep_obj_create_iro(), and pcep_obj_create_rro() is the object_class + * and the object_type. + */ +typedef struct pcep_object_ro *(*ro_func)(double_linked_list *); +static void test_pcep_obj_create_object_common(ro_func func_to_test, + uint8_t object_class, + uint8_t object_type) +{ + double_linked_list *ero_list = dll_initialize(); + + struct pcep_object_ro *ero = func_to_test(NULL); + CU_ASSERT_PTR_NOT_NULL(ero); + assert(ero != NULL); + pcep_encode_object(&ero->header, versioning, object_buf); + verify_pcep_obj_header2(object_class, object_type, OBJECT_HEADER_LENGTH, + ero->header.encoded_object); + pcep_obj_free_object((struct pcep_object_header *)ero); + + reset_objects_buffer(); + ero = func_to_test(ero_list); + CU_ASSERT_PTR_NOT_NULL(ero); + assert(ero != NULL); + pcep_encode_object(&ero->header, versioning, object_buf); + verify_pcep_obj_header2(object_class, object_type, OBJECT_HEADER_LENGTH, + ero->header.encoded_object); + pcep_obj_free_object((struct pcep_object_header *)ero); + + reset_objects_buffer(); + struct pcep_ro_subobj_32label *ro_subobj = + pcep_obj_create_ro_subobj_32label(false, 0, 101); + ero_list = dll_initialize(); + dll_append(ero_list, ro_subobj); + ero = func_to_test(ero_list); + CU_ASSERT_PTR_NOT_NULL(ero); + assert(ero != NULL); + pcep_encode_object(&ero->header, versioning, object_buf); + /* 4 bytes for obj header + + * 2 bytes for ro_subobj header + + * 2 bytes for lable c-type and flags + + * 4 bytes for label */ + verify_pcep_obj_header2(object_class, object_type, + OBJECT_HEADER_LENGTH + sizeof(uint32_t) * 2, + ero->header.encoded_object); + pcep_obj_free_object((struct pcep_object_header *)ero); +} + +void test_pcep_obj_create_ero() +{ + test_pcep_obj_create_object_common( + pcep_obj_create_ero, PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO); +} + +void test_pcep_obj_create_rro() +{ + test_pcep_obj_create_object_common( + pcep_obj_create_rro, PCEP_OBJ_CLASS_RRO, PCEP_OBJ_TYPE_RRO); +} + +void test_pcep_obj_create_iro() +{ + test_pcep_obj_create_object_common( + pcep_obj_create_iro, PCEP_OBJ_CLASS_IRO, PCEP_OBJ_TYPE_IRO); +} + +/* Internal util function to wrap an RO Subobj in a RO and encode it */ +static struct pcep_object_ro *encode_ro_subobj(struct pcep_object_ro_subobj *sr) +{ + double_linked_list *sr_subobj_list = dll_initialize(); + dll_append(sr_subobj_list, sr); + struct pcep_object_ro *ro = pcep_obj_create_ero(sr_subobj_list); + pcep_encode_object(&ro->header, versioning, object_buf); + + return ro; +} + +static void verify_pcep_obj_ro_header(struct pcep_object_ro *ro, + struct pcep_object_ro_subobj *ro_subobj, + uint8_t ro_subobj_type, bool loose_hop, + uint16_t length) +{ + (void)ro_subobj; + + verify_pcep_obj_header2(PCEP_OBJ_CLASS_ERO, PCEP_OBJ_TYPE_ERO, length, + ro->header.encoded_object); + + /* TODO consider printing the stack trace: + * https://stackoverflow.com/questions/105659/how-can-one-grab-a-stack-trace-in-c + */ + + /* Not using CU_ASSERT_EQUAL here, so that in case of failure, + * we can provide more info in the error message. */ + uint8_t found_type = (ro->header.encoded_object[4] + & 0x7f); /* remove the Loose hop bit */ + if (found_type != ro_subobj_type) { + fprintf(stderr, + "Test failure ro_sub_obj_type expected [%d] found [%d]\n", + ro_subobj_type, found_type); + CU_FAIL("Sub Object Header Type"); + } + + bool loose_hop_found = (ro->header.encoded_object[4] & 0x80); + if (loose_hop != loose_hop_found) { + fprintf(stderr, + "Test failure ro_sub_obj Loose Hop bit expected [%d] found [%d]\n", + loose_hop, loose_hop_found); + CU_FAIL("Sub Object Header Loose Hop bit"); + } + + if (length - 4 != ro->header.encoded_object[5]) { + fprintf(stderr, + "Test failure ro_sub_obj length expected [%d] found [%d]\n", + length - 4, ro->header.encoded_object[5]); + CU_FAIL("Sub Object Length"); + } +} + +static void +verify_pcep_obj_ro_sr_header(struct pcep_object_ro *ro, + struct pcep_object_ro_subobj *ro_subobj, + uint8_t nai_type, bool loose_hop, uint16_t length) +{ + verify_pcep_obj_ro_header(ro, ro_subobj, RO_SUBOBJ_TYPE_SR, loose_hop, + length); + uint8_t found_nai_type = ((ro->header.encoded_object[6] >> 4) & 0x0f); + if (nai_type != found_nai_type) { + fprintf(stderr, + "Test failure ro_sr_sub_obj nai_type expected [%d] found [%d]\n", + nai_type, found_nai_type); + CU_FAIL("Sub Object SR NAI Type"); + } +} + +void test_pcep_obj_create_ro_subobj_ipv4() +{ + struct in_addr ro_ipv4; + inet_pton(AF_INET, "192.168.1.2", &ro_ipv4); + uint8_t prefix_len = 8; + + struct pcep_ro_subobj_ipv4 *ipv4 = + pcep_obj_create_ro_subobj_ipv4(true, NULL, prefix_len, false); + CU_ASSERT_PTR_NULL(ipv4); + + ipv4 = pcep_obj_create_ro_subobj_ipv4(false, &ro_ipv4, prefix_len, + true); + CU_ASSERT_PTR_NOT_NULL(ipv4); + struct pcep_object_ro *ro = encode_ro_subobj(&ipv4->ro_subobj); + verify_pcep_obj_ro_header(ro, &ipv4->ro_subobj, RO_SUBOBJ_TYPE_IPV4, + false, sizeof(uint32_t) * 3); + CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 6)), + ro_ipv4.s_addr); + CU_ASSERT_EQUAL(ro->header.encoded_object[10], prefix_len); + CU_ASSERT_TRUE(ro->header.encoded_object[11] + & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT); + pcep_obj_free_object((struct pcep_object_header *)ro); + + reset_objects_buffer(); + ipv4 = pcep_obj_create_ro_subobj_ipv4(true, &ro_ipv4, prefix_len, + false); + CU_ASSERT_PTR_NOT_NULL(ipv4); + ro = encode_ro_subobj(&ipv4->ro_subobj); + verify_pcep_obj_ro_header(ro, &ipv4->ro_subobj, RO_SUBOBJ_TYPE_IPV4, + true, sizeof(uint32_t) * 3); + CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 6)), + ro_ipv4.s_addr); + CU_ASSERT_EQUAL(ro->header.encoded_object[10], prefix_len); + CU_ASSERT_EQUAL(ro->header.encoded_object[11], 0); + pcep_obj_free_object((struct pcep_object_header *)ro); +} + +void test_pcep_obj_create_ro_subobj_ipv6() +{ + struct in6_addr ro_ipv6; + uint8_t prefix_len = 16; + + struct pcep_ro_subobj_ipv6 *ipv6 = + pcep_obj_create_ro_subobj_ipv6(true, NULL, prefix_len, true); + CU_ASSERT_PTR_NULL(ipv6); + + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &ro_ipv6); + ipv6 = pcep_obj_create_ro_subobj_ipv6(false, &ro_ipv6, prefix_len, + true); + CU_ASSERT_PTR_NOT_NULL(ipv6); + struct pcep_object_ro *ro = encode_ro_subobj(&ipv6->ro_subobj); + verify_pcep_obj_ro_header(ro, &ipv6->ro_subobj, RO_SUBOBJ_TYPE_IPV6, + false, sizeof(uint32_t) * 6); + uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 6); + CU_ASSERT_EQUAL(uint32_ptr[0], ro_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[1], ro_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[2], ro_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[3], ro_ipv6.__in6_u.__u6_addr32[3]); + CU_ASSERT_EQUAL(ro->header.encoded_object[22], prefix_len); + CU_ASSERT_TRUE(ro->header.encoded_object[23] + & OBJECT_SUBOBJ_IP_FLAG_LOCAL_PROT); + pcep_obj_free_object((struct pcep_object_header *)ro); + + reset_objects_buffer(); + ipv6 = pcep_obj_create_ro_subobj_ipv6(true, &ro_ipv6, prefix_len, + false); + CU_ASSERT_PTR_NOT_NULL(ipv6); + ro = encode_ro_subobj(&ipv6->ro_subobj); + verify_pcep_obj_ro_header(ro, &ipv6->ro_subobj, RO_SUBOBJ_TYPE_IPV6, + true, sizeof(uint32_t) * 6); + uint32_ptr = (uint32_t *)(ro->header.encoded_object + 6); + CU_ASSERT_EQUAL(uint32_ptr[0], ro_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[1], ro_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[2], ro_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[3], ro_ipv6.__in6_u.__u6_addr32[3]); + CU_ASSERT_EQUAL(ro->header.encoded_object[22], prefix_len); + CU_ASSERT_EQUAL(ro->header.encoded_object[23], 0); + pcep_obj_free_object((struct pcep_object_header *)ro); +} + +void test_pcep_obj_create_ro_subobj_unnum() +{ + struct in_addr router_id; + uint32_t if_id = 123; + + struct pcep_ro_subobj_unnum *unnum = + pcep_obj_create_ro_subobj_unnum(NULL, if_id); + CU_ASSERT_PTR_NULL(unnum); + + inet_pton(AF_INET, "192.168.1.2", &router_id); + unnum = pcep_obj_create_ro_subobj_unnum(&router_id, if_id); + CU_ASSERT_PTR_NOT_NULL(unnum); + struct pcep_object_ro *ro = encode_ro_subobj(&unnum->ro_subobj); + verify_pcep_obj_ro_header(ro, &unnum->ro_subobj, RO_SUBOBJ_TYPE_UNNUM, + false, sizeof(uint32_t) * 4); + CU_ASSERT_EQUAL(ro->header.encoded_object[6], 0); + CU_ASSERT_EQUAL(ro->header.encoded_object[7], 0); + CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)), + router_id.s_addr); + CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 12)), + htonl(if_id)); + + pcep_obj_free_object((struct pcep_object_header *)ro); +} + +void test_pcep_obj_create_ro_subobj_32label() +{ + uint8_t class_type = 1; + uint32_t label = 0xeeffaabb; + + struct pcep_ro_subobj_32label *label32 = + pcep_obj_create_ro_subobj_32label(true, class_type, label); + CU_ASSERT_PTR_NOT_NULL(label32); + struct pcep_object_ro *ro = encode_ro_subobj(&label32->ro_subobj); + verify_pcep_obj_ro_header(ro, &label32->ro_subobj, RO_SUBOBJ_TYPE_LABEL, + false, sizeof(uint32_t) * 3); + CU_ASSERT_TRUE(ro->header.encoded_object[6] + & OBJECT_SUBOBJ_LABEL_FLAG_GLOGAL); + CU_ASSERT_EQUAL(ro->header.encoded_object[7], class_type); + CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)), + htonl(label)); + + pcep_obj_free_object((struct pcep_object_header *)ro); +} + +void test_pcep_obj_create_ro_subobj_asn() +{ + uint16_t asn = 0x0102; + + struct pcep_ro_subobj_asn *asn_obj = pcep_obj_create_ro_subobj_asn(asn); + CU_ASSERT_PTR_NOT_NULL(asn_obj); + struct pcep_object_ro *ro = encode_ro_subobj(&asn_obj->ro_subobj); + verify_pcep_obj_ro_header(ro, &asn_obj->ro_subobj, RO_SUBOBJ_TYPE_ASN, + false, sizeof(uint32_t) * 2); + CU_ASSERT_EQUAL(*((uint16_t *)(ro->header.encoded_object + 6)), + htons(asn)); + + pcep_obj_free_object((struct pcep_object_header *)ro); +} + +void test_pcep_obj_create_ro_subobj_sr_nonai() +{ + uint32_t sid = 0x01020304; + + struct pcep_ro_subobj_sr *sr = + pcep_obj_create_ro_subobj_sr_nonai(false, sid, false, false); + CU_ASSERT_PTR_NOT_NULL(sr); + struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_ABSENT, false, + sizeof(uint32_t) * 3); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_F); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M); + pcep_obj_free_object((struct pcep_object_header *)ro); + + reset_objects_buffer(); + sr = pcep_obj_create_ro_subobj_sr_nonai(true, sid, true, true); + CU_ASSERT_PTR_NOT_NULL(sr); + ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_ABSENT, true, + sizeof(uint32_t) * 3); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_F); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M); + pcep_obj_free_object((struct pcep_object_header *)ro); +} + +void test_pcep_obj_create_ro_subobj_sr_ipv4_node() +{ + uint32_t sid = 0x01020304; + struct in_addr ipv4_node_id; + inet_pton(AF_INET, "192.168.1.2", &ipv4_node_id); + + /* (loose_hop, sid_absent, c_flag, m_flag, sid, ipv4_node_id) */ + struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv4_node( + true, false, true, true, sid, NULL); + CU_ASSERT_PTR_NULL(sr); + + /* Test the sid is absent */ + sr = pcep_obj_create_ro_subobj_sr_ipv4_node(true, true, false, false, + sid, &ipv4_node_id); + CU_ASSERT_PTR_NOT_NULL(sr); + assert(sr != NULL); + struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_IPV4_NODE, true, + sizeof(uint32_t) * 3); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M); + CU_ASSERT_EQUAL(sr->sid, 0); + CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)), + ipv4_node_id.s_addr); + pcep_obj_free_object((struct pcep_object_header *)ro); + + /* Test the sid is present */ + reset_objects_buffer(); + inet_pton(AF_INET, "192.168.1.2", &ipv4_node_id); + sr = pcep_obj_create_ro_subobj_sr_ipv4_node(false, false, true, true, + sid, &ipv4_node_id); + CU_ASSERT_PTR_NOT_NULL(sr); + assert(sr != NULL); + ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_IPV4_NODE, false, + sizeof(uint32_t) * 4); + assert(ro != NULL); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 8)), + htonl(sid)); + CU_ASSERT_EQUAL(*((uint32_t *)(ro->header.encoded_object + 12)), + ipv4_node_id.s_addr); + pcep_obj_free_object((struct pcep_object_header *)ro); +} + +void test_pcep_obj_create_ro_subobj_sr_ipv6_node() +{ + uint32_t sid = 0x01020304; + struct in6_addr ipv6_node_id; + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &ipv6_node_id); + + /* (loose_hop, sid_absent, c_flag, m_flag, sid, ipv6_node_id) */ + struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv6_node( + false, true, true, true, sid, NULL); + CU_ASSERT_PTR_NULL(sr); + + /* Test the sid is absent */ + sr = pcep_obj_create_ro_subobj_sr_ipv6_node(true, true, true, true, sid, + &ipv6_node_id); + CU_ASSERT_PTR_NOT_NULL(sr); + struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_IPV6_NODE, true, + sizeof(uint32_t) * 6); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M); + uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8); + CU_ASSERT_EQUAL(uint32_ptr[0], ipv6_node_id.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[1], ipv6_node_id.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[2], ipv6_node_id.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[3], ipv6_node_id.__in6_u.__u6_addr32[3]); + pcep_obj_free_object((struct pcep_object_header *)ro); + + /* Test the sid is present */ + reset_objects_buffer(); + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &ipv6_node_id); + sr = pcep_obj_create_ro_subobj_sr_ipv6_node(false, false, true, true, + sid, &ipv6_node_id); + CU_ASSERT_PTR_NOT_NULL(sr); + ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_IPV6_NODE, false, + sizeof(uint32_t) * 7); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8); + CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid)); + CU_ASSERT_EQUAL(uint32_ptr[1], ipv6_node_id.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[2], ipv6_node_id.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[3], ipv6_node_id.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[4], ipv6_node_id.__in6_u.__u6_addr32[3]); + pcep_obj_free_object((struct pcep_object_header *)ro); +} + +void test_pcep_obj_create_ro_subobj_sr_ipv4_adj() +{ + struct in_addr local_ipv4; + struct in_addr remote_ipv4; + inet_pton(AF_INET, "192.168.1.2", &local_ipv4); + inet_pton(AF_INET, "172.168.1.2", &remote_ipv4); + + uint32_t sid = ENCODE_SR_ERO_SID(3, 7, 0, 188); + CU_ASSERT_EQUAL(sid, 16060); + + /* (loose_hop, sid_absent, c_flag, m_flag, sid, local_ipv4, remote_ipv4) + */ + struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv4_adj( + false, true, true, true, sid, NULL, NULL); + CU_ASSERT_PTR_NULL(sr); + + sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(false, true, true, true, sid, + &local_ipv4, NULL); + CU_ASSERT_PTR_NULL(sr); + + sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(false, true, true, true, sid, + NULL, &remote_ipv4); + CU_ASSERT_PTR_NULL(sr); + + /* Test the sid is absent */ + sr = pcep_obj_create_ro_subobj_sr_ipv4_adj(true, true, true, true, sid, + &local_ipv4, &remote_ipv4); + CU_ASSERT_PTR_NOT_NULL(sr); + struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, true, + sizeof(uint32_t) * 4); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M); + assert(sr != NULL); + CU_ASSERT_EQUAL(sr->sid, 0); + uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8); + CU_ASSERT_EQUAL(uint32_ptr[0], local_ipv4.s_addr); + CU_ASSERT_EQUAL(uint32_ptr[1], remote_ipv4.s_addr); + pcep_obj_free_object((struct pcep_object_header *)ro); + + /* Test the sid is present */ + inet_pton(AF_INET, "192.168.1.2", &local_ipv4); + inet_pton(AF_INET, "172.168.1.2", &remote_ipv4); + reset_objects_buffer(); + sr = pcep_obj_create_ro_subobj_sr_ipv4_adj( + false, false, true, true, sid, &local_ipv4, &remote_ipv4); + CU_ASSERT_PTR_NOT_NULL(sr); + ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_IPV4_ADJACENCY, false, + sizeof(uint32_t) * 5); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8); + CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid)); + CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv4.s_addr); + CU_ASSERT_EQUAL(uint32_ptr[2], remote_ipv4.s_addr); + pcep_obj_free_object((struct pcep_object_header *)ro); +} + +void test_pcep_obj_create_ro_subobj_sr_ipv6_adj() +{ + uint32_t sid = 0x01020304; + struct in6_addr local_ipv6; + struct in6_addr remote_ipv6; + inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6); + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6); + + /* (loose_hop, sid_absent, c_flag, m_flag, sid, local_ipv6, remote_ipv6) + */ + struct pcep_ro_subobj_sr *sr = pcep_obj_create_ro_subobj_sr_ipv6_adj( + false, true, true, true, sid, NULL, NULL); + CU_ASSERT_PTR_NULL(sr); + + sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(false, true, true, true, sid, + &local_ipv6, NULL); + CU_ASSERT_PTR_NULL(sr); + + sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(false, true, true, true, sid, + NULL, &remote_ipv6); + CU_ASSERT_PTR_NULL(sr); + + /* Test the sid is absent */ + sr = pcep_obj_create_ro_subobj_sr_ipv6_adj(true, true, true, true, sid, + &local_ipv6, &remote_ipv6); + CU_ASSERT_PTR_NOT_NULL(sr); + assert(sr != NULL); + struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, true, + sizeof(uint32_t) * 10); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M); + CU_ASSERT_EQUAL(sr->sid, 0); + uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8); + CU_ASSERT_EQUAL(uint32_ptr[0], local_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[3]); + + CU_ASSERT_EQUAL(uint32_ptr[4], remote_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[5], remote_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[3]); + pcep_obj_free_object((struct pcep_object_header *)ro); + + /* Test the sid is present */ + reset_objects_buffer(); + inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6); + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6); + sr = pcep_obj_create_ro_subobj_sr_ipv6_adj( + false, false, true, false, sid, &local_ipv6, &remote_ipv6); + CU_ASSERT_PTR_NOT_NULL(sr); + ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header(ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_IPV6_ADJACENCY, false, + sizeof(uint32_t) * 11); + /* All flags are false */ + CU_ASSERT_EQUAL(ro->header.encoded_object[7], 0); + uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8); + CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid)); + CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[4], local_ipv6.__in6_u.__u6_addr32[3]); + + CU_ASSERT_EQUAL(uint32_ptr[5], remote_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[8], remote_ipv6.__in6_u.__u6_addr32[3]); + pcep_obj_free_object((struct pcep_object_header *)ro); +} + +void test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj() +{ + uint32_t sid = 0x01020304; + uint32_t local_node_id = 0x11223344; + uint32_t local_if_id = 0x55667788; + uint32_t remote_node_id = 0x99aabbcc; + uint32_t remote_if_id = 0xddeeff11; + + /* (loose_hop, sid_absent, c_flag, m_flag, + sid, local_node_id, local_if_id, remote_node_id, remote_if_id) */ + + /* Test the sid is absent */ + struct pcep_ro_subobj_sr *sr = + pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj( + true, true, true, true, sid, local_node_id, local_if_id, + remote_node_id, remote_if_id); + CU_ASSERT_PTR_NOT_NULL(sr); + struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header( + ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, true, + sizeof(uint32_t) * 6); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M); + assert(sr != NULL); + CU_ASSERT_EQUAL(sr->sid, 0); + uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8); + CU_ASSERT_EQUAL(uint32_ptr[0], local_node_id); + CU_ASSERT_EQUAL(uint32_ptr[1], local_if_id); + CU_ASSERT_EQUAL(uint32_ptr[2], remote_node_id); + CU_ASSERT_EQUAL(uint32_ptr[3], remote_if_id); + pcep_obj_free_object((struct pcep_object_header *)ro); + + /* Test the sid is present */ + reset_objects_buffer(); + sr = pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj( + false, false, true, true, sid, local_node_id, local_if_id, + remote_node_id, remote_if_id); + CU_ASSERT_PTR_NOT_NULL(sr); + ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header( + ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY, false, + sizeof(uint32_t) * 7); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8); + CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid)); + CU_ASSERT_EQUAL(uint32_ptr[1], local_node_id); + CU_ASSERT_EQUAL(uint32_ptr[2], local_if_id); + CU_ASSERT_EQUAL(uint32_ptr[3], remote_node_id); + CU_ASSERT_EQUAL(uint32_ptr[4], remote_if_id); + pcep_obj_free_object((struct pcep_object_header *)ro); + + /* TODO Test draft07 types */ +} + +void test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj() +{ + uint32_t sid = 0x01020304; + uint32_t local_if_id = 0x11002200; + uint32_t remote_if_id = 0x00110022; + struct in6_addr local_ipv6; + struct in6_addr remote_ipv6; + inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6); + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6); + + /* (loose_hop, sid_absent, c_flag, m_flag, sid, local_ipv6, local_if_id, + * remote_ipv6, remote_if_id */ + struct pcep_ro_subobj_sr *sr = + pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj( + false, true, true, true, sid, NULL, local_if_id, NULL, + remote_if_id); + CU_ASSERT_PTR_NULL(sr); + + sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj( + false, true, true, true, sid, &local_ipv6, local_if_id, NULL, + remote_if_id); + CU_ASSERT_PTR_NULL(sr); + + sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj( + false, true, true, true, sid, NULL, local_if_id, &remote_ipv6, + remote_if_id); + CU_ASSERT_PTR_NULL(sr); + + /* Test the sid is absent */ + sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj( + true, true, true, true, sid, &local_ipv6, local_if_id, + &remote_ipv6, remote_if_id); + CU_ASSERT_PTR_NOT_NULL(sr); + struct pcep_object_ro *ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header( + ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, true, + sizeof(uint32_t) * 12); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_M); + assert(sr != NULL); + CU_ASSERT_EQUAL(sr->sid, 0); + uint32_t *uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8); + CU_ASSERT_EQUAL(uint32_ptr[0], local_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[3]); + CU_ASSERT_EQUAL(uint32_ptr[4], local_if_id); + + CU_ASSERT_EQUAL(uint32_ptr[5], remote_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[8], remote_ipv6.__in6_u.__u6_addr32[3]); + CU_ASSERT_EQUAL(uint32_ptr[9], remote_if_id); + pcep_obj_free_object((struct pcep_object_header *)ro); + + /* Test the sid is present */ + inet_pton(AF_INET6, "2001:db8::8a2e:370:8221", &local_ipv6); + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &remote_ipv6); + reset_objects_buffer(); + sr = pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj( + false, false, true, true, sid, &local_ipv6, local_if_id, + &remote_ipv6, remote_if_id); + CU_ASSERT_PTR_NOT_NULL(sr); + ro = encode_ro_subobj(&sr->ro_subobj); + verify_pcep_obj_ro_sr_header( + ro, &sr->ro_subobj, + PCEP_SR_SUBOBJ_NAI_LINK_LOCAL_IPV6_ADJACENCY, false, + sizeof(uint32_t) * 13); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_C); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & OBJECT_SUBOBJ_SR_FLAG_M); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_S); + CU_ASSERT_TRUE(ro->header.encoded_object[7] & ~OBJECT_SUBOBJ_SR_FLAG_F); + uint32_ptr = (uint32_t *)(ro->header.encoded_object + 8); + CU_ASSERT_EQUAL(uint32_ptr[0], htonl(sid)); + CU_ASSERT_EQUAL(uint32_ptr[1], local_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[2], local_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[3], local_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[4], local_ipv6.__in6_u.__u6_addr32[3]); + CU_ASSERT_EQUAL(uint32_ptr[5], local_if_id); + + CU_ASSERT_EQUAL(uint32_ptr[6], remote_ipv6.__in6_u.__u6_addr32[0]); + CU_ASSERT_EQUAL(uint32_ptr[7], remote_ipv6.__in6_u.__u6_addr32[1]); + CU_ASSERT_EQUAL(uint32_ptr[8], remote_ipv6.__in6_u.__u6_addr32[2]); + CU_ASSERT_EQUAL(uint32_ptr[9], remote_ipv6.__in6_u.__u6_addr32[3]); + CU_ASSERT_EQUAL(uint32_ptr[10], remote_if_id); + pcep_obj_free_object((struct pcep_object_header *)ro); +} diff --git a/pceplib/test/pcep_msg_objects_test.h b/pceplib/test/pcep_msg_objects_test.h new file mode 100644 index 0000000..0f08193 --- /dev/null +++ b/pceplib/test/pcep_msg_objects_test.h @@ -0,0 +1,64 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + + +#ifndef PCEP_MSG_OBJECTS_TEST_H_ +#define PCEP_MSG_OBJECTS_TEST_H_ + +int pcep_objects_test_suite_setup(void); +int pcep_objects_test_suite_teardown(void); +void pcep_objects_test_setup(void); +void pcep_objects_test_teardown(void); +void test_pcep_obj_create_open(void); +void test_pcep_obj_create_open_with_tlvs(void); +void test_pcep_obj_create_rp(void); +void test_pcep_obj_create_nopath(void); +void test_pcep_obj_create_endpoint_ipv4(void); +void test_pcep_obj_create_endpoint_ipv6(void); +void test_pcep_obj_create_association_ipv4(void); +void test_pcep_obj_create_association_ipv6(void); +void test_pcep_obj_create_bandwidth(void); +void test_pcep_obj_create_metric(void); +void test_pcep_obj_create_lspa(void); +void test_pcep_obj_create_svec(void); +void test_pcep_obj_create_error(void); +void test_pcep_obj_create_close(void); +void test_pcep_obj_create_srp(void); +void test_pcep_obj_create_lsp(void); +void test_pcep_obj_create_vendor_info(void); +void test_pcep_obj_create_ero(void); +void test_pcep_obj_create_rro(void); +void test_pcep_obj_create_iro(void); +void test_pcep_obj_create_ro_subobj_ipv4(void); +void test_pcep_obj_create_ro_subobj_ipv6(void); +void test_pcep_obj_create_ro_subobj_unnum(void); +void test_pcep_obj_create_ro_subobj_32label(void); +void test_pcep_obj_create_ro_subobj_asn(void); +void test_pcep_obj_create_ro_subobj_sr_nonai(void); +void test_pcep_obj_create_ro_subobj_sr_ipv4_node(void); +void test_pcep_obj_create_ro_subobj_sr_ipv6_node(void); +void test_pcep_obj_create_ro_subobj_sr_ipv4_adj(void); +void test_pcep_obj_create_ro_subobj_sr_ipv6_adj(void); +void test_pcep_obj_create_ro_subobj_sr_unnumbered_ipv4_adj(void); +void test_pcep_obj_create_ro_subobj_sr_linklocal_ipv6_adj(void); + +#endif diff --git a/pceplib/test/pcep_msg_tests_valgrind.sh b/pceplib/test/pcep_msg_tests_valgrind.sh new file mode 100755 index 0000000..4a9a999 --- /dev/null +++ b/pceplib/test/pcep_msg_tests_valgrind.sh @@ -0,0 +1,2 @@ +source pceplib/test/pcep_tests_valgrind.sh +valgrind_test pceplib/test/pcep_msg_tests diff --git a/pceplib/test/pcep_msg_tlvs_test.c b/pceplib/test/pcep_msg_tlvs_test.c new file mode 100644 index 0000000..888925f --- /dev/null +++ b/pceplib/test/pcep_msg_tlvs_test.c @@ -0,0 +1,725 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __FreeBSD__ +#include <sys/endian.h> +#else +#include <endian.h> +#endif /* __FreeBSD__ */ +#include <assert.h> +#include <stdlib.h> + +#include <CUnit/CUnit.h> + +#include "pcep_msg_encoding.h" +#include "pcep_msg_objects.h" +#include "pcep_msg_tlvs.h" +#include "pcep_msg_tools.h" +#include "pcep_utils_memory.h" +#include "pcep_msg_tlvs_test.h" + +/* + * Notice: + * All of these TLV Unit Tests encode the created TLVs by explicitly calling + * pcep_encode_tlv() thus testing the TLV creation and the TLV encoding. + * All APIs expect IPs to be in network byte order. + */ + +static struct pcep_versioning *versioning = NULL; +static uint8_t tlv_buf[2000]; + +void reset_tlv_buffer(void); + +int pcep_tlvs_test_suite_setup(void) +{ + pceplib_memory_reset(); + return 0; +} + +int pcep_tlvs_test_suite_teardown(void) +{ + printf("\n"); + pceplib_memory_dump(); + return 0; +} + +void reset_tlv_buffer() +{ + memset(tlv_buf, 0, 2000); +} + +void pcep_tlvs_test_setup() +{ + versioning = create_default_pcep_versioning(); + reset_tlv_buffer(); +} + +void pcep_tlvs_test_teardown() +{ + destroy_pcep_versioning(versioning); +} + +void test_pcep_tlv_create_stateful_pce_capability() +{ + struct pcep_object_tlv_stateful_pce_capability *tlv = + pcep_tlv_create_stateful_pce_capability(true, true, true, true, + true, true); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, + PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t)); + CU_ASSERT_TRUE(tlv->flag_u_lsp_update_capability); + CU_ASSERT_TRUE(tlv->flag_s_include_db_version); + CU_ASSERT_TRUE(tlv->flag_i_lsp_instantiation_capability); + CU_ASSERT_TRUE(tlv->flag_t_triggered_resync); + CU_ASSERT_TRUE(tlv->flag_d_delta_lsp_sync); + CU_ASSERT_TRUE(tlv->flag_f_triggered_initial_sync); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[7], 0x3f); + /* TODO add a new function: verify_tlv_header(tlv->header.encoded_tlv) + * to all tests */ + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_speaker_entity_id() +{ + struct pcep_object_tlv_speaker_entity_identifier *tlv = + pcep_tlv_create_speaker_entity_id(NULL); + CU_ASSERT_PTR_NULL(tlv); + + double_linked_list *list = dll_initialize(); + tlv = pcep_tlv_create_speaker_entity_id(list); + CU_ASSERT_PTR_NULL(tlv); + if (tlv != NULL) { + pceplib_free(PCEPLIB_INFRA, tlv); + tlv = NULL; + } + + uint32_t *speaker_entity = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(uint32_t)); + *speaker_entity = 42; + dll_append(list, speaker_entity); + tlv = pcep_tlv_create_speaker_entity_id(list); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t)); + CU_ASSERT_PTR_NOT_NULL(tlv->speaker_entity_id_list); + assert(tlv->speaker_entity_id_list != NULL); + CU_ASSERT_EQUAL(tlv->speaker_entity_id_list->num_entries, 1); + uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint32_ptr[1], htonl(*speaker_entity)); + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_lsp_db_version() +{ + uint64_t lsp_db_version = 0xf005ba11ba5eba11; + struct pcep_object_tlv_lsp_db_version *tlv = + pcep_tlv_create_lsp_db_version(lsp_db_version); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint64_t)); + CU_ASSERT_EQUAL(tlv->lsp_db_version, lsp_db_version); + CU_ASSERT_EQUAL(*((uint64_t *)(tlv->header.encoded_tlv + 4)), + be64toh(lsp_db_version)); + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_path_setup_type() +{ + uint8_t pst = 0x89; + + struct pcep_object_tlv_path_setup_type *tlv = + pcep_tlv_create_path_setup_type(pst); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t)); + CU_ASSERT_EQUAL(tlv->path_setup_type, pst); + uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x000000FF & pst)); + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_path_setup_type_capability() +{ + /* The sub_tlv list is optional */ + + /* Should return NULL if pst_list is NULL */ + struct pcep_object_tlv_path_setup_type_capability *tlv = + pcep_tlv_create_path_setup_type_capability(NULL, NULL); + CU_ASSERT_PTR_NULL(tlv); + + /* Should return NULL if pst_list is empty */ + double_linked_list *pst_list = dll_initialize(); + tlv = pcep_tlv_create_path_setup_type_capability(pst_list, NULL); + CU_ASSERT_PTR_NULL(tlv); + if (tlv != NULL) { + pcep_obj_free_tlv(&tlv->header); + tlv = NULL; + } + + /* Should still return NULL if pst_list is NULL */ + double_linked_list *sub_tlv_list = dll_initialize(); + tlv = pcep_tlv_create_path_setup_type_capability(NULL, sub_tlv_list); + CU_ASSERT_PTR_NULL(tlv); + if (tlv != NULL) { + pcep_obj_free_tlv(&tlv->header); + tlv = NULL; + } + + /* Should still return NULL if pst_list is empty */ + tlv = pcep_tlv_create_path_setup_type_capability(pst_list, + sub_tlv_list); + CU_ASSERT_PTR_NULL(tlv); + if (tlv != NULL) { + pcep_obj_free_tlv(&tlv->header); + tlv = NULL; + } + + /* Test only populating the pst list */ + uint8_t *pst1 = pceplib_malloc(PCEPLIB_MESSAGES, 1); + uint8_t *pst2 = pceplib_malloc(PCEPLIB_MESSAGES, 1); + uint8_t *pst3 = pceplib_malloc(PCEPLIB_MESSAGES, 1); + *pst1 = 1; + *pst2 = 2; + *pst3 = 3; + dll_append(pst_list, pst1); + dll_append(pst_list, pst2); + dll_append(pst_list, pst3); + tlv = pcep_tlv_create_path_setup_type_capability(pst_list, + sub_tlv_list); + CU_ASSERT_PTR_NOT_NULL(tlv); + if (tlv == NULL) { + CU_ASSERT_TRUE(tlv != NULL); + return; + } + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t) * 2); + CU_ASSERT_PTR_NOT_NULL(tlv->pst_list); + assert(tlv != NULL); + CU_ASSERT_EQUAL(tlv->pst_list->num_entries, 3); + uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x00000003)); + CU_ASSERT_EQUAL(uint32_ptr[2], htonl(0x01020300)); + pcep_obj_free_tlv(&tlv->header); + + /* Now test populating both the pst_list and the sub_tlv_list */ + reset_tlv_buffer(); + struct pcep_object_tlv_header *sub_tlv = + (struct pcep_object_tlv_header *) + pcep_tlv_create_sr_pce_capability(true, true, 0); + pst_list = dll_initialize(); + sub_tlv_list = dll_initialize(); + pst1 = pceplib_malloc(PCEPLIB_MESSAGES, 1); + *pst1 = 1; + dll_append(pst_list, pst1); + dll_append(sub_tlv_list, sub_tlv); + tlv = pcep_tlv_create_path_setup_type_capability(pst_list, + sub_tlv_list); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, + sizeof(uint32_t) * 2 + TLV_HEADER_LENGTH + + sub_tlv->encoded_tlv_length); + CU_ASSERT_PTR_NOT_NULL(tlv->pst_list); + CU_ASSERT_PTR_NOT_NULL(tlv->sub_tlv_list); + uint32_ptr = (uint32_t *)tlv->header.encoded_tlv; + uint16_t *uint16_ptr = (uint16_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint16_ptr[0], + htons(PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY)); + CU_ASSERT_EQUAL(uint16_ptr[1], htons(tlv->header.encoded_tlv_length)); + CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x00000001)); + CU_ASSERT_EQUAL(uint32_ptr[2], htonl(0x01000000)); + /* Verify the Sub-TLV */ + uint16_ptr = (uint16_t *)(tlv->header.encoded_tlv + 12); + CU_ASSERT_EQUAL(uint16_ptr[0], + htons(PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY)); + CU_ASSERT_EQUAL(uint16_ptr[1], htons(4)); + CU_ASSERT_EQUAL(uint16_ptr[2], 0); + CU_ASSERT_EQUAL(uint16_ptr[3], htons(0x0300)); + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_sr_pce_capability() +{ + struct pcep_object_tlv_sr_pce_capability *tlv = + pcep_tlv_create_sr_pce_capability(true, true, 8); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t)); + uint16_t *uint16_ptr = (uint16_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint16_ptr[0], + htons(PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY)); + CU_ASSERT_EQUAL(uint16_ptr[1], htons(tlv->header.encoded_tlv_length)); + uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint32_ptr[1], htonl(0x00000308)); + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_symbolic_path_name() +{ + /* char *symbolic_path_name, uint16_t symbolic_path_name_length); */ + char path_name[16] = "Some Path Name"; + uint16_t path_name_length = 14; + struct pcep_object_tlv_symbolic_path_name *tlv = + pcep_tlv_create_symbolic_path_name(path_name, path_name_length); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, path_name_length); + /* Test the padding is correct */ + CU_ASSERT_EQUAL(0, strncmp((char *)&(tlv->header.encoded_tlv[4]), + &path_name[0], 4)); + CU_ASSERT_EQUAL(0, strncmp((char *)&(tlv->header.encoded_tlv[8]), + &path_name[4], 4)); + CU_ASSERT_EQUAL(0, strncmp((char *)&(tlv->header.encoded_tlv[12]), + &path_name[8], 4)); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[16], 'm'); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[17], 'e'); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[18], 0); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[19], 0); + pcep_obj_free_tlv(&tlv->header); + + reset_tlv_buffer(); + tlv = pcep_tlv_create_symbolic_path_name(path_name, 3); + CU_ASSERT_PTR_NOT_NULL(tlv); + printf("El tlv es %p", tlv); + assert(tlv != NULL); // crash si FALSE + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 3); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[4], 'S'); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[5], 'o'); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[6], 'm'); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[7], 0); + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_ipv4_lsp_identifiers() +{ + struct in_addr sender_ip, endpoint_ip; + uint16_t lsp_id = 7; + uint16_t tunnel_id = 16; + struct in_addr extended_tunnel_id; + extended_tunnel_id.s_addr = 256; + inet_pton(AF_INET, "192.168.1.1", &sender_ip); + inet_pton(AF_INET, "192.168.1.2", &endpoint_ip); + + struct pcep_object_tlv_ipv4_lsp_identifier *tlv = + pcep_tlv_create_ipv4_lsp_identifiers(NULL, &endpoint_ip, lsp_id, + tunnel_id, + &extended_tunnel_id); + CU_ASSERT_PTR_NULL(tlv); + + tlv = pcep_tlv_create_ipv4_lsp_identifiers( + &sender_ip, NULL, lsp_id, tunnel_id, &extended_tunnel_id); + CU_ASSERT_PTR_NULL(tlv); + + tlv = pcep_tlv_create_ipv4_lsp_identifiers( + NULL, NULL, lsp_id, tunnel_id, &extended_tunnel_id); + CU_ASSERT_PTR_NULL(tlv); + assert(tlv == NULL); + + tlv = pcep_tlv_create_ipv4_lsp_identifiers(&sender_ip, &endpoint_ip, + lsp_id, tunnel_id, + &extended_tunnel_id); + CU_ASSERT_PTR_NOT_NULL(tlv); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, + PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t) * 4); + uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint32_ptr[1], sender_ip.s_addr); + CU_ASSERT_EQUAL(uint32_ptr[2], + (uint32_t)(htons(tunnel_id) << 16) | htons(lsp_id)); + CU_ASSERT_EQUAL(uint32_ptr[3], extended_tunnel_id.s_addr); + CU_ASSERT_EQUAL(uint32_ptr[4], endpoint_ip.s_addr); + pcep_obj_free_tlv(&tlv->header); + + reset_tlv_buffer(); + tlv = pcep_tlv_create_ipv4_lsp_identifiers(&sender_ip, &endpoint_ip, + lsp_id, tunnel_id, NULL); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, + PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t) * 4); + uint32_ptr = (uint32_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint32_ptr[1], sender_ip.s_addr); + CU_ASSERT_EQUAL(uint32_ptr[2], + (uint32_t)(htons(tunnel_id) << 16) | htons(lsp_id)); + CU_ASSERT_EQUAL(uint32_ptr[3], INADDR_ANY); + CU_ASSERT_EQUAL(uint32_ptr[4], endpoint_ip.s_addr); + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_ipv6_lsp_identifiers() +{ + struct in6_addr sender_ip, endpoint_ip; + uint16_t lsp_id = 3; + uint16_t tunnel_id = 16; + uint32_t extended_tunnel_id[4]; + + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &sender_ip); + inet_pton(AF_INET6, "2001:db8::8a2e:370:8446", &endpoint_ip); + extended_tunnel_id[0] = 1; + extended_tunnel_id[1] = 2; + extended_tunnel_id[2] = 3; + extended_tunnel_id[3] = 4; + + struct pcep_object_tlv_ipv6_lsp_identifier *tlv = + pcep_tlv_create_ipv6_lsp_identifiers( + NULL, &endpoint_ip, lsp_id, tunnel_id, + (struct in6_addr *)&extended_tunnel_id); + CU_ASSERT_PTR_NULL(tlv); + + tlv = pcep_tlv_create_ipv6_lsp_identifiers( + &sender_ip, NULL, lsp_id, tunnel_id, + (struct in6_addr *)&extended_tunnel_id); + CU_ASSERT_PTR_NULL(tlv); + + tlv = pcep_tlv_create_ipv6_lsp_identifiers( + NULL, NULL, lsp_id, tunnel_id, + (struct in6_addr *)&extended_tunnel_id); + CU_ASSERT_PTR_NULL(tlv); + assert(tlv == NULL); + + tlv = pcep_tlv_create_ipv6_lsp_identifiers( + &sender_ip, &endpoint_ip, lsp_id, tunnel_id, + (struct in6_addr *)&extended_tunnel_id); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, + PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 52); + uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint32_ptr[5], + (uint32_t)(htons(tunnel_id) << 16) | htons(lsp_id)); + + pcep_obj_free_tlv(&tlv->header); +} +void test_pcep_tlv_create_srpag_pol_id_ipv4() +{ + uint32_t color = 1; + struct in_addr src; + inet_pton(AF_INET, "192.168.1.2", &src); + + struct pcep_object_tlv_srpag_pol_id *tlv = + pcep_tlv_create_srpag_pol_id_ipv4(color, (void *)&src); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, (PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID)); + CU_ASSERT_EQUAL( + tlv->header.encoded_tlv_length, + (8 /*draft-barth-pce-segment-routing-policy-cp-04#5.1*/)); + CU_ASSERT_EQUAL(tlv->color, (color)); + uint32_t aux_color = htonl(color); // Is color right encoded + CU_ASSERT_EQUAL(0, memcmp(&tlv_buf[0] + TLV_HEADER_LENGTH, &aux_color, + sizeof(color))); + CU_ASSERT_EQUAL(tlv->end_point.ipv4.s_addr, (src.s_addr)); + // Are simetrical? + struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf); + struct pcep_object_tlv_srpag_pol_id *dec_tlv = + (struct pcep_object_tlv_srpag_pol_id *)dec_hdr; + CU_ASSERT_EQUAL(tlv->color, dec_tlv->color); + + pceplib_free(PCEPLIB_MESSAGES, dec_hdr); + pcep_obj_free_tlv(&tlv->header); +} +void test_pcep_tlv_create_srpag_pol_id_ipv6() +{ + + uint32_t color = 1; + struct in6_addr src; + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &src); + + struct pcep_object_tlv_srpag_pol_id *tlv = + pcep_tlv_create_srpag_pol_id_ipv6(color, &src); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, (PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID)); + CU_ASSERT_EQUAL( + tlv->header.encoded_tlv_length, + (20 /*draft-barth-pce-segment-routing-policy-cp-04#5.1*/)); + CU_ASSERT_EQUAL(tlv->color, (color)); + CU_ASSERT_EQUAL(0, memcmp(&tlv->end_point.ipv6, &src, sizeof(src))); + + uint32_t aux_color = htonl(color); + CU_ASSERT_EQUAL(0, memcmp(&aux_color, tlv_buf + TLV_HEADER_LENGTH, + sizeof(tlv->color))); + // Are simetrical? + struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf); + struct pcep_object_tlv_srpag_pol_id *dec_tlv = + (struct pcep_object_tlv_srpag_pol_id *)dec_hdr; + CU_ASSERT_EQUAL(tlv->color, dec_tlv->color); + + pceplib_free(PCEPLIB_MESSAGES, dec_hdr); + pcep_obj_free_tlv(&tlv->header); +} +void test_pcep_tlv_create_srpag_pol_name() +{ + const char *pol_name = "Some Pol Name"; + + struct pcep_object_tlv_srpag_pol_name *tlv = + pcep_tlv_create_srpag_pol_name(pol_name, strlen(pol_name)); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, + (PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME)); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, + (normalize_pcep_tlv_length(strlen(pol_name)))); + CU_ASSERT_EQUAL(0, strcmp(pol_name, (char *)tlv->name)); + + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_srpag_cp_id() +{ + // draft-ietf-spring-segment-routing-policy-06.pdf#2.3 + // 10 PCEP, 20 BGP SR Policy, 30 Via Configuration + uint8_t proto_origin = 10; + uint32_t ASN = 0; + struct in6_addr with_mapped_ipv4; + inet_pton(AF_INET6, "::ffff:192.0.2.128", &with_mapped_ipv4); + uint32_t discriminator = 0; + + struct pcep_object_tlv_srpag_cp_id *tlv = pcep_tlv_create_srpag_cp_id( + proto_origin, ASN, &with_mapped_ipv4, discriminator); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, + (PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID)); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, + (sizeof(proto_origin) + sizeof(ASN) + + sizeof(with_mapped_ipv4) + sizeof(discriminator))); + CU_ASSERT_EQUAL(tlv->proto, (proto_origin)); + CU_ASSERT_EQUAL(tlv->orig_asn, (ASN)); + CU_ASSERT_EQUAL(0, memcmp(&tlv->orig_addres, &with_mapped_ipv4, + sizeof(with_mapped_ipv4))); + CU_ASSERT_EQUAL(tlv->discriminator, (discriminator)); + // Are simetrical? + struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf); + struct pcep_object_tlv_srpag_cp_id *dec_tlv = + (struct pcep_object_tlv_srpag_cp_id *)dec_hdr; + CU_ASSERT_EQUAL(tlv->proto, dec_tlv->proto); + + pceplib_free(PCEPLIB_MESSAGES, dec_hdr); + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_srpag_cp_pref() +{ + uint32_t preference_default = 100; + + struct pcep_object_tlv_srpag_cp_pref *tlv = + pcep_tlv_create_srpag_cp_pref(preference_default); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, + (PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE)); + printf(" encoded length vs sizeof pref (%d) vs (%ld)\n", + tlv->header.encoded_tlv_length, sizeof(preference_default)); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, + sizeof(preference_default)); + CU_ASSERT_EQUAL(tlv->preference, (preference_default)); + uint32_t aux_pref = htonl(preference_default); // Is pref right encoded + CU_ASSERT_EQUAL(0, memcmp(tlv_buf + TLV_HEADER_LENGTH, &aux_pref, + sizeof(preference_default))); + // Are simetrical? + struct pcep_object_tlv_header *dec_hdr = pcep_decode_tlv(tlv_buf); + struct pcep_object_tlv_srpag_cp_pref *dec_tlv = + (struct pcep_object_tlv_srpag_cp_pref *)dec_hdr; + CU_ASSERT_EQUAL(tlv->preference, dec_tlv->preference); + + pceplib_free(PCEPLIB_MESSAGES, dec_hdr); + pcep_obj_free_tlv(&tlv->header); +} +void test_pcep_tlv_create_lsp_error_code() +{ + struct pcep_object_tlv_lsp_error_code *tlv = + pcep_tlv_create_lsp_error_code( + PCEP_TLV_LSP_ERROR_CODE_RSVP_SIGNALING_ERROR); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, sizeof(uint32_t)); + uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint32_ptr[1], + htonl(PCEP_TLV_LSP_ERROR_CODE_RSVP_SIGNALING_ERROR)); + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_rsvp_ipv4_error_spec() +{ + struct in_addr error_node_ip; + inet_pton(AF_INET, "192.168.1.1", &error_node_ip); + uint8_t error_code = 8; + uint16_t error_value = 0xaabb; + + struct pcep_object_tlv_rsvp_error_spec *tlv = + pcep_tlv_create_rsvp_ipv4_error_spec(NULL, error_code, + error_value); + CU_ASSERT_PTR_NULL(tlv); + + tlv = pcep_tlv_create_rsvp_ipv4_error_spec(&error_node_ip, error_code, + error_value); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 12); + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_rsvp_ipv6_error_spec() +{ + struct in6_addr error_node_ip; + inet_pton(AF_INET6, "2001:db8::8a2e:370:7334", &error_node_ip); + uint8_t error_code = 8; + uint16_t error_value = 0xaabb; + + struct pcep_object_tlv_rsvp_error_spec *tlv = + pcep_tlv_create_rsvp_ipv6_error_spec(NULL, error_code, + error_value); + CU_ASSERT_PTR_NULL(tlv); + + tlv = pcep_tlv_create_rsvp_ipv6_error_spec(&error_node_ip, error_code, + error_value); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 24); + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_nopath_vector() +{ + uint32_t enterprise_number = 0x01020304; + uint32_t enterprise_specific_info = 0x05060708; + + struct pcep_object_tlv_vendor_info *tlv = pcep_tlv_create_vendor_info( + enterprise_number, enterprise_specific_info); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, PCEP_OBJ_TLV_TYPE_VENDOR_INFO); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 8); + uint32_t *uint32_ptr = (uint32_t *)tlv->header.encoded_tlv; + CU_ASSERT_EQUAL(uint32_ptr[1], htonl(enterprise_number)); + CU_ASSERT_EQUAL(uint32_ptr[2], htonl(enterprise_specific_info)); + + pcep_obj_free_tlv(&tlv->header); +} + +void test_pcep_tlv_create_arbitrary() +{ + char data[16] = "Some Data"; + uint16_t data_length = 9; + uint16_t tlv_id_unknown = 1; // 65505; // Whatever id to be created + struct pcep_object_tlv_arbitrary *tlv = pcep_tlv_create_tlv_arbitrary( + data, data_length, tlv_id_unknown); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, tlv_id_unknown); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, data_length); + /* Test the padding is correct */ + CU_ASSERT_EQUAL( + 0, strncmp((char *)&(tlv->header.encoded_tlv[4]), &data[0], 4)); + CU_ASSERT_EQUAL( + 0, strncmp((char *)&(tlv->header.encoded_tlv[8]), &data[4], 4)); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[11], 't'); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[12], 'a'); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[13], 0); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[14], 0); + pcep_obj_free_tlv(&tlv->header); + + reset_tlv_buffer(); + tlv = pcep_tlv_create_tlv_arbitrary(data, 3, tlv_id_unknown); + CU_ASSERT_PTR_NOT_NULL(tlv); + assert(tlv != NULL); + pcep_encode_tlv(&tlv->header, versioning, tlv_buf); + CU_ASSERT_EQUAL(tlv->header.type, tlv_id_unknown); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv_length, 3); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[4], 'S'); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[5], 'o'); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[6], 'm'); + CU_ASSERT_EQUAL(tlv->header.encoded_tlv[7], 0); + + pcep_obj_free_tlv(&tlv->header); +} diff --git a/pceplib/test/pcep_msg_tlvs_test.h b/pceplib/test/pcep_msg_tlvs_test.h new file mode 100644 index 0000000..a961d7e --- /dev/null +++ b/pceplib/test/pcep_msg_tlvs_test.h @@ -0,0 +1,51 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +#ifndef PCEP_MSG_TLVS_TEST_H_ +#define PCEP_MSG_TLVS_TEST_H_ + +int pcep_tlvs_test_suite_setup(void); +int pcep_tlvs_test_suite_teardown(void); +void pcep_tlvs_test_setup(void); +void pcep_tlvs_test_teardown(void); +void test_pcep_tlv_create_stateful_pce_capability(void); +void test_pcep_tlv_create_speaker_entity_id(void); +void test_pcep_tlv_create_lsp_db_version(void); +void test_pcep_tlv_create_path_setup_type(void); +void test_pcep_tlv_create_path_setup_type_capability(void); +void test_pcep_tlv_create_sr_pce_capability(void); +void test_pcep_tlv_create_symbolic_path_name(void); +void test_pcep_tlv_create_ipv4_lsp_identifiers(void); +void test_pcep_tlv_create_ipv6_lsp_identifiers(void); +void test_pcep_tlv_create_lsp_error_code(void); +void test_pcep_tlv_create_rsvp_ipv4_error_spec(void); +void test_pcep_tlv_create_rsvp_ipv6_error_spec(void); +void test_pcep_tlv_create_srpag_pol_id_ipv4(void); +void test_pcep_tlv_create_srpag_pol_id_ipv6(void); +void test_pcep_tlv_create_srpag_pol_name(void); +void test_pcep_tlv_create_srpag_cp_id(void); +void test_pcep_tlv_create_srpag_cp_pref(void); +void test_pcep_tlv_create_nopath_vector(void); +void test_pcep_tlv_create_arbitrary(void); + + +#endif diff --git a/pceplib/test/pcep_msg_tools_test.c b/pceplib/test/pcep_msg_tools_test.c new file mode 100644 index 0000000..ffbe802 --- /dev/null +++ b/pceplib/test/pcep_msg_tools_test.c @@ -0,0 +1,1367 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + + +#include <CUnit/CUnit.h> + +#include <zebra.h> + +#include "pcep_msg_encoding.h" +#include "pcep_msg_messages.h" +#include "pcep_msg_tools.h" +#include "pcep_msg_tools_test.h" +#include "pcep_utils_double_linked_list.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +const uint8_t any_obj_class = 255; + +uint16_t pcep_open_hexbyte_strs_length = 28; +const char *pcep_open_odl_hexbyte_strs[] = { + "20", "01", "00", "1c", "01", "10", "00", "18", "20", "1e", + "78", "55", "00", "10", "00", "04", "00", "00", "00", "3f", + "00", "1a", "00", "04", "00", "00", "00", "00"}; + +/* PCEP INITIATE str received from ODL with 4 objects: [SRP, LSP, Endpoints, + * ERO] The LSP has a SYMBOLIC_PATH_NAME TLV. The ERO has 2 IPV4 Endpoints. */ +uint16_t pcep_initiate_hexbyte_strs_length = 68; +const char *pcep_initiate_hexbyte_strs[] = { + "20", "0c", "00", "44", "21", "12", "00", "0c", "00", "00", "00", "00", + "00", "00", "00", "01", "20", "10", "00", "14", "00", "00", "00", "09", + "00", "11", "00", "08", "66", "61", "39", "33", "33", "39", "32", "39", + "04", "10", "00", "0c", "7f", "00", "00", "01", "28", "28", "28", "28", + "07", "10", "00", "14", "01", "08", "0a", "00", "01", "01", "18", "00", + "01", "08", "0a", "00", "07", "04", "18", "00"}; + +uint16_t pcep_initiate2_hexbyte_strs_length = 72; +const char *pcep_initiate2_hexbyte_strs[] = { + "20", "0c", "00", "48", "21", "12", "00", "14", "00", "00", "00", "00", + "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01", + "20", "10", "00", "14", "00", "00", "00", "09", "00", "11", "00", "08", + "36", "65", "31", "31", "38", "39", "32", "31", "04", "10", "00", "0c", + "c0", "a8", "14", "05", "01", "01", "01", "01", "07", "10", "00", "10", + "05", "0c", "10", "01", "03", "e8", "a0", "00", "01", "01", "01", "01"}; + +uint16_t pcep_update_hexbyte_strs_length = 48; +const char *pcep_update_hexbyte_strs[] = { + "20", "0b", "00", "30", "21", "12", "00", "14", "00", "00", "00", "00", + "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01", + "20", "10", "00", "08", "00", "02", "a0", "09", "07", "10", "00", "10", + "05", "0c", "10", "01", "03", "e8", "a0", "00", "01", "01", "01", "01"}; + +/* Test that pcep_msg_read() can read multiple messages in 1 call */ +uint16_t pcep_open_initiate_hexbyte_strs_length = 100; +const char *pcep_open_initiate_odl_hexbyte_strs[] = { + "20", "01", "00", "1c", "01", "10", "00", "18", "20", "1e", "78", "55", + "00", "10", "00", "04", "00", "00", "00", "3f", "00", "1a", "00", "04", + "00", "00", "00", "00", "20", "0c", "00", "48", "21", "12", "00", "14", + "00", "00", "00", "00", "00", "00", "00", "01", "00", "1c", "00", "04", + "00", "00", "00", "01", "20", "10", "00", "14", "00", "00", "00", "09", + "00", "11", "00", "08", "36", "65", "31", "31", "38", "39", "32", "31", + "04", "10", "00", "0c", "c0", "a8", "14", "05", "01", "01", "01", "01", + "07", "10", "00", "10", "05", "0c", "10", "01", "03", "e8", "a0", "00", + "01", "01", "01", "01"}; + +uint16_t pcep_open_cisco_pce_hexbyte_strs_length = 28; +const char *pcep_open_cisco_pce_hexbyte_strs[] = { + "20", "01", "00", "1c", "01", "10", "00", "18", "20", "3c", + "78", "00", "00", "10", "00", "04", "00", "00", "00", "05", + "00", "1a", "00", "04", "00", "00", "00", "0a"}; + +uint16_t pcep_update_cisco_pce_hexbyte_strs_length = 100; +const char *pcep_update_cisco_pce_hexbyte_strs[] = { + "20", "0b", "00", "64", "21", "10", "00", "14", "00", "00", "00", "00", + "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01", + "20", "10", "00", "18", "80", "00", "f0", "89", "00", "07", "00", "0c", + "00", "00", "00", "09", "00", "03", "00", "04", "00", "00", "00", "01", + "07", "10", "00", "28", "24", "0c", "10", "01", "04", "65", "50", "00", + "0a", "0a", "0a", "05", "24", "0c", "10", "01", "04", "65", "20", "00", + "0a", "0a", "0a", "02", "24", "0c", "10", "01", "04", "65", "10", "00", + "0a", "0a", "0a", "01", "06", "10", "00", "0c", "00", "00", "00", "02", + "41", "f0", "00", "00"}; + +uint16_t pcep_report_cisco_pcc_hexbyte_strs_length = 148; +const char *pcep_report_cisco_pcc_hexbyte_strs[] = { + "20", "0a", "00", "94", "21", "10", "00", "14", "00", "00", "00", "00", + "00", "00", "00", "00", "00", "1c", "00", "04", "00", "00", "00", "01", + "20", "10", "00", "3c", "80", "00", "f0", "09", "00", "12", "00", "10", + "0a", "0a", "0a", "06", "00", "02", "00", "0f", "0a", "0a", "0a", "06", + "0a", "0a", "0a", "01", "00", "11", "00", "0d", "63", "66", "67", "5f", + "52", "36", "2d", "74", "6f", "2d", "52", "31", "00", "00", "00", "00", + "ff", "e1", "00", "06", "00", "00", "05", "dd", "70", "00", "00", "00", + "07", "10", "00", "04", "09", "10", "00", "14", "00", "00", "00", "00", + "00", "00", "00", "00", "00", "00", "00", "00", "07", "07", "01", "00", + "05", "12", "00", "08", "00", "00", "00", "00", "05", "52", "00", "08", + "00", "00", "00", "00", "06", "10", "00", "0c", "00", "00", "00", "02", + "00", "00", "00", "00", "06", "10", "00", "0c", "00", "00", "01", "04", + "41", "80", "00", "00"}; + +/* Cisco PcInitiate with the following objects: + * SRP, LSP, Endpoint, Inter-layer, Switch-layer, ERO + */ +uint16_t pcep_initiate_cisco_pcc_hexbyte_strs_length = 104; +const char *pcep_initiate_cisco_pcc_hexbyte_strs[] = { + "20", "0c", "00", "68", "21", "10", "00", "14", "00", "00", "00", "00", + "00", "00", "00", "01", "00", "1c", "00", "04", "00", "00", "00", "01", + "20", "10", "00", "30", "00", "00", "00", "89", "00", "11", "00", "13", + "50", "4f", "4c", "31", "5f", "50", "43", "49", "4e", "49", "54", "41", + "54", "45", "5f", "54", "45", "53", "54", "00", "00", "07", "00", "0c", + "00", "00", "00", "09", "00", "03", "00", "04", "00", "00", "00", "01", + "04", "10", "00", "0c", "0a", "0a", "0a", "0a", "0a", "0a", "0a", "04", + "24", "10", "00", "08", "00", "00", "01", "4d", "25", "10", "00", "08", + "00", "00", "00", "64", "07", "10", "00", "04"}; + +struct pcep_message *create_message(uint8_t msg_type, uint8_t obj1_class, + uint8_t obj2_class, uint8_t obj3_class, + uint8_t obj4_class); +int convert_hexstrs_to_binary(char *filename, const char *hexbyte_strs[], + uint16_t hexbyte_strs_length); + +int pcep_tools_test_suite_setup(void) +{ + pceplib_memory_reset(); + return 0; +} + +int pcep_tools_test_suite_teardown(void) +{ + printf("\n"); + pceplib_memory_dump(); + return 0; +} + +void pcep_tools_test_setup(void) +{ +} + +void pcep_tools_test_teardown(void) +{ +} + +static const char BASE_TMPFILE[] = "/tmp/pceplib_XXXXXX"; +static int BASE_TMPFILE_SIZE = sizeof(BASE_TMPFILE); + +/* Reads an array of hexbyte strs, and writes them to a temporary file. + * The caller should close the returned file. */ +int convert_hexstrs_to_binary(char *filename, const char *hexbyte_strs[], + uint16_t hexbyte_strs_length) +{ + mode_t oldumask; + oldumask = umask(S_IXUSR | S_IXGRP | S_IWOTH | S_IROTH | S_IXOTH); + /* Set umask before anything for security */ + umask(0027); + + strlcpy(filename, BASE_TMPFILE, BASE_TMPFILE_SIZE); + int fd = mkstemp(filename); + umask(oldumask); + + if (fd == -1) + return -1; + + int i = 0; + for (; i < hexbyte_strs_length; i++) { + uint8_t byte = (uint8_t)strtol(hexbyte_strs[i], 0, 16); + if (write(fd, (char *)&byte, 1) < 0) { + return -1; + } + } + + /* Go back to the beginning of the file */ + lseek(fd, 0, SEEK_SET); + return fd; +} + +static bool pcep_obj_has_tlv(struct pcep_object_header *obj_hdr) +{ + if (obj_hdr->tlv_list == NULL) { + return false; + } + + return (obj_hdr->tlv_list->num_entries > 0); +} + +void test_pcep_msg_read_pcep_initiate() +{ + char filename[BASE_TMPFILE_SIZE]; + + int fd = convert_hexstrs_to_binary(filename, pcep_initiate_hexbyte_strs, + pcep_initiate_hexbyte_strs_length); + if (fd == -1) { + CU_ASSERT_TRUE(fd >= 0); + return; + } + double_linked_list *msg_list = pcep_msg_read(fd); + CU_ASSERT_PTR_NOT_NULL(msg_list); + assert(msg_list != NULL); + CU_ASSERT_EQUAL(msg_list->num_entries, 1); + + struct pcep_message *msg = (struct pcep_message *)msg_list->head->data; + CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4); + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE); + CU_ASSERT_EQUAL(msg->encoded_message_length, + pcep_initiate_hexbyte_strs_length); + + /* Verify each of the object types */ + + /* SRP object */ + double_linked_list_node *node = msg->obj_list->head; + struct pcep_object_header *obj_hdr = + (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_SRP); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_SRP); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, + pcep_object_get_length_by_hdr(obj_hdr)); + CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr)); + + /* LSP object and its TLV*/ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_LSP); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_LSP); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20); + CU_ASSERT_EQUAL(((struct pcep_object_lsp *)obj_hdr)->plsp_id, 0); + CU_ASSERT_TRUE(((struct pcep_object_lsp *)obj_hdr)->flag_d); + CU_ASSERT_TRUE(((struct pcep_object_lsp *)obj_hdr)->flag_a); + CU_ASSERT_FALSE(((struct pcep_object_lsp *)obj_hdr)->flag_s); + CU_ASSERT_FALSE(((struct pcep_object_lsp *)obj_hdr)->flag_r); + CU_ASSERT_FALSE(((struct pcep_object_lsp *)obj_hdr)->flag_c); + + /* LSP TLV */ + CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr)); + CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 1); + struct pcep_object_tlv_header *tlv = + (struct pcep_object_tlv_header *)obj_hdr->tlv_list->head->data; + CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME); + CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 8); + + /* Endpoints object */ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ENDPOINTS); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ENDPOINT_IPV4); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, + pcep_object_get_length_by_hdr(obj_hdr)); + CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr)); + + /* ERO object */ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ERO); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ERO); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20); + + /* ERO Subobjects */ + double_linked_list *ero_subobj_list = + ((struct pcep_object_ro *)obj_hdr)->sub_objects; + CU_ASSERT_PTR_NOT_NULL(ero_subobj_list); + assert(ero_subobj_list != NULL); + CU_ASSERT_EQUAL(ero_subobj_list->num_entries, 2); + double_linked_list_node *subobj_node = ero_subobj_list->head; + struct pcep_object_ro_subobj *subobj_hdr = + (struct pcep_object_ro_subobj *)subobj_node->data; + CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_IPV4); + struct in_addr ero_subobj_ip; + inet_pton(AF_INET, "10.0.1.1", &ero_subobj_ip); + CU_ASSERT_EQUAL( + ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->ip_addr.s_addr, + ero_subobj_ip.s_addr); + CU_ASSERT_EQUAL( + ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->prefix_length, 24); + + subobj_hdr = + (struct pcep_object_ro_subobj *)subobj_node->next_node->data; + CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_IPV4); + inet_pton(AF_INET, "10.0.7.4", &ero_subobj_ip); + CU_ASSERT_EQUAL( + ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->ip_addr.s_addr, + ero_subobj_ip.s_addr); + CU_ASSERT_EQUAL( + ((struct pcep_ro_subobj_ipv4 *)subobj_hdr)->prefix_length, 24); + + pcep_msg_free_message_list(msg_list); + close(fd); + unlink(filename); +} + + +void test_pcep_msg_read_pcep_initiate2() +{ + char filename[BASE_TMPFILE_SIZE]; + + int fd = + convert_hexstrs_to_binary(filename, pcep_initiate2_hexbyte_strs, + pcep_initiate2_hexbyte_strs_length); + if (fd == -1) { + CU_ASSERT_TRUE(fd >= 0); + return; + } + double_linked_list *msg_list = pcep_msg_read(fd); + CU_ASSERT_PTR_NOT_NULL(msg_list); + assert(msg_list != NULL); + CU_ASSERT_EQUAL(msg_list->num_entries, 1); + + struct pcep_message *msg = (struct pcep_message *)msg_list->head->data; + CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4); + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE); + CU_ASSERT_EQUAL(msg->encoded_message_length, + pcep_initiate2_hexbyte_strs_length); + + /* Verify each of the object types */ + + /* SRP object */ + double_linked_list_node *node = msg->obj_list->head; + struct pcep_object_header *obj_hdr = + (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_SRP); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_SRP); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20); + CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr)); + /* TODO test the TLVs */ + + /* LSP object and its TLV*/ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_LSP); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_LSP); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20); + + /* LSP TLV */ + CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr)); + CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 1); + struct pcep_object_tlv_header *tlv = + (struct pcep_object_tlv_header *)obj_hdr->tlv_list->head->data; + CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME); + CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 8); + + /* Endpoints object */ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ENDPOINTS); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ENDPOINT_IPV4); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, + pcep_object_get_length_by_hdr(obj_hdr)); + CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr)); + + /* ERO object */ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ERO); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ERO); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 16); + + /* ERO Subobjects */ + double_linked_list *ero_subobj_list = + ((struct pcep_object_ro *)obj_hdr)->sub_objects; + CU_ASSERT_PTR_NOT_NULL(ero_subobj_list); + assert(ero_subobj_list != NULL); + CU_ASSERT_EQUAL(ero_subobj_list->num_entries, 0); + double_linked_list_node *subobj_node = ero_subobj_list->head; + CU_ASSERT_PTR_NULL(subobj_node); + /* We no longer support draft07 SR sub-object type=5, and only support + type=36 struct pcep_object_ro_subobj *subobj_hdr = (struct + pcep_object_ro_subobj *) subobj_node->data; + CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_SR); + struct pcep_ro_subobj_sr *subobj_sr = (struct pcep_ro_subobj_sr *) + subobj_hdr; CU_ASSERT_EQUAL(subobj_sr->nai_type, + PCEP_SR_SUBOBJ_NAI_IPV4_NODE); CU_ASSERT_TRUE(subobj_sr->flag_m); + CU_ASSERT_FALSE(subobj_sr->flag_c); + CU_ASSERT_FALSE(subobj_sr->flag_s); + CU_ASSERT_FALSE(subobj_sr->flag_f); + CU_ASSERT_EQUAL(subobj_sr->sid, 65576960); + CU_ASSERT_EQUAL(*((uint32_t *) subobj_sr->nai_list->head->data), + 0x01010101); + */ + + pcep_msg_free_message_list(msg_list); + close(fd); + unlink(filename); +} + +void test_pcep_msg_read_pcep_open() +{ + char filename[BASE_TMPFILE_SIZE]; + + int fd = convert_hexstrs_to_binary(filename, pcep_open_odl_hexbyte_strs, + pcep_open_hexbyte_strs_length); + if (fd == -1) { + CU_ASSERT_TRUE(fd >= 0); + return; + } + double_linked_list *msg_list = pcep_msg_read(fd); + CU_ASSERT_PTR_NOT_NULL(msg_list); + assert(msg_list != NULL); + CU_ASSERT_EQUAL(msg_list->num_entries, 1); + + struct pcep_message *msg = (struct pcep_message *)msg_list->head->data; + CU_ASSERT_EQUAL(msg->obj_list->num_entries, 1); + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN); + CU_ASSERT_EQUAL(msg->encoded_message_length, + pcep_open_hexbyte_strs_length); + + /* Verify the Open message */ + struct pcep_object_header *obj_hdr = + (struct pcep_object_header *)msg->obj_list->head->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_OPEN); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_OPEN); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 24); + CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr)); + + /* Open TLV: Stateful PCE Capability */ + CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 2); + double_linked_list_node *tlv_node = obj_hdr->tlv_list->head; + struct pcep_object_tlv_header *tlv = + (struct pcep_object_tlv_header *)tlv_node->data; + CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY); + CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 4); + + /* Open TLV: SR PCE Capability */ + tlv_node = tlv_node->next_node; + tlv = (struct pcep_object_tlv_header *)tlv_node->data; + CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY); + CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 4); + + pcep_msg_free_message_list(msg_list); + close(fd); + unlink(filename); +} + +void test_pcep_msg_read_pcep_update() +{ + char filename[BASE_TMPFILE_SIZE]; + + int fd = convert_hexstrs_to_binary(filename, pcep_update_hexbyte_strs, + pcep_update_hexbyte_strs_length); + if (fd == -1) { + CU_ASSERT_TRUE(fd >= 0); + return; + } + double_linked_list *msg_list = pcep_msg_read(fd); + CU_ASSERT_PTR_NOT_NULL(msg_list); + assert(msg_list != NULL); + CU_ASSERT_EQUAL(msg_list->num_entries, 1); + + struct pcep_message *msg = (struct pcep_message *)msg_list->head->data; + CU_ASSERT_EQUAL(msg->obj_list->num_entries, 3); + + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_UPDATE); + CU_ASSERT_EQUAL(msg->encoded_message_length, + pcep_update_hexbyte_strs_length); + + /* Verify each of the object types */ + + double_linked_list_node *node = msg->obj_list->head; + + /* SRP object */ + struct pcep_object_header *obj_hdr = + (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_SRP); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_SRP); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 20); + CU_ASSERT_TRUE(pcep_obj_has_tlv(obj_hdr)); + + /* SRP TLV */ + CU_ASSERT_EQUAL(obj_hdr->tlv_list->num_entries, 1); + struct pcep_object_tlv_header *tlv = + (struct pcep_object_tlv_header *)obj_hdr->tlv_list->head->data; + CU_ASSERT_EQUAL(tlv->type, PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE); + CU_ASSERT_EQUAL(tlv->encoded_tlv_length, 4); + /* TODO verify the path setup type */ + + /* LSP object */ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_LSP); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_LSP); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, + pcep_object_get_length_by_hdr(obj_hdr)); + CU_ASSERT_FALSE(pcep_obj_has_tlv(obj_hdr)); + + /* ERO object */ + node = node->next_node; + obj_hdr = (struct pcep_object_header *)node->data; + CU_ASSERT_EQUAL(obj_hdr->object_class, PCEP_OBJ_CLASS_ERO); + CU_ASSERT_EQUAL(obj_hdr->object_type, PCEP_OBJ_TYPE_ERO); + CU_ASSERT_EQUAL(obj_hdr->encoded_object_length, 16); + + /* ERO Subobjects */ + double_linked_list *ero_subobj_list = + ((struct pcep_object_ro *)obj_hdr)->sub_objects; + CU_ASSERT_PTR_NOT_NULL(ero_subobj_list); + assert(ero_subobj_list != NULL); + CU_ASSERT_EQUAL(ero_subobj_list->num_entries, 0); + double_linked_list_node *subobj_node = ero_subobj_list->head; + CU_ASSERT_PTR_NULL(subobj_node); + /* We no longer support draft07 SR sub-object type=5, and only support + type=36 struct pcep_object_ro_subobj *subobj_hdr = (struct + pcep_object_ro_subobj *) subobj_node->data; + CU_ASSERT_EQUAL(subobj_hdr->ro_subobj_type, RO_SUBOBJ_TYPE_SR); + struct pcep_ro_subobj_sr *subobj_sr = (struct pcep_ro_subobj_sr *) + subobj_hdr; CU_ASSERT_EQUAL(subobj_sr->nai_type, + PCEP_SR_SUBOBJ_NAI_IPV4_NODE); CU_ASSERT_TRUE(subobj_sr->flag_m); + CU_ASSERT_FALSE(subobj_sr->flag_c); + CU_ASSERT_FALSE(subobj_sr->flag_s); + CU_ASSERT_FALSE(subobj_sr->flag_f); + CU_ASSERT_EQUAL(subobj_sr->sid, 65576960); + CU_ASSERT_EQUAL(*((uint32_t *) subobj_sr->nai_list->head->data), + 0x01010101); + */ + + pcep_msg_free_message_list(msg_list); + close(fd); + unlink(filename); +} + +void test_pcep_msg_read_pcep_open_initiate() +{ + char filename[BASE_TMPFILE_SIZE]; + + int fd = convert_hexstrs_to_binary( + filename, pcep_open_initiate_odl_hexbyte_strs, + pcep_open_initiate_hexbyte_strs_length); + if (fd == -1) { + CU_ASSERT_TRUE(fd >= 0); + return; + } + double_linked_list *msg_list = pcep_msg_read(fd); + CU_ASSERT_PTR_NOT_NULL(msg_list); + assert(msg_list != NULL); + CU_ASSERT_EQUAL(msg_list->num_entries, 2); + + struct pcep_message *msg = (struct pcep_message *)msg_list->head->data; + CU_ASSERT_EQUAL(msg->obj_list->num_entries, 1); + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN); + CU_ASSERT_EQUAL(msg->encoded_message_length, + pcep_open_hexbyte_strs_length); + + msg = (struct pcep_message *)msg_list->head->next_node->data; + CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4); + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE); + CU_ASSERT_EQUAL(msg->encoded_message_length, + pcep_initiate2_hexbyte_strs_length); + + pcep_msg_free_message_list(msg_list); + close(fd); + unlink(filename); +} + +void test_pcep_msg_read_pcep_open_cisco_pce() +{ + char filename[BASE_TMPFILE_SIZE]; + + int fd = convert_hexstrs_to_binary( + filename, pcep_open_cisco_pce_hexbyte_strs, + pcep_open_cisco_pce_hexbyte_strs_length); + if (fd == -1) { + CU_ASSERT_TRUE(fd >= 0); + return; + } + double_linked_list *msg_list = pcep_msg_read(fd); + CU_ASSERT_PTR_NOT_NULL(msg_list); + assert(msg_list != NULL); + CU_ASSERT_EQUAL(msg_list->num_entries, 1); + + struct pcep_message *msg = (struct pcep_message *)msg_list->head->data; + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN); + CU_ASSERT_EQUAL(msg->encoded_message_length, + pcep_open_hexbyte_strs_length); + CU_ASSERT_EQUAL(msg->obj_list->num_entries, 1); + + /* Open object */ + struct pcep_object_open *open = + (struct pcep_object_open *)msg->obj_list->head->data; + CU_ASSERT_EQUAL(open->header.object_class, PCEP_OBJ_CLASS_OPEN); + CU_ASSERT_EQUAL(open->header.object_type, PCEP_OBJ_TYPE_OPEN); + CU_ASSERT_EQUAL(open->header.encoded_object_length, 24); + CU_ASSERT_EQUAL(open->open_deadtimer, 120); + CU_ASSERT_EQUAL(open->open_keepalive, 60); + CU_ASSERT_EQUAL(open->open_sid, 0); + CU_ASSERT_EQUAL(open->open_version, 1); + CU_ASSERT_PTR_NOT_NULL(open->header.tlv_list); + assert(open->header.tlv_list != NULL); + CU_ASSERT_EQUAL(open->header.tlv_list->num_entries, 2); + + /* Stateful PCE Capability TLV */ + double_linked_list_node *tlv_node = open->header.tlv_list->head; + struct pcep_object_tlv_stateful_pce_capability *pce_cap_tlv = + (struct pcep_object_tlv_stateful_pce_capability *) + tlv_node->data; + CU_ASSERT_EQUAL(pce_cap_tlv->header.type, + PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY); + CU_ASSERT_EQUAL(pce_cap_tlv->header.encoded_tlv_length, 4); + CU_ASSERT_TRUE(pce_cap_tlv->flag_u_lsp_update_capability); + CU_ASSERT_TRUE(pce_cap_tlv->flag_i_lsp_instantiation_capability); + CU_ASSERT_FALSE(pce_cap_tlv->flag_s_include_db_version); + CU_ASSERT_FALSE(pce_cap_tlv->flag_t_triggered_resync); + CU_ASSERT_FALSE(pce_cap_tlv->flag_d_delta_lsp_sync); + CU_ASSERT_FALSE(pce_cap_tlv->flag_f_triggered_initial_sync); + + /* SR PCE Capability TLV */ + tlv_node = tlv_node->next_node; + struct pcep_object_tlv_sr_pce_capability *sr_pce_cap_tlv = + (struct pcep_object_tlv_sr_pce_capability *)tlv_node->data; + CU_ASSERT_EQUAL(sr_pce_cap_tlv->header.type, + PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY); + CU_ASSERT_EQUAL(sr_pce_cap_tlv->header.encoded_tlv_length, 4); + CU_ASSERT_FALSE(sr_pce_cap_tlv->flag_n); + CU_ASSERT_FALSE(sr_pce_cap_tlv->flag_x); + CU_ASSERT_EQUAL(sr_pce_cap_tlv->max_sid_depth, 10); + + pcep_msg_free_message_list(msg_list); + close(fd); + unlink(filename); +} + +void test_pcep_msg_read_pcep_update_cisco_pce() +{ + char filename[BASE_TMPFILE_SIZE]; + + int fd = convert_hexstrs_to_binary( + filename, pcep_update_cisco_pce_hexbyte_strs, + pcep_update_cisco_pce_hexbyte_strs_length); + if (fd == -1) { + CU_ASSERT_TRUE(fd >= 0); + return; + } + double_linked_list *msg_list = pcep_msg_read(fd); + CU_ASSERT_PTR_NOT_NULL(msg_list); + assert(msg_list != NULL); + CU_ASSERT_EQUAL(msg_list->num_entries, 1); + + struct pcep_message *msg = (struct pcep_message *)msg_list->head->data; + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_UPDATE); + CU_ASSERT_EQUAL(msg->encoded_message_length, + pcep_update_cisco_pce_hexbyte_strs_length); + CU_ASSERT_EQUAL(msg->obj_list->num_entries, 4); + + /* SRP object */ + double_linked_list_node *obj_node = msg->obj_list->head; + struct pcep_object_srp *srp = (struct pcep_object_srp *)obj_node->data; + CU_ASSERT_EQUAL(srp->header.object_class, PCEP_OBJ_CLASS_SRP); + CU_ASSERT_EQUAL(srp->header.object_type, PCEP_OBJ_TYPE_SRP); + CU_ASSERT_EQUAL(srp->header.encoded_object_length, 20); + CU_ASSERT_PTR_NOT_NULL(srp->header.tlv_list); + assert(srp->header.tlv_list != NULL); + CU_ASSERT_EQUAL(srp->header.tlv_list->num_entries, 1); + CU_ASSERT_EQUAL(srp->srp_id_number, 1); + CU_ASSERT_FALSE(srp->flag_lsp_remove); + + /* SRP Path Setup Type TLV */ + double_linked_list_node *tlv_node = srp->header.tlv_list->head; + struct pcep_object_tlv_path_setup_type *pst_tlv = + (struct pcep_object_tlv_path_setup_type *)tlv_node->data; + CU_ASSERT_EQUAL(pst_tlv->header.type, + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE); + CU_ASSERT_EQUAL(pst_tlv->header.encoded_tlv_length, 4); + CU_ASSERT_EQUAL(pst_tlv->path_setup_type, 1); + + /* LSP object */ + obj_node = obj_node->next_node; + struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)obj_node->data; + CU_ASSERT_EQUAL(lsp->header.object_class, PCEP_OBJ_CLASS_LSP); + CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP); + CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 24); + CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list); + assert(lsp->header.tlv_list != NULL); + CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 1); + CU_ASSERT_EQUAL(lsp->plsp_id, 524303); + CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN); + CU_ASSERT_TRUE(lsp->flag_a); + CU_ASSERT_TRUE(lsp->flag_c); + CU_ASSERT_TRUE(lsp->flag_d); + CU_ASSERT_FALSE(lsp->flag_r); + CU_ASSERT_FALSE(lsp->flag_s); + + /* LSP Vendor Info TLV */ + tlv_node = lsp->header.tlv_list->head; + struct pcep_object_tlv_vendor_info *vendor_tlv = + (struct pcep_object_tlv_vendor_info *)tlv_node->data; + CU_ASSERT_EQUAL(vendor_tlv->header.type, PCEP_OBJ_TLV_TYPE_VENDOR_INFO); + CU_ASSERT_EQUAL(vendor_tlv->header.encoded_tlv_length, 12); + CU_ASSERT_EQUAL(vendor_tlv->enterprise_number, 9); + CU_ASSERT_EQUAL(vendor_tlv->enterprise_specific_info, 0x00030004); + + /* ERO object */ + obj_node = obj_node->next_node; + struct pcep_object_ro *ero = (struct pcep_object_ro *)obj_node->data; + CU_ASSERT_EQUAL(ero->header.object_class, PCEP_OBJ_CLASS_ERO); + CU_ASSERT_EQUAL(ero->header.object_type, PCEP_OBJ_TYPE_ERO); + CU_ASSERT_EQUAL(ero->header.encoded_object_length, 40); + CU_ASSERT_PTR_NULL(ero->header.tlv_list); + CU_ASSERT_PTR_NOT_NULL(ero->sub_objects); + assert(ero->sub_objects != NULL); + CU_ASSERT_EQUAL(ero->sub_objects->num_entries, 3); + + /* ERO Subobjects */ + double_linked_list_node *ero_subobj_node = ero->sub_objects->head; + struct pcep_ro_subobj_sr *sr_subobj_ipv4_node = + (struct pcep_ro_subobj_sr *)ero_subobj_node->data; + CU_ASSERT_EQUAL(sr_subobj_ipv4_node->ro_subobj.ro_subobj_type, + RO_SUBOBJ_TYPE_SR); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->ro_subobj.flag_subobj_loose_hop); + CU_ASSERT_EQUAL(sr_subobj_ipv4_node->nai_type, + PCEP_SR_SUBOBJ_NAI_IPV4_NODE); + CU_ASSERT_TRUE(sr_subobj_ipv4_node->flag_m); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_c); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_f); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_s); + CU_ASSERT_EQUAL(sr_subobj_ipv4_node->sid, 73748480); + CU_ASSERT_EQUAL( + *((uint32_t *)sr_subobj_ipv4_node->nai_list->head->data), + htonl(0x0a0a0a05)); + + ero_subobj_node = ero_subobj_node->next_node; + sr_subobj_ipv4_node = (struct pcep_ro_subobj_sr *)ero_subobj_node->data; + CU_ASSERT_EQUAL(sr_subobj_ipv4_node->ro_subobj.ro_subobj_type, + RO_SUBOBJ_TYPE_SR); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->ro_subobj.flag_subobj_loose_hop); + CU_ASSERT_EQUAL(sr_subobj_ipv4_node->nai_type, + PCEP_SR_SUBOBJ_NAI_IPV4_NODE); + CU_ASSERT_TRUE(sr_subobj_ipv4_node->flag_m); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_c); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_f); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_s); + CU_ASSERT_EQUAL(sr_subobj_ipv4_node->sid, 73736192); + CU_ASSERT_EQUAL( + *((uint32_t *)sr_subobj_ipv4_node->nai_list->head->data), + htonl(0x0a0a0a02)); + + ero_subobj_node = ero_subobj_node->next_node; + sr_subobj_ipv4_node = (struct pcep_ro_subobj_sr *)ero_subobj_node->data; + CU_ASSERT_EQUAL(sr_subobj_ipv4_node->ro_subobj.ro_subobj_type, + RO_SUBOBJ_TYPE_SR); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->ro_subobj.flag_subobj_loose_hop); + CU_ASSERT_EQUAL(sr_subobj_ipv4_node->nai_type, + PCEP_SR_SUBOBJ_NAI_IPV4_NODE); + CU_ASSERT_TRUE(sr_subobj_ipv4_node->flag_m); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_c); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_f); + CU_ASSERT_FALSE(sr_subobj_ipv4_node->flag_s); + CU_ASSERT_EQUAL(sr_subobj_ipv4_node->sid, 73732096); + CU_ASSERT_EQUAL( + *((uint32_t *)sr_subobj_ipv4_node->nai_list->head->data), + htonl(0x0a0a0a01)); + + /* Metric object */ + obj_node = obj_node->next_node; + struct pcep_object_metric *metric = + (struct pcep_object_metric *)obj_node->data; + CU_ASSERT_EQUAL(metric->header.object_class, PCEP_OBJ_CLASS_METRIC); + CU_ASSERT_EQUAL(metric->header.object_type, PCEP_OBJ_TYPE_METRIC); + CU_ASSERT_EQUAL(metric->header.encoded_object_length, 12); + CU_ASSERT_PTR_NULL(metric->header.tlv_list); + CU_ASSERT_FALSE(metric->flag_b); + CU_ASSERT_FALSE(metric->flag_c); + CU_ASSERT_EQUAL(metric->type, PCEP_METRIC_TE); + CU_ASSERT_EQUAL(metric->value, 30.0); + + pcep_msg_free_message_list(msg_list); + close(fd); + unlink(filename); +} + +void test_pcep_msg_read_pcep_report_cisco_pcc() +{ + char filename[BASE_TMPFILE_SIZE]; + + int fd = convert_hexstrs_to_binary( + filename, pcep_report_cisco_pcc_hexbyte_strs, + pcep_report_cisco_pcc_hexbyte_strs_length); + if (fd == -1) { + CU_ASSERT_TRUE(fd >= 0); + return; + } + double_linked_list *msg_list = pcep_msg_read(fd); + CU_ASSERT_PTR_NOT_NULL(msg_list); + assert(msg_list != NULL); + CU_ASSERT_EQUAL(msg_list->num_entries, 1); + + struct pcep_message *msg = (struct pcep_message *)msg_list->head->data; + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_REPORT); + CU_ASSERT_EQUAL(msg->encoded_message_length, + pcep_report_cisco_pcc_hexbyte_strs_length); + CU_ASSERT_EQUAL(msg->obj_list->num_entries, 8); + + /* SRP object */ + double_linked_list_node *obj_node = msg->obj_list->head; + struct pcep_object_srp *srp = (struct pcep_object_srp *)obj_node->data; + CU_ASSERT_EQUAL(srp->header.object_class, PCEP_OBJ_CLASS_SRP); + CU_ASSERT_EQUAL(srp->header.object_type, PCEP_OBJ_TYPE_SRP); + CU_ASSERT_EQUAL(srp->header.encoded_object_length, 20); + CU_ASSERT_PTR_NOT_NULL(srp->header.tlv_list); + assert(srp->header.tlv_list != NULL); + CU_ASSERT_EQUAL(srp->header.tlv_list->num_entries, 1); + CU_ASSERT_EQUAL(srp->srp_id_number, 0); + CU_ASSERT_FALSE(srp->flag_lsp_remove); + + /* SRP Path Setup Type TLV */ + double_linked_list_node *tlv_node = srp->header.tlv_list->head; + struct pcep_object_tlv_path_setup_type *pst_tlv = + (struct pcep_object_tlv_path_setup_type *)tlv_node->data; + CU_ASSERT_EQUAL(pst_tlv->header.type, + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE); + CU_ASSERT_EQUAL(pst_tlv->header.encoded_tlv_length, 4); + CU_ASSERT_EQUAL(pst_tlv->path_setup_type, 1); + + /* LSP object */ + obj_node = obj_node->next_node; + struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)obj_node->data; + CU_ASSERT_EQUAL(lsp->header.object_class, PCEP_OBJ_CLASS_LSP); + CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP); + CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 60); + CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list); + /* The TLV with ID 65505 is now recognized, and its in the list */ + CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 3); + CU_ASSERT_EQUAL(lsp->plsp_id, 524303); + CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN); + CU_ASSERT_TRUE(lsp->flag_a); + CU_ASSERT_TRUE(lsp->flag_d); + CU_ASSERT_FALSE(lsp->flag_c); + CU_ASSERT_FALSE(lsp->flag_r); + CU_ASSERT_FALSE(lsp->flag_s); + + /* LSP IPv4 LSP Identifier TLV */ + tlv_node = lsp->header.tlv_list->head; + struct pcep_object_tlv_ipv4_lsp_identifier *ipv4_lsp_id = + (struct pcep_object_tlv_ipv4_lsp_identifier *)tlv_node->data; + CU_ASSERT_EQUAL(ipv4_lsp_id->header.type, + PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS); + CU_ASSERT_EQUAL(ipv4_lsp_id->header.encoded_tlv_length, 16); + CU_ASSERT_EQUAL(ipv4_lsp_id->ipv4_tunnel_sender.s_addr, + htonl(0x0a0a0a06)); + CU_ASSERT_EQUAL(ipv4_lsp_id->ipv4_tunnel_endpoint.s_addr, + htonl(0x0a0a0a01)); + CU_ASSERT_EQUAL(ipv4_lsp_id->extended_tunnel_id.s_addr, + htonl(0x0a0a0a06)); + CU_ASSERT_EQUAL(ipv4_lsp_id->tunnel_id, 15); + CU_ASSERT_EQUAL(ipv4_lsp_id->lsp_id, 2); + + /* LSP Symbolic Path Name TLV */ + tlv_node = tlv_node->next_node; + struct pcep_object_tlv_symbolic_path_name *sym_path_name = + (struct pcep_object_tlv_symbolic_path_name *)tlv_node->data; + CU_ASSERT_EQUAL(sym_path_name->header.type, + PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME); + CU_ASSERT_EQUAL(sym_path_name->header.encoded_tlv_length, 13); + CU_ASSERT_EQUAL(sym_path_name->symbolic_path_name_length, 13); + CU_ASSERT_EQUAL( + strncmp(sym_path_name->symbolic_path_name, "cfg_R6-to-R1", 13), + 0); + + /* ERO object */ + obj_node = obj_node->next_node; + struct pcep_object_ro *ero = (struct pcep_object_ro *)obj_node->data; + CU_ASSERT_EQUAL(ero->header.object_class, PCEP_OBJ_CLASS_ERO); + CU_ASSERT_EQUAL(ero->header.object_type, PCEP_OBJ_TYPE_ERO); + CU_ASSERT_EQUAL(ero->header.encoded_object_length, 4); + CU_ASSERT_PTR_NULL(ero->header.tlv_list); + CU_ASSERT_PTR_NOT_NULL(ero->sub_objects); + assert(ero->sub_objects != NULL); + CU_ASSERT_EQUAL(ero->sub_objects->num_entries, 0); + + /* LSPA object */ + obj_node = obj_node->next_node; + struct pcep_object_lspa *lspa = + (struct pcep_object_lspa *)obj_node->data; + CU_ASSERT_EQUAL(lspa->header.object_class, PCEP_OBJ_CLASS_LSPA); + CU_ASSERT_EQUAL(lspa->header.object_type, PCEP_OBJ_TYPE_LSPA); + CU_ASSERT_EQUAL(lspa->header.encoded_object_length, 20); + CU_ASSERT_PTR_NULL(lspa->header.tlv_list); + CU_ASSERT_TRUE(lspa->flag_local_protection); + CU_ASSERT_EQUAL(lspa->holding_priority, 7); + CU_ASSERT_EQUAL(lspa->setup_priority, 7); + CU_ASSERT_EQUAL(lspa->lspa_include_all, 0); + CU_ASSERT_EQUAL(lspa->lspa_include_any, 0); + CU_ASSERT_EQUAL(lspa->lspa_exclude_any, 0); + + /* Bandwidth object 1 */ + obj_node = obj_node->next_node; + struct pcep_object_bandwidth *bandwidth = + (struct pcep_object_bandwidth *)obj_node->data; + CU_ASSERT_EQUAL(bandwidth->header.object_class, + PCEP_OBJ_CLASS_BANDWIDTH); + CU_ASSERT_EQUAL(bandwidth->header.object_type, + PCEP_OBJ_TYPE_BANDWIDTH_REQ); + CU_ASSERT_EQUAL(bandwidth->header.encoded_object_length, 8); + CU_ASSERT_EQUAL(bandwidth->bandwidth, 0); + + /* Bandwidth object 2 */ + obj_node = obj_node->next_node; + bandwidth = (struct pcep_object_bandwidth *)obj_node->data; + CU_ASSERT_EQUAL(bandwidth->header.object_class, + PCEP_OBJ_CLASS_BANDWIDTH); + CU_ASSERT_EQUAL(bandwidth->header.object_type, + PCEP_OBJ_TYPE_BANDWIDTH_CISCO); + CU_ASSERT_EQUAL(bandwidth->header.encoded_object_length, 8); + CU_ASSERT_EQUAL(bandwidth->bandwidth, 0); + + /* Metric object 1 */ + obj_node = obj_node->next_node; + struct pcep_object_metric *metric = + (struct pcep_object_metric *)obj_node->data; + CU_ASSERT_EQUAL(metric->header.object_class, PCEP_OBJ_CLASS_METRIC); + CU_ASSERT_EQUAL(metric->header.object_type, PCEP_OBJ_TYPE_METRIC); + CU_ASSERT_EQUAL(metric->header.encoded_object_length, 12); + CU_ASSERT_PTR_NULL(metric->header.tlv_list); + CU_ASSERT_FALSE(metric->flag_b); + CU_ASSERT_FALSE(metric->flag_c); + CU_ASSERT_EQUAL(metric->type, PCEP_METRIC_TE); + CU_ASSERT_EQUAL(metric->value, 0); + + /* Metric object 2 */ + obj_node = obj_node->next_node; + metric = (struct pcep_object_metric *)obj_node->data; + CU_ASSERT_EQUAL(metric->header.object_class, PCEP_OBJ_CLASS_METRIC); + CU_ASSERT_EQUAL(metric->header.object_type, PCEP_OBJ_TYPE_METRIC); + CU_ASSERT_EQUAL(metric->header.encoded_object_length, 12); + CU_ASSERT_PTR_NULL(metric->header.tlv_list); + CU_ASSERT_TRUE(metric->flag_b); + CU_ASSERT_FALSE(metric->flag_c); + CU_ASSERT_EQUAL(metric->type, PCEP_METRIC_AGGREGATE_BW); + CU_ASSERT_EQUAL(metric->value, 16.0); + + pcep_msg_free_message_list(msg_list); + close(fd); + unlink(filename); +} + +void test_pcep_msg_read_pcep_initiate_cisco_pcc() +{ + char filename[BASE_TMPFILE_SIZE]; + + int fd = convert_hexstrs_to_binary( + filename, pcep_initiate_cisco_pcc_hexbyte_strs, + pcep_initiate_cisco_pcc_hexbyte_strs_length); + if (fd == -1) { + CU_ASSERT_TRUE(fd >= 0); + return; + } + double_linked_list *msg_list = pcep_msg_read(fd); + CU_ASSERT_PTR_NOT_NULL(msg_list); + assert(msg_list != NULL); + CU_ASSERT_EQUAL(msg_list->num_entries, 1); + + struct pcep_message *msg = (struct pcep_message *)msg_list->head->data; + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_INITIATE); + CU_ASSERT_EQUAL(msg->encoded_message_length, + pcep_initiate_cisco_pcc_hexbyte_strs_length); + CU_ASSERT_EQUAL(msg->obj_list->num_entries, 6); + + /* SRP object */ + double_linked_list_node *obj_node = msg->obj_list->head; + struct pcep_object_srp *srp = (struct pcep_object_srp *)obj_node->data; + CU_ASSERT_EQUAL(srp->header.object_class, PCEP_OBJ_CLASS_SRP); + CU_ASSERT_EQUAL(srp->header.object_type, PCEP_OBJ_TYPE_SRP); + CU_ASSERT_EQUAL(srp->header.encoded_object_length, 20); + CU_ASSERT_PTR_NOT_NULL(srp->header.tlv_list); + assert(srp->header.tlv_list != NULL); + CU_ASSERT_EQUAL(srp->header.tlv_list->num_entries, 1); + CU_ASSERT_EQUAL(srp->srp_id_number, 1); + CU_ASSERT_FALSE(srp->flag_lsp_remove); + + /* LSP object */ + obj_node = obj_node->next_node; + struct pcep_object_lsp *lsp = (struct pcep_object_lsp *)obj_node->data; + CU_ASSERT_EQUAL(lsp->header.object_class, PCEP_OBJ_CLASS_LSP); + CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP); + CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 48); + CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list); + assert(lsp->header.tlv_list != NULL); + CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 2); + CU_ASSERT_EQUAL(lsp->plsp_id, 0); + CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN); + CU_ASSERT_TRUE(lsp->flag_a); + CU_ASSERT_TRUE(lsp->flag_d); + CU_ASSERT_TRUE(lsp->flag_c); + CU_ASSERT_FALSE(lsp->flag_r); + CU_ASSERT_FALSE(lsp->flag_s); + + /* Endpoint object */ + obj_node = obj_node->next_node; + struct pcep_object_endpoints_ipv4 *endpoint = + (struct pcep_object_endpoints_ipv4 *)obj_node->data; + CU_ASSERT_EQUAL(endpoint->header.object_class, + PCEP_OBJ_CLASS_ENDPOINTS); + CU_ASSERT_EQUAL(endpoint->header.object_type, + PCEP_OBJ_TYPE_ENDPOINT_IPV4); + CU_ASSERT_EQUAL(endpoint->header.encoded_object_length, 12); + CU_ASSERT_PTR_NULL(endpoint->header.tlv_list); + CU_ASSERT_EQUAL(endpoint->src_ipv4.s_addr, htonl(0x0a0a0a0a)); + CU_ASSERT_EQUAL(endpoint->dst_ipv4.s_addr, htonl(0x0a0a0a04)); + + /* Inter-Layer object */ + obj_node = obj_node->next_node; + struct pcep_object_inter_layer *inter_layer = + (struct pcep_object_inter_layer *)obj_node->data; + CU_ASSERT_EQUAL(inter_layer->header.object_class, + PCEP_OBJ_CLASS_INTER_LAYER); + CU_ASSERT_EQUAL(inter_layer->header.object_type, + PCEP_OBJ_TYPE_INTER_LAYER); + CU_ASSERT_EQUAL(inter_layer->header.encoded_object_length, 8); + CU_ASSERT_PTR_NULL(inter_layer->header.tlv_list); + CU_ASSERT_TRUE(inter_layer->flag_i); + CU_ASSERT_FALSE(inter_layer->flag_m); + CU_ASSERT_TRUE(inter_layer->flag_t); + + /* Switch-Layer object */ + obj_node = obj_node->next_node; + struct pcep_object_switch_layer *switch_layer = + (struct pcep_object_switch_layer *)obj_node->data; + CU_ASSERT_EQUAL(switch_layer->header.object_class, + PCEP_OBJ_CLASS_SWITCH_LAYER); + CU_ASSERT_EQUAL(switch_layer->header.object_type, + PCEP_OBJ_TYPE_SWITCH_LAYER); + CU_ASSERT_EQUAL(switch_layer->header.encoded_object_length, 8); + CU_ASSERT_PTR_NULL(switch_layer->header.tlv_list); + assert(switch_layer->header.tlv_list == NULL); + CU_ASSERT_PTR_NOT_NULL(switch_layer->switch_layer_rows); + assert(switch_layer->switch_layer_rows != NULL); + CU_ASSERT_EQUAL(switch_layer->switch_layer_rows->num_entries, 1); + struct pcep_object_switch_layer_row *switch_layer_row = + (struct pcep_object_switch_layer_row *) + switch_layer->switch_layer_rows->head->data; + CU_ASSERT_EQUAL(switch_layer_row->lsp_encoding_type, 0); + CU_ASSERT_EQUAL(switch_layer_row->switching_type, 0); + CU_ASSERT_FALSE(switch_layer_row->flag_i); + + /* ERO object */ + obj_node = obj_node->next_node; + struct pcep_object_ro *ero = (struct pcep_object_ro *)obj_node->data; + CU_ASSERT_EQUAL(ero->header.object_class, PCEP_OBJ_CLASS_ERO); + CU_ASSERT_EQUAL(ero->header.object_type, PCEP_OBJ_TYPE_ERO); + CU_ASSERT_EQUAL(ero->header.encoded_object_length, 4); + CU_ASSERT_PTR_NULL(ero->header.tlv_list); + + pcep_msg_free_message_list(msg_list); + close(fd); + unlink(filename); +} + +void test_validate_message_header() +{ + uint8_t pcep_message_invalid_version[] = {0x40, 0x01, 0x04, 0x00}; + uint8_t pcep_message_invalid_flags[] = {0x22, 0x01, 0x04, 0x00}; + uint8_t pcep_message_invalid_length[] = {0x20, 0x01, 0x00, 0x00}; + uint8_t pcep_message_invalid_type[] = {0x20, 0xff, 0x04, 0x00}; + uint8_t pcep_message_valid[] = {0x20, 0x01, 0x00, 0x04}; + + /* Verify invalid message header version */ + CU_ASSERT_TRUE( + pcep_decode_validate_msg_header(pcep_message_invalid_version) + < 0); + + /* Verify invalid message header flags */ + CU_ASSERT_TRUE( + pcep_decode_validate_msg_header(pcep_message_invalid_flags) + < 0); + + /* Verify invalid message header lengths */ + CU_ASSERT_TRUE( + pcep_decode_validate_msg_header(pcep_message_invalid_length) + < 0); + pcep_message_invalid_length[3] = 0x05; + CU_ASSERT_TRUE( + pcep_decode_validate_msg_header(pcep_message_invalid_length) + < 0); + + /* Verify invalid message header types */ + CU_ASSERT_TRUE( + pcep_decode_validate_msg_header(pcep_message_invalid_type) < 0); + pcep_message_invalid_type[1] = 0x00; + CU_ASSERT_TRUE( + pcep_decode_validate_msg_header(pcep_message_invalid_type) < 0); + + /* Verify a valid message header */ + CU_ASSERT_EQUAL(pcep_decode_validate_msg_header(pcep_message_valid), 4); +} + +/* Internal util function */ +struct pcep_message *create_message(uint8_t msg_type, uint8_t obj1_class, + uint8_t obj2_class, uint8_t obj3_class, + uint8_t obj4_class) +{ + struct pcep_message *msg = + pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message)); + msg->obj_list = dll_initialize(); + msg->msg_header = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct pcep_message_header)); + msg->msg_header->type = msg_type; + msg->encoded_message = NULL; + + if (obj1_class > 0) { + struct pcep_object_header *obj_hdr = pceplib_malloc( + PCEPLIB_MESSAGES, sizeof(struct pcep_object_header)); + obj_hdr->object_class = obj1_class; + obj_hdr->tlv_list = NULL; + dll_append(msg->obj_list, obj_hdr); + } + + if (obj2_class > 0) { + struct pcep_object_header *obj_hdr = pceplib_malloc( + PCEPLIB_MESSAGES, sizeof(struct pcep_object_header)); + obj_hdr->object_class = obj2_class; + obj_hdr->tlv_list = NULL; + dll_append(msg->obj_list, obj_hdr); + } + + if (obj3_class > 0) { + struct pcep_object_header *obj_hdr = pceplib_malloc( + PCEPLIB_MESSAGES, sizeof(struct pcep_object_header)); + obj_hdr->object_class = obj3_class; + obj_hdr->tlv_list = NULL; + dll_append(msg->obj_list, obj_hdr); + } + + if (obj4_class > 0) { + struct pcep_object_header *obj_hdr = pceplib_malloc( + PCEPLIB_MESSAGES, sizeof(struct pcep_object_header)); + obj_hdr->object_class = obj4_class; + obj_hdr->tlv_list = NULL; + dll_append(msg->obj_list, obj_hdr); + } + + return msg; +} + +void test_validate_message_objects() +{ + /* Valid Open message */ + struct pcep_message *msg = + create_message(PCEP_TYPE_OPEN, PCEP_OBJ_CLASS_OPEN, 0, 0, 0); + CU_ASSERT_TRUE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Valid KeepAlive message */ + msg = create_message(PCEP_TYPE_KEEPALIVE, 0, 0, 0, 0); + CU_ASSERT_TRUE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Valid PcReq message */ + /* Using object_class=255 to verify it can take any object */ + msg = create_message(PCEP_TYPE_PCREQ, PCEP_OBJ_CLASS_RP, + PCEP_OBJ_CLASS_ENDPOINTS, any_obj_class, 0); + CU_ASSERT_TRUE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Valid PcRep message */ + msg = create_message(PCEP_TYPE_PCREP, PCEP_OBJ_CLASS_RP, any_obj_class, + 0, 0); + CU_ASSERT_TRUE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Valid Notify message */ + msg = create_message(PCEP_TYPE_PCNOTF, PCEP_OBJ_CLASS_NOTF, + any_obj_class, 0, 0); + CU_ASSERT_TRUE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Valid Error message */ + msg = create_message(PCEP_TYPE_ERROR, PCEP_OBJ_CLASS_ERROR, + any_obj_class, 0, 0); + CU_ASSERT_TRUE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Valid Close message */ + msg = create_message(PCEP_TYPE_CLOSE, PCEP_OBJ_CLASS_CLOSE, 0, 0, 0); + CU_ASSERT_TRUE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Valid Report message */ + msg = create_message(PCEP_TYPE_REPORT, PCEP_OBJ_CLASS_SRP, + PCEP_OBJ_CLASS_LSP, any_obj_class, any_obj_class); + CU_ASSERT_TRUE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Valid Update message */ + msg = create_message(PCEP_TYPE_UPDATE, PCEP_OBJ_CLASS_SRP, + PCEP_OBJ_CLASS_LSP, any_obj_class, any_obj_class); + CU_ASSERT_TRUE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Valid Initiate message */ + msg = create_message(PCEP_TYPE_INITIATE, PCEP_OBJ_CLASS_SRP, + PCEP_OBJ_CLASS_LSP, any_obj_class, any_obj_class); + CU_ASSERT_TRUE(validate_message_objects(msg)); + pcep_msg_free_message(msg); +} + +void test_validate_message_objects_invalid() +{ + /* unsupported message ID = 0 + * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */ + struct pcep_message *msg = create_message(0, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Open message + * {PCEP_OBJ_CLASS_OPEN, NO_OBJECT, NO_OBJECT, NO_OBJECT} */ + msg = create_message(PCEP_TYPE_OPEN, 0, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_OPEN, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_OPEN, PCEP_OBJ_CLASS_OPEN, any_obj_class, + 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* KeepAlive message + * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */ + msg = create_message(PCEP_TYPE_KEEPALIVE, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* PcReq message + * {PCEP_OBJ_CLASS_RP, PCEP_OBJ_CLASS_ENDPOINTS, ANY_OBJECT, ANY_OBJECT} + */ + msg = create_message(PCEP_TYPE_PCREQ, 0, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_PCREQ, PCEP_OBJ_CLASS_RP, any_obj_class, + 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* PcRep message + * {PCEP_OBJ_CLASS_RP, ANY_OBJECT, ANY_OBJECT, ANY_OBJECT} */ + msg = create_message(PCEP_TYPE_PCREP, 0, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_PCREP, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Notify message + * {PCEP_OBJ_CLASS_NOTF, ANY_OBJECT, ANY_OBJECT, ANY_OBJECT} */ + msg = create_message(PCEP_TYPE_PCNOTF, 0, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_PCNOTF, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Error message + * {PCEP_OBJ_CLASS_ERROR, ANY_OBJECT, ANY_OBJECT, ANY_OBJECT} */ + msg = create_message(PCEP_TYPE_ERROR, 0, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_ERROR, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Close message + * {PCEP_OBJ_CLASS_CLOSE, NO_OBJECT, NO_OBJECT, NO_OBJECT} */ + msg = create_message(PCEP_TYPE_CLOSE, 0, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_CLOSE, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* unsupported message ID = 8 + * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */ + msg = create_message(8, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* unsupported message ID = 9 + * {NO_OBJECT, NO_OBJECT, NO_OBJECT, NO_OBJECT} */ + msg = create_message(9, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Report message + * {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, ANY_OBJECT} */ + msg = create_message(PCEP_TYPE_REPORT, 0, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_REPORT, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_REPORT, PCEP_OBJ_CLASS_SRP, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_REPORT, PCEP_OBJ_CLASS_SRP, + any_obj_class, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Update message + * {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, ANY_OBJECT} */ + msg = create_message(PCEP_TYPE_UPDATE, 0, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_UPDATE, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_UPDATE, PCEP_OBJ_CLASS_SRP, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_UPDATE, PCEP_OBJ_CLASS_SRP, + any_obj_class, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + /* Initiate message + * {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, ANY_OBJECT} */ + msg = create_message(PCEP_TYPE_INITIATE, 0, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_INITIATE, any_obj_class, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_INITIATE, PCEP_OBJ_CLASS_SRP, 0, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); + + msg = create_message(PCEP_TYPE_INITIATE, PCEP_OBJ_CLASS_SRP, + any_obj_class, 0, 0); + CU_ASSERT_FALSE(validate_message_objects(msg)); + pcep_msg_free_message(msg); +} diff --git a/pceplib/test/pcep_msg_tools_test.h b/pceplib/test/pcep_msg_tools_test.h new file mode 100644 index 0000000..dc66390 --- /dev/null +++ b/pceplib/test/pcep_msg_tools_test.h @@ -0,0 +1,48 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_MSG_TOOLS_TEST_H_ +#define PCEP_MSG_TOOLS_TEST_H_ + + +int pcep_tools_test_suite_setup(void); +int pcep_tools_test_suite_teardown(void); +void pcep_tools_test_setup(void); +void pcep_tools_test_teardown(void); +void test_pcep_msg_read_pcep_initiate(void); +void test_pcep_msg_read_pcep_initiate2(void); +void test_pcep_msg_read_pcep_update(void); +void test_pcep_msg_read_pcep_open(void); +void test_pcep_msg_read_pcep_open_initiate(void); +void test_validate_message_header(void); +void test_validate_message_objects(void); +void test_validate_message_objects_invalid(void); +void test_pcep_msg_read_pcep_open_cisco_pce(void); +void test_pcep_msg_read_pcep_update_cisco_pce(void); +void test_pcep_msg_read_pcep_report_cisco_pcc(void); +void test_pcep_msg_read_pcep_initiate_cisco_pcc(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_pcc_api_test.c b/pceplib/test/pcep_pcc_api_test.c new file mode 100644 index 0000000..9106671 --- /dev/null +++ b/pceplib/test/pcep_pcc_api_test.c @@ -0,0 +1,299 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <netdb.h> // gethostbyname +#include <pthread.h> +#include <stdlib.h> +#include <unistd.h> + +#include <CUnit/CUnit.h> + +#include "pcep_pcc_api.h" +#include "pcep_pcc_api_test.h" +#include "pcep_socket_comm_mock.h" +#include "pcep_utils_memory.h" + +extern pcep_event_queue *session_logic_event_queue_; +extern const char MESSAGE_RECEIVED_STR[]; +extern const char UNKNOWN_EVENT_STR[]; + +/* + * Test suite setup and teardown called before AND after the test suite. + */ + +int pcep_pcc_api_test_suite_setup() +{ + pceplib_memory_reset(); + return 0; +} + +int pcep_pcc_api_test_suite_teardown() +{ + printf("\n"); + pceplib_memory_dump(); + return 0; +} + +/* + * Test case setup and teardown called before AND after each test. + */ + +void pcep_pcc_api_test_setup() +{ + setup_mock_socket_comm_info(); +} + + +void pcep_pcc_api_test_teardown() +{ + teardown_mock_socket_comm_info(); +} + +/* + * Unit test cases + */ + +void test_initialize_pcc() +{ + CU_ASSERT_TRUE(initialize_pcc()); + /* Give the PCC time to initialize */ + sleep(1); + CU_ASSERT_TRUE(destroy_pcc()); +} + +void test_connect_pce() +{ + pcep_configuration *config = create_default_pcep_configuration(); + struct hostent *host_info = gethostbyname("localhost"); + struct in_addr dest_address; + memcpy(&dest_address, host_info->h_addr, host_info->h_length); + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + initialize_pcc(); + + pcep_session *session = connect_pce(config, &dest_address); + + CU_ASSERT_PTR_NOT_NULL(session); + CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 1); + /* What gets saved in the mock is the msg byte buffer. The msg struct + * was deleted when it was sent. Instead of inspecting the msg byte + * buffer, lets just decode it. */ + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + struct pcep_message *open_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(open_msg); + assert(open_msg != NULL); + assert(open_msg->msg_header != NULL); + CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN); + + pcep_msg_free_message(open_msg); + destroy_pcep_session(session); + destroy_pcep_configuration(config); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + destroy_pcc(); +} + +void test_connect_pce_ipv6() +{ + pcep_configuration *config = create_default_pcep_configuration(); + struct in6_addr dest_address; + dest_address.__in6_u.__u6_addr32[0] = 0; + dest_address.__in6_u.__u6_addr32[1] = 0; + dest_address.__in6_u.__u6_addr32[2] = 0; + dest_address.__in6_u.__u6_addr32[3] = htonl(1); + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + initialize_pcc(); + + pcep_session *session = connect_pce_ipv6(config, &dest_address); + + CU_ASSERT_PTR_NOT_NULL(session); + assert(session != NULL); + CU_ASSERT_TRUE(session->socket_comm_session->is_ipv6); + CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 1); + /* What gets saved in the mock is the msg byte buffer. The msg struct + * was deleted when it was sent. Instead of inspecting the msg byte + * buffer, lets just decode it. */ + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + struct pcep_message *open_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(open_msg); + assert(open_msg != NULL); + CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN); + + pcep_msg_free_message(open_msg); + destroy_pcep_session(session); + destroy_pcep_configuration(config); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + destroy_pcc(); +} + +void test_connect_pce_with_src_ip() +{ + pcep_configuration *config = create_default_pcep_configuration(); + struct hostent *host_info = gethostbyname("localhost"); + struct in_addr dest_address; + memcpy(&dest_address, host_info->h_addr, host_info->h_length); + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + config->src_ip.src_ipv4.s_addr = 0x0a0a0102; + + initialize_pcc(); + + pcep_session *session = connect_pce(config, &dest_address); + + CU_ASSERT_PTR_NOT_NULL(session); + CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 1); + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + struct pcep_message *open_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(open_msg); + assert(open_msg != NULL); + assert(open_msg->msg_header != NULL); + CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN); + + pcep_msg_free_message(open_msg); + destroy_pcep_session(session); + destroy_pcep_configuration(config); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + destroy_pcc(); +} + +void test_disconnect_pce() +{ + pcep_configuration *config = create_default_pcep_configuration(); + struct hostent *host_info = gethostbyname("localhost"); + struct in_addr dest_address; + memcpy(&dest_address, host_info->h_addr, host_info->h_length); + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + initialize_pcc(); + + pcep_session *session = connect_pce(config, &dest_address); + disconnect_pce(session); + + CU_ASSERT_EQUAL(mock_info->sent_message_list->num_entries, 2); + + /* First there should be an open message from connect_pce() */ + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + struct pcep_message *msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(msg); + assert(msg != NULL); + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_OPEN); + pcep_msg_free_message(msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + /* Then there should be a close message from disconnect_pce() */ + encoded_msg = dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(msg); + assert(msg != NULL); + assert(msg->msg_header != NULL); + CU_ASSERT_EQUAL(msg->msg_header->type, PCEP_TYPE_CLOSE); + + pcep_msg_free_message(msg); + destroy_pcep_session(session); + destroy_pcep_configuration(config); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + destroy_pcc(); +} + + +void test_send_message() +{ + pcep_configuration *config = create_default_pcep_configuration(); + struct hostent *host_info = gethostbyname("localhost"); + struct in_addr dest_address; + + initialize_pcc(); + + memcpy(&dest_address, host_info->h_addr, host_info->h_length); + pcep_session *session = connect_pce(config, &dest_address); + verify_socket_comm_times_called(0, 0, 1, 1, 0, 0, 0); + + struct pcep_message *msg = pcep_msg_create_keepalive(); + send_message(session, msg, false); + + verify_socket_comm_times_called(0, 0, 1, 2, 0, 0, 0); + + pcep_msg_free_message(msg); + destroy_pcep_session(session); + destroy_pcep_configuration(config); + + destroy_pcc(); +} + +void test_event_queue() +{ + /* This initializes the event_queue */ + CU_ASSERT_TRUE(initialize_pcc()); + + /* Verify correct behavior when the queue is empty */ + CU_ASSERT_TRUE(event_queue_is_empty()); + CU_ASSERT_EQUAL(event_queue_num_events_available(), 0); + CU_ASSERT_PTR_NULL(event_queue_get_event()); + destroy_pcep_event(NULL); + + /* Create an empty event and put it on the queue */ + pcep_event *event = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event)); + memset(event, 0, sizeof(pcep_event)); + pthread_mutex_lock(&session_logic_event_queue_->event_queue_mutex); + queue_enqueue(session_logic_event_queue_->event_queue, event); + pthread_mutex_unlock(&session_logic_event_queue_->event_queue_mutex); + + /* Verify correct behavior when there is an entry in the queue */ + CU_ASSERT_FALSE(event_queue_is_empty()); + CU_ASSERT_EQUAL(event_queue_num_events_available(), 1); + pcep_event *queued_event = event_queue_get_event(); + CU_ASSERT_PTR_NOT_NULL(queued_event); + CU_ASSERT_PTR_EQUAL(event, queued_event); + destroy_pcep_event(queued_event); + + CU_ASSERT_TRUE(destroy_pcc()); +} + +void test_get_event_type_str() +{ + CU_ASSERT_EQUAL(strcmp(get_event_type_str(MESSAGE_RECEIVED), + MESSAGE_RECEIVED_STR), + 0); + CU_ASSERT_EQUAL(strcmp(get_event_type_str(1000), UNKNOWN_EVENT_STR), 0); +} diff --git a/pceplib/test/pcep_pcc_api_test.h b/pceplib/test/pcep_pcc_api_test.h new file mode 100644 index 0000000..d3db96e --- /dev/null +++ b/pceplib/test/pcep_pcc_api_test.h @@ -0,0 +1,43 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_PCC_API_TEST_ +#define PCEP_PCC_API_TEST_ + +int pcep_pcc_api_test_suite_setup(void); +int pcep_pcc_api_test_suite_teardown(void); +void pcep_pcc_api_test_setup(void); +void pcep_pcc_api_test_teardown(void); +void test_initialize_pcc(void); +void test_connect_pce(void); +void test_connect_pce_ipv6(void); +void test_connect_pce_with_src_ip(void); +void test_disconnect_pce(void); +void test_send_message(void); +void test_event_queue(void); +void test_get_event_type_str(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_pcc_api_tests.c b/pceplib/test/pcep_pcc_api_tests.c new file mode 100644 index 0000000..5d9e92c --- /dev/null +++ b/pceplib/test/pcep_pcc_api_tests.c @@ -0,0 +1,92 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <CUnit/Basic.h> +#include <CUnit/CUnit.h> +#include <CUnit/TestDB.h> + +#include "pcep_pcc_api_test.h" + +int main(int argc, char **argv) +{ + /* Unused parameters cause compilation warnings */ + (void)argc; + (void)argv; + + CU_initialize_registry(); + + /* + * Tests defined in pcep_socket_comm_test.c + */ + CU_pSuite test_pcc_api_suite = CU_add_suite_with_setup_and_teardown( + "PCEP PCC API Test Suite", + pcep_pcc_api_test_suite_setup, // suite setup and cleanup + // function pointers + pcep_pcc_api_test_suite_teardown, + pcep_pcc_api_test_setup, // test case setup function pointer + pcep_pcc_api_test_teardown); // test case teardown function + // pointer + + CU_add_test(test_pcc_api_suite, "test_initialize_pcc", + test_initialize_pcc); + CU_add_test(test_pcc_api_suite, "test_connect_pce", test_connect_pce); + CU_add_test(test_pcc_api_suite, "test_connect_pce_ipv6", + test_connect_pce_ipv6); + CU_add_test(test_pcc_api_suite, "test_connect_pce_with_src_ip", + test_connect_pce_with_src_ip); + CU_add_test(test_pcc_api_suite, "test_disconnect_pce", + test_disconnect_pce); + CU_add_test(test_pcc_api_suite, "test_send_message", test_send_message); + CU_add_test(test_pcc_api_suite, "test_event_queue", test_event_queue); + CU_add_test(test_pcc_api_suite, "test_get_event_type_str", + test_get_event_type_str); + + /* + * Run the tests and cleanup. + */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_FailureRecord *failure_record = CU_get_failure_list(); + if (failure_record != NULL) { + printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n"); + do { + printf("\t [%s] [%s] [%s:%d]\n", + failure_record->pSuite->pName, + failure_record->pTest->pName, + failure_record->strFileName, + failure_record->uiLineNumber); + failure_record = failure_record->pNext; + + } while (failure_record != NULL); + } + + CU_pRunSummary run_summary = CU_get_run_summary(); + int result = run_summary->nTestsFailed; + CU_cleanup_registry(); + + return result; +} diff --git a/pceplib/test/pcep_pcc_api_tests_valgrind.sh b/pceplib/test/pcep_pcc_api_tests_valgrind.sh new file mode 100755 index 0000000..74494b7 --- /dev/null +++ b/pceplib/test/pcep_pcc_api_tests_valgrind.sh @@ -0,0 +1,2 @@ +source pceplib/test/pcep_tests_valgrind.sh +valgrind_test pceplib/test/pcep_pcc_api_tests diff --git a/pceplib/test/pcep_session_logic_loop_test.c b/pceplib/test/pcep_session_logic_loop_test.c new file mode 100644 index 0000000..7a42715 --- /dev/null +++ b/pceplib/test/pcep_session_logic_loop_test.c @@ -0,0 +1,245 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <CUnit/CUnit.h> + +#include "pcep_msg_encoding.h" +#include "pcep_session_logic.h" +#include "pcep_session_logic_internals.h" +#include "pcep_timers.h" +#include "pcep_utils_ordered_list.h" +#include "pcep_utils_memory.h" +#include "pcep_session_logic_loop_test.h" + + +extern pcep_session_logic_handle *session_logic_handle_; +extern pcep_event_queue *session_logic_event_queue_; + +/* + * Test suite setup and teardown called before AND after the test suite. + */ + +int pcep_session_logic_loop_test_suite_setup(void) +{ + pceplib_memory_reset(); + return 0; +} + +int pcep_session_logic_loop_test_suite_teardown(void) +{ + printf("\n"); + pceplib_memory_dump(); + return 0; +} + + +/* + * Test case setup and teardown called before AND after each test. + */ + +void pcep_session_logic_loop_test_setup() +{ + /* We need to setup the session_logic_handle_ without starting the + * thread */ + session_logic_handle_ = pceplib_malloc( + PCEPLIB_INFRA, sizeof(pcep_session_logic_handle)); + memset(session_logic_handle_, 0, sizeof(pcep_session_logic_handle)); + session_logic_handle_->active = true; + session_logic_handle_->session_list = + ordered_list_initialize(pointer_compare_function); + session_logic_handle_->session_event_queue = queue_initialize(); + pthread_cond_init(&(session_logic_handle_->session_logic_cond_var), + NULL); + pthread_mutex_init(&(session_logic_handle_->session_logic_mutex), NULL); + pthread_mutex_init(&(session_logic_handle_->session_list_mutex), NULL); + + pthread_mutex_lock(&(session_logic_handle_->session_logic_mutex)); + session_logic_handle_->session_logic_condition = true; + pthread_cond_signal(&(session_logic_handle_->session_logic_cond_var)); + pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex)); + + session_logic_event_queue_ = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event_queue)); + memset(session_logic_event_queue_, 0, sizeof(pcep_event_queue)); + session_logic_event_queue_->event_queue = queue_initialize(); +} + + +void pcep_session_logic_loop_test_teardown() +{ + ordered_list_destroy(session_logic_handle_->session_list); + queue_destroy(session_logic_handle_->session_event_queue); + pthread_mutex_unlock(&(session_logic_handle_->session_logic_mutex)); + pthread_mutex_destroy(&(session_logic_handle_->session_logic_mutex)); + pthread_mutex_destroy(&(session_logic_handle_->session_list_mutex)); + pceplib_free(PCEPLIB_INFRA, session_logic_handle_); + session_logic_handle_ = NULL; + + queue_destroy(session_logic_event_queue_->event_queue); + pceplib_free(PCEPLIB_INFRA, session_logic_event_queue_); + session_logic_event_queue_ = NULL; +} + + +/* + * Test cases + */ + +void test_session_logic_loop_null_data() +{ + /* Just testing that it does not core dump */ + session_logic_loop(NULL); +} + + +void test_session_logic_loop_inactive() +{ + session_logic_handle_->active = false; + + session_logic_loop(session_logic_handle_); +} + + +void test_session_logic_msg_ready_handler() +{ + /* Just testing that it does not core dump */ + CU_ASSERT_EQUAL(session_logic_msg_ready_handler(NULL, 0), -1); + + /* Read from an empty file should return 0, thus + * session_logic_msg_ready_handler returns -1 */ + mode_t oldumask; + oldumask = umask(S_IXUSR | S_IXGRP | S_IWOTH | S_IROTH | S_IXOTH); + /* Set umask before anything for security */ + umask(0027); + char tmpfile[] = "/tmp/pceplib_XXXXXX"; + int fd = mkstemp(tmpfile); + umask(oldumask); + if (fd == -1) { + CU_ASSERT_TRUE(fd >= 0); + return; + } + pcep_session session; + memset(&session, 0, sizeof(pcep_session)); + session.session_id = 100; + CU_ASSERT_EQUAL(session_logic_msg_ready_handler(&session, fd), 0); + CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries, + 1); + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCE_CLOSED_SOCKET, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + pcep_session_event *socket_event = (pcep_session_event *)queue_dequeue( + session_logic_handle_->session_event_queue); + CU_ASSERT_PTR_NOT_NULL(socket_event); + assert(socket_event != NULL); + CU_ASSERT_TRUE(socket_event->socket_closed); + pceplib_free(PCEPLIB_INFRA, socket_event); + + /* A pcep_session_event should be created */ + struct pcep_versioning *versioning = create_default_pcep_versioning(); + struct pcep_message *keep_alive_msg = pcep_msg_create_keepalive(); + pcep_encode_message(keep_alive_msg, versioning); + int retval = write(fd, (char *)keep_alive_msg->encoded_message, + keep_alive_msg->encoded_message_length); + CU_ASSERT_TRUE(retval > 0); + lseek(fd, 0, SEEK_SET); + CU_ASSERT_EQUAL(session_logic_msg_ready_handler(&session, fd), + keep_alive_msg->encoded_message_length); + CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries, + 1); + socket_event = (pcep_session_event *)queue_dequeue( + session_logic_handle_->session_event_queue); + CU_ASSERT_PTR_NOT_NULL(socket_event); + assert(socket_event != NULL); + CU_ASSERT_FALSE(socket_event->socket_closed); + CU_ASSERT_PTR_EQUAL(socket_event->session, &session); + CU_ASSERT_EQUAL(socket_event->expired_timer_id, TIMER_ID_NOT_SET); + CU_ASSERT_PTR_NOT_NULL(socket_event->received_msg_list); + pcep_msg_free_message_list(socket_event->received_msg_list); + pcep_msg_free_message(keep_alive_msg); + destroy_pcep_versioning(versioning); + pceplib_free(PCEPLIB_INFRA, socket_event); + close(fd); + unlink(tmpfile); +} + + +void test_session_logic_conn_except_notifier() +{ + /* Just testing that it does not core dump */ + session_logic_conn_except_notifier(NULL, 1); + + /* A pcep_session_event should be created */ + pcep_session session; + memset(&session, 0, sizeof(pcep_session)); + session.session_id = 100; + session_logic_conn_except_notifier(&session, 10); + CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries, + 1); + pcep_session_event *socket_event = (pcep_session_event *)queue_dequeue( + session_logic_handle_->session_event_queue); + CU_ASSERT_PTR_NOT_NULL_FATAL(socket_event); + assert(socket_event != NULL); + CU_ASSERT_TRUE(socket_event->socket_closed); + CU_ASSERT_PTR_EQUAL(socket_event->session, &session); + CU_ASSERT_EQUAL(socket_event->expired_timer_id, TIMER_ID_NOT_SET); + CU_ASSERT_PTR_NULL(socket_event->received_msg_list); + + pceplib_free(PCEPLIB_INFRA, socket_event); +} + + +void test_session_logic_timer_expire_handler() +{ + /* Just testing that it does not core dump */ + session_logic_timer_expire_handler(NULL, 42); + + /* A pcep_session_event should be created */ + pcep_session session; + memset(&session, 0, sizeof(pcep_session)); + session.session_id = 100; + session_logic_timer_expire_handler(&session, 42); + CU_ASSERT_EQUAL(session_logic_handle_->session_event_queue->num_entries, + 1); + pcep_session_event *socket_event = (pcep_session_event *)queue_dequeue( + session_logic_handle_->session_event_queue); + CU_ASSERT_PTR_NOT_NULL_FATAL(socket_event); + assert(socket_event != NULL); + CU_ASSERT_FALSE(socket_event->socket_closed); + CU_ASSERT_PTR_EQUAL(socket_event->session, &session); + CU_ASSERT_EQUAL(socket_event->expired_timer_id, 42); + CU_ASSERT_PTR_NULL(socket_event->received_msg_list); + + pceplib_free(PCEPLIB_INFRA, socket_event); +} diff --git a/pceplib/test/pcep_session_logic_loop_test.h b/pceplib/test/pcep_session_logic_loop_test.h new file mode 100644 index 0000000..ae3c3e3 --- /dev/null +++ b/pceplib/test/pcep_session_logic_loop_test.h @@ -0,0 +1,40 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_SESSION_LOGIC_LOOP_TEST_H_ +#define PCEP_SESSION_LOGIC_LOOP_TEST_H_ + +int pcep_session_logic_loop_test_suite_setup(void); +int pcep_session_logic_loop_test_suite_teardown(void); +void pcep_session_logic_loop_test_setup(void); +void pcep_session_logic_loop_test_teardown(void); +void test_session_logic_loop_null_data(void); +void test_session_logic_loop_inactive(void); +void test_session_logic_msg_ready_handler(void); +void test_session_logic_conn_except_notifier(void); +void test_session_logic_timer_expire_handler(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_session_logic_states_test.c b/pceplib/test/pcep_session_logic_states_test.c new file mode 100644 index 0000000..17ddae6 --- /dev/null +++ b/pceplib/test/pcep_session_logic_states_test.c @@ -0,0 +1,933 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <CUnit/CUnit.h> + +#include "pcep_socket_comm_mock.h" +#include "pcep_session_logic.h" +#include "pcep_session_logic_internals.h" +#include "pcep_timers.h" +#include "pcep_utils_ordered_list.h" +#include "pcep_utils_double_linked_list.h" +#include "pcep_utils_memory.h" +#include "pcep_msg_objects.h" +#include "pcep_msg_tools.h" +#include "pcep_session_logic_states_test.h" + +/* Functions being tested */ +extern pcep_session_logic_handle *session_logic_handle_; +extern pcep_event_queue *session_logic_event_queue_; + +static pcep_session_event event; +static pcep_session session; +/* A message list is a dll of struct pcep_messages_list_node items */ +static double_linked_list *msg_list; +struct pcep_message *message; +static bool free_msg_list; +static bool msg_enqueued; +/* Forward declaration */ +void destroy_message_for_test(void); +void create_message_for_test(uint8_t msg_type, bool free_msg_list_at_teardown, + bool was_msg_enqueued); +void test_handle_timer_event_open_keep_alive(void); + +/* + * Test suite setup and teardown called before AND after the test suite. + */ + +int pcep_session_logic_states_test_suite_setup(void) +{ + pceplib_memory_reset(); + return 0; +} + +int pcep_session_logic_states_test_suite_teardown(void) +{ + printf("\n"); + pceplib_memory_dump(); + return 0; +} + +/* + * Test case setup and teardown called before AND after each test. + */ + +void pcep_session_logic_states_test_setup() +{ + session_logic_handle_ = pceplib_malloc( + PCEPLIB_INFRA, sizeof(pcep_session_logic_handle)); + memset(session_logic_handle_, 0, sizeof(pcep_session_logic_handle)); + + session_logic_event_queue_ = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_event_queue)); + memset(session_logic_event_queue_, 0, sizeof(pcep_event_queue)); + session_logic_event_queue_->event_queue = queue_initialize(); + + memset(&session, 0, sizeof(pcep_session)); + session.pcc_config.keep_alive_seconds = 5; + session.pcc_config.keep_alive_pce_negotiated_timer_seconds = 5; + session.pcc_config.min_keep_alive_seconds = 1; + session.pcc_config.max_keep_alive_seconds = 10; + session.pcc_config.dead_timer_seconds = 5; + session.pcc_config.dead_timer_pce_negotiated_seconds = 5; + session.pcc_config.min_dead_timer_seconds = 1; + session.pcc_config.max_dead_timer_seconds = 10; + session.pcc_config.max_unknown_messages = 2; + memcpy(&session.pce_config, &session.pcc_config, + sizeof(pcep_configuration)); + session.num_unknown_messages_time_queue = queue_initialize(); + + memset(&event, 0, sizeof(pcep_session_event)); + event.socket_closed = false; + event.session = &session; + + setup_mock_socket_comm_info(); + free_msg_list = false; + msg_enqueued = false; +} + + +void pcep_session_logic_states_test_teardown() +{ + destroy_message_for_test(); + pceplib_free(PCEPLIB_INFRA, session_logic_handle_); + queue_destroy(session_logic_event_queue_->event_queue); + pceplib_free(PCEPLIB_INFRA, session_logic_event_queue_); + session_logic_handle_ = NULL; + session_logic_event_queue_ = NULL; + queue_destroy_with_data(session.num_unknown_messages_time_queue); + teardown_mock_socket_comm_info(); +} + +void create_message_for_test(uint8_t msg_type, bool free_msg_list_at_teardown, + bool was_msg_enqueued) +{ + /* See the comments in destroy_message_for_test() about these 2 + * variables */ + free_msg_list = free_msg_list_at_teardown; + msg_enqueued = was_msg_enqueued; + + message = pceplib_malloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message)); + memset(message, 0, sizeof(struct pcep_message)); + + message->msg_header = pceplib_malloc( + PCEPLIB_MESSAGES, sizeof(struct pcep_message_header)); + memset(message->msg_header, 0, sizeof(struct pcep_message_header)); + message->obj_list = dll_initialize(); + message->msg_header->type = msg_type; + + msg_list = dll_initialize(); + dll_append(msg_list, message); + event.received_msg_list = msg_list; +} + +void destroy_message_for_test() +{ + /* Some test cases internally free the message list, so we dont + * want to double free it */ + if (free_msg_list == true) { + /* This will destroy both the msg_list and the obj_list */ + pcep_msg_free_message_list(msg_list); + } + + /* Some tests cause the message to be enqueued and dont delete it, + * so we have to delete it here */ + if (msg_enqueued == true) { + pcep_msg_free_message(message); + } +} + +/* + * Test cases + */ + +void test_handle_timer_event_dead_timer() +{ + /* Dead Timer expired */ + event.expired_timer_id = session.timer_id_dead_timer = 100; + + handle_timer_event(&event); + + CU_ASSERT_EQUAL(session.timer_id_dead_timer, TIMER_ID_NOT_SET); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCE_DEAD_TIMER_EXPIRED, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + + /* verify_socket_comm_times_called( + * initialized, teardown, connect, send_message, close_after_write, + * close, destroy); */ + verify_socket_comm_times_called(0, 0, 0, 1, 1, 0, 0); +} + + +void test_handle_timer_event_keep_alive() +{ + /* Keep Alive timer expired */ + event.expired_timer_id = session.timer_id_keep_alive = 200; + + handle_timer_event(&event); + + CU_ASSERT_EQUAL(session.timer_id_keep_alive, TIMER_ID_NOT_SET); + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); +} + + +void test_handle_timer_event_open_keep_wait() +{ + /* Open Keep Wait timer expired */ + event.expired_timer_id = session.timer_id_open_keep_wait = 300; + session.session_state = SESSION_STATE_PCEP_CONNECTING; + handle_timer_event(&event); + + CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + verify_socket_comm_times_called(0, 0, 0, 0, 1, 0, 0); + + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCE_OPEN_KEEP_WAIT_TIMER_EXPIRED, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + + /* If the state is not SESSION_STATE_PCEP_CONNECTED, then nothing should + * happen */ + reset_mock_socket_comm_info(); + session.session_state = SESSION_STATE_UNKNOWN; + event.expired_timer_id = session.timer_id_open_keep_wait = 300; + handle_timer_event(&event); + + CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, 300); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_UNKNOWN); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0); +} + + +void test_handle_timer_event_open_keep_alive() +{ + /* Open Keep Alive timer expired, but the Keep Alive should not be sent + * since the PCE Open has not been received yet */ + event.expired_timer_id = session.timer_id_open_keep_alive = 300; + session.session_state = SESSION_STATE_PCEP_CONNECTING; + session.pce_open_keep_alive_sent = false; + session.pce_open_received = false; + handle_timer_event(&event); + + CU_ASSERT_EQUAL(session.timer_id_open_keep_alive, TIMER_ID_NOT_SET); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING); + CU_ASSERT_FALSE(session.pce_open_keep_alive_sent); + + /* Open Keep Alive timer expired, the Keep Alive should be sent, + * but the session should not be connected, since the PCC Open + * has not been accepted yet */ + event.expired_timer_id = session.timer_id_open_keep_alive = 300; + session.session_state = SESSION_STATE_PCEP_CONNECTING; + session.pce_open_keep_alive_sent = false; + session.pce_open_received = true; + session.pce_open_rejected = false; + session.pcc_open_accepted = false; + handle_timer_event(&event); + + CU_ASSERT_EQUAL(session.timer_id_open_keep_alive, TIMER_ID_NOT_SET); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING); + CU_ASSERT_TRUE(session.pce_open_keep_alive_sent); + + /* Open Keep Alive timer expired, the Keep Alive should be sent, + * and the session is connected */ + event.expired_timer_id = session.timer_id_open_keep_alive = 300; + session.session_state = SESSION_STATE_PCEP_CONNECTING; + session.pce_open_keep_alive_sent = false; + session.pce_open_received = true; + session.pce_open_rejected = false; + session.pcc_open_accepted = true; + handle_timer_event(&event); + + CU_ASSERT_EQUAL(session.timer_id_open_keep_alive, TIMER_ID_NOT_SET); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTED); + CU_ASSERT_FALSE(session.pce_open_keep_alive_sent); +} + + +void test_handle_socket_comm_event_null_params() +{ + /* Verify it doesn't core dump */ + handle_socket_comm_event(NULL); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0); + reset_mock_socket_comm_info(); + + event.received_msg_list = NULL; + handle_socket_comm_event(&event); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0); +} + + +void test_handle_socket_comm_event_close() +{ + event.socket_closed = true; + handle_socket_comm_event(&event); + + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 1, 0); + + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCE_CLOSED_SOCKET, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); +} + + +void test_handle_socket_comm_event_open() +{ + /* + * Test when a PCE Open is received, but the PCC Open has not been + * accepted yet + */ + create_message_for_test(PCEP_TYPE_OPEN, false, true); + struct pcep_object_open *open_object = + pcep_obj_create_open(1, 1, 1, NULL); + dll_append(message->obj_list, open_object); + session.pcc_open_accepted = false; + session.pce_open_received = false; + session.pce_open_accepted = false; + session.timer_id_open_keep_alive = 100; + session.session_state = SESSION_STATE_PCEP_CONNECTING; + + handle_socket_comm_event(&event); + + CU_ASSERT_TRUE(session.pce_open_received); + CU_ASSERT_TRUE(session.pce_open_accepted); + CU_ASSERT_FALSE(session.pce_open_rejected); + CU_ASSERT_FALSE(session.pce_open_keep_alive_sent); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING); + CU_ASSERT_NOT_EQUAL(session.timer_id_open_keep_alive, 100); + /* A keep alive response should NOT be sent yet */ + verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type); + CU_ASSERT_EQUAL(PCEP_TYPE_OPEN, e->message->msg_header->type); + pceplib_free(PCEPLIB_INFRA, e); + destroy_message_for_test(); + + /* + * Test when a PCE Open is received, and the PCC Open has been accepted + */ + create_message_for_test(PCEP_TYPE_OPEN, false, true); + reset_mock_socket_comm_info(); + open_object = pcep_obj_create_open(1, 1, 1, NULL); + dll_append(message->obj_list, open_object); + session.pcc_open_accepted = true; + session.pce_open_received = false; + session.pce_open_accepted = false; + session.session_state = SESSION_STATE_PCEP_CONNECTING; + + handle_socket_comm_event(&event); + + CU_ASSERT_TRUE(session.pce_open_received); + CU_ASSERT_TRUE(session.pce_open_accepted); + CU_ASSERT_FALSE(session.pce_open_rejected); + CU_ASSERT_TRUE(session.pce_open_keep_alive_sent); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTED); + /* A keep alive response should be sent, accepting the Open */ + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 2); + e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type); + CU_ASSERT_EQUAL(PCEP_TYPE_OPEN, e->message->msg_header->type); + pceplib_free(PCEPLIB_INFRA, e); + e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCC_CONNECTED_TO_PCE, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + destroy_message_for_test(); + + /* + * Send a 2nd Open, an error should be sent + */ + create_message_for_test(PCEP_TYPE_OPEN, false, false); + reset_mock_socket_comm_info(); + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + handle_socket_comm_event(&event); + + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 0); + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); + /* What gets saved in the mock is the msg byte buffer. The msg struct + * was deleted when it was sent. Instead of inspecting the msg byte + * buffer, lets just decode it. */ + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + struct pcep_message *msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(msg); + assert(msg != NULL); + CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, msg->msg_header->type); + /* Verify the error object */ + CU_ASSERT_EQUAL(1, msg->obj_list->num_entries); + struct pcep_object_error *error_obj = msg->obj_list->head->data; + CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, error_obj->header.object_class); + CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, error_obj->header.object_type); + CU_ASSERT_EQUAL(PCEP_ERRT_ATTEMPT_TO_ESTABLISH_2ND_PCEP_SESSION, + error_obj->error_type); + CU_ASSERT_EQUAL(PCEP_ERRV_RECVD_INVALID_OPEN_MSG, + error_obj->error_value); + pcep_msg_free_message(msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); +} + + +void test_handle_socket_comm_event_open_error() +{ + /* Test when the PCE rejects the PCC Open with an Error + * that a "corrected" Open message is sent. */ + + create_message_for_test(PCEP_TYPE_ERROR, false, true); + struct pcep_object_error *error_object = pcep_obj_create_error( + PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG); + struct pcep_object_open *error_open_object = + pcep_obj_create_open(1, 1, 1, NULL); + /* The configured [Keep-alive, Dead-timer] values are [5, 5], + * this error open object will request they be changed to [10, 10] */ + error_open_object->open_keepalive = 10; + error_open_object->open_deadtimer = 10; + dll_append(message->obj_list, error_object); + dll_append(message->obj_list, error_open_object); + session.session_state = SESSION_STATE_PCEP_CONNECTING; + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + handle_socket_comm_event(&event); + + CU_ASSERT_TRUE(session.pcc_open_rejected); + CU_ASSERT_FALSE(session.pce_open_keep_alive_sent); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING); + /* Another Open should be sent */ + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 2); + + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCC_SENT_INVALID_OPEN, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + + e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type); + CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, e->message->msg_header->type); + pceplib_free(PCEPLIB_INFRA, e); + + /* Check the Corrected Open Message */ + + /* What gets saved in the mock is the msg byte buffer. The msg struct + * was deleted when it was sent. Instead of inspecting the msg byte + * buffer, lets just decode it. */ + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + assert(encoded_msg != NULL); + struct pcep_message *open_msg_corrected = + pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(open_msg_corrected); + struct pcep_object_open *open_object_corrected = + (struct pcep_object_open *)pcep_obj_get( + open_msg_corrected->obj_list, PCEP_OBJ_CLASS_OPEN); + CU_ASSERT_PTR_NOT_NULL(open_object_corrected); + assert(open_object_corrected != NULL); + /* Verify the Keep-alive and Dead timers have been negotiated */ + CU_ASSERT_EQUAL(error_open_object->open_keepalive, + open_object_corrected->open_keepalive); + CU_ASSERT_EQUAL(error_open_object->open_deadtimer, + open_object_corrected->open_deadtimer); + CU_ASSERT_EQUAL(session.pcc_config.dead_timer_pce_negotiated_seconds, + open_object_corrected->open_deadtimer); + CU_ASSERT_EQUAL( + session.pcc_config.keep_alive_pce_negotiated_timer_seconds, + open_object_corrected->open_keepalive); + CU_ASSERT_NOT_EQUAL( + session.pcc_config.dead_timer_pce_negotiated_seconds, + session.pcc_config.dead_timer_seconds); + CU_ASSERT_NOT_EQUAL( + session.pcc_config.keep_alive_pce_negotiated_timer_seconds, + session.pcc_config.keep_alive_seconds); + + pcep_msg_free_message(open_msg_corrected); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); +} + + +void test_handle_socket_comm_event_keep_alive() +{ + /* Test when a Keep Alive is received, but the PCE Open has not been + * received yet */ + create_message_for_test(PCEP_TYPE_KEEPALIVE, false, false); + session.session_state = SESSION_STATE_PCEP_CONNECTING; + session.timer_id_dead_timer = 100; + session.timer_id_open_keep_wait = 200; + session.pce_open_accepted = false; + session.pce_open_received = false; + session.pcc_open_accepted = false; + + handle_socket_comm_event(&event); + + CU_ASSERT_TRUE(session.pcc_open_accepted); + CU_ASSERT_FALSE(session.pce_open_keep_alive_sent); + CU_ASSERT_FALSE(session.pcc_open_rejected); + CU_ASSERT_FALSE(session.pce_open_accepted); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING); + CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET); + CU_ASSERT_EQUAL(session.timer_id_dead_timer, 100); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 0); + + /* Test when a Keep Alive is received, and the PCE Open has been + * received and accepted */ + create_message_for_test(PCEP_TYPE_KEEPALIVE, false, false); + session.session_state = SESSION_STATE_PCEP_CONNECTING; + session.timer_id_dead_timer = 100; + session.timer_id_open_keep_wait = 200; + session.pce_open_received = true; + session.pce_open_accepted = true; + session.pcc_open_accepted = false; + + handle_socket_comm_event(&event); + + CU_ASSERT_TRUE(session.pcc_open_accepted); + CU_ASSERT_TRUE(session.pce_open_keep_alive_sent); + CU_ASSERT_FALSE(session.pcc_open_rejected); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTED); + CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET); + CU_ASSERT_EQUAL(session.timer_id_dead_timer, 100); + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); + + /* Test when a Keep Alive is received, and the PCE Open has been + * received and rejected */ + create_message_for_test(PCEP_TYPE_KEEPALIVE, false, false); + session.session_state = SESSION_STATE_PCEP_CONNECTING; + session.timer_id_dead_timer = 100; + session.timer_id_open_keep_wait = 200; + session.pce_open_received = true; + session.pce_open_accepted = false; + session.pce_open_rejected = true; + session.pce_open_keep_alive_sent = false; + session.pcc_open_accepted = true; + + handle_socket_comm_event(&event); + + CU_ASSERT_TRUE(session.pcc_open_accepted); + CU_ASSERT_FALSE(session.pce_open_keep_alive_sent); + CU_ASSERT_FALSE(session.pcc_open_rejected); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING); + CU_ASSERT_EQUAL(session.timer_id_open_keep_wait, TIMER_ID_NOT_SET); + CU_ASSERT_EQUAL(session.timer_id_dead_timer, 100); + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); + + /* The session is considered connected, when both the + * PCE and PCC Open messages have been accepted */ + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCC_CONNECTED_TO_PCE, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); +} + + +void test_handle_socket_comm_event_pcrep() +{ + create_message_for_test(PCEP_TYPE_PCREP, false, true); + struct pcep_object_rp *rp = + pcep_obj_create_rp(1, true, true, true, true, 1, NULL); + dll_append(message->obj_list, rp); + + handle_socket_comm_event(&event); + + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0); + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); +} + + +void test_handle_socket_comm_event_pcreq() +{ + create_message_for_test(PCEP_TYPE_PCREQ, false, false); + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + handle_socket_comm_event(&event); + + /* The PCC does not support receiving PcReq messages, so an error should + * be sent */ + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 0); + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + struct pcep_message *error_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(error_msg); + assert(error_msg != NULL); + CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, error_msg->msg_header->type); + /* Verify the error object */ + CU_ASSERT_EQUAL(1, error_msg->obj_list->num_entries); + struct pcep_object_error *obj = error_msg->obj_list->head->data; + CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, obj->header.object_class); + CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, obj->header.object_type); + CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, obj->error_type); + CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, obj->error_value); + pcep_msg_free_message(error_msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); +} + + +void test_handle_socket_comm_event_report() +{ + create_message_for_test(PCEP_TYPE_REPORT, false, false); + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + handle_socket_comm_event(&event); + + /* The PCC does not support receiving Report messages, so an error + * should be sent */ + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 0); + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + struct pcep_message *error_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(error_msg); + assert(error_msg != NULL); + CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, error_msg->msg_header->type); + /* Verify the error object */ + CU_ASSERT_EQUAL(1, error_msg->obj_list->num_entries); + struct pcep_object_error *obj = error_msg->obj_list->head->data; + CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, obj->header.object_class); + CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, obj->header.object_type); + CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, obj->error_type); + CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, obj->error_value); + pcep_msg_free_message(error_msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); +} + + +void test_handle_socket_comm_event_update() +{ + create_message_for_test(PCEP_TYPE_UPDATE, false, true); + struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL); + struct pcep_object_lsp *lsp = + pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true, + true, true, true, NULL); + double_linked_list *ero_subobj_list = dll_initialize(); + dll_append(ero_subobj_list, pcep_obj_create_ro_subobj_asn(0x0102)); + struct pcep_object_ro *ero = pcep_obj_create_ero(ero_subobj_list); + struct pcep_object_metric *metric = + pcep_obj_create_metric(PCEP_METRIC_TE, false, true, 16.0); + dll_append(message->obj_list, srp); + dll_append(message->obj_list, lsp); + dll_append(message->obj_list, ero); + dll_append(message->obj_list, metric); + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + handle_socket_comm_event(&event); + + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0); + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type); + CU_ASSERT_EQUAL(PCEP_TYPE_UPDATE, e->message->msg_header->type); + pceplib_free(PCEPLIB_INFRA, e); +} + + +void test_handle_socket_comm_event_initiate() +{ + create_message_for_test(PCEP_TYPE_INITIATE, false, true); + struct pcep_object_srp *srp = pcep_obj_create_srp(false, 100, NULL); + struct pcep_object_lsp *lsp = + pcep_obj_create_lsp(100, PCEP_LSP_OPERATIONAL_UP, true, true, + true, true, true, NULL); + dll_append(message->obj_list, srp); + dll_append(message->obj_list, lsp); + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + handle_socket_comm_event(&event); + + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0); + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type); + CU_ASSERT_EQUAL(PCEP_TYPE_INITIATE, e->message->msg_header->type); + pceplib_free(PCEPLIB_INFRA, e); +} + + +void test_handle_socket_comm_event_notify() +{ + create_message_for_test(PCEP_TYPE_PCNOTF, false, true); + handle_socket_comm_event(&event); + + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0); + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type); + CU_ASSERT_EQUAL(PCEP_TYPE_PCNOTF, e->message->msg_header->type); + pceplib_free(PCEPLIB_INFRA, e); +} + + +void test_handle_socket_comm_event_error() +{ + create_message_for_test(PCEP_TYPE_ERROR, false, true); + handle_socket_comm_event(&event); + + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 0, 0); + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type); + CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, e->message->msg_header->type); + pceplib_free(PCEPLIB_INFRA, e); +} + + +void test_handle_socket_comm_event_unknown_msg() +{ + create_message_for_test(13, false, false); + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + handle_socket_comm_event(&event); + + /* Sending an unsupported message type, so an error should be sent, + * but the connection should remain open, since max_unknown_messages = 2 + */ + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 0); + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + struct pcep_message *msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(msg); + assert(msg != NULL); + CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, msg->msg_header->type); + /* Verify the error object */ + CU_ASSERT_EQUAL(1, msg->obj_list->num_entries); + struct pcep_object_error *error_obj = msg->obj_list->head->data; + CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, error_obj->header.object_class); + CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, error_obj->header.object_type); + CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, + error_obj->error_type); + CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, error_obj->error_value); + pcep_msg_free_message(msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + destroy_message_for_test(); + + /* Send another unsupported message type, an error should be sent and + * the connection should be closed, since max_unknown_messages = 2 */ + create_message_for_test(13, false, false); + reset_mock_socket_comm_info(); + mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + + handle_socket_comm_event(&event); + + verify_socket_comm_times_called(0, 0, 0, 2, 1, 0, 0); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCC_RCVD_MAX_UNKOWN_MSGS, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + + /* Verify the error message */ + encoded_msg = dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + assert(encoded_msg != NULL); + msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(msg); + CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, msg->msg_header->type); + /* Verify the error object */ + CU_ASSERT_EQUAL(1, msg->obj_list->num_entries); + error_obj = msg->obj_list->head->data; + CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_ERROR, error_obj->header.object_class); + CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_ERROR, error_obj->header.object_type); + CU_ASSERT_EQUAL(PCEP_ERRT_CAPABILITY_NOT_SUPPORTED, + error_obj->error_type); + CU_ASSERT_EQUAL(PCEP_ERRV_UNASSIGNED, error_obj->error_value); + pcep_msg_free_message(msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + /* Verify the Close message */ + encoded_msg = dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + assert(encoded_msg != NULL); + msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(msg); + assert(msg != NULL); + CU_ASSERT_EQUAL(PCEP_TYPE_CLOSE, msg->msg_header->type); + /* Verify the error object */ + CU_ASSERT_EQUAL(1, msg->obj_list->num_entries); + struct pcep_object_close *close_obj = msg->obj_list->head->data; + CU_ASSERT_EQUAL(PCEP_OBJ_CLASS_CLOSE, close_obj->header.object_class); + CU_ASSERT_EQUAL(PCEP_OBJ_TYPE_CLOSE, close_obj->header.object_type); + CU_ASSERT_EQUAL(PCEP_CLOSE_REASON_UNREC_MSG, close_obj->reason); + pcep_msg_free_message(msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); +} + + +void test_connection_failure(void) +{ + /* + * Test when 2 invalid Open messages are received that a + * PCC_CONNECTION_FAILURE event is generated. + */ + create_message_for_test(PCEP_TYPE_OPEN, false, false); + reset_mock_socket_comm_info(); + struct pcep_object_open *open_object = + pcep_obj_create_open(1, 1, 1, NULL); + /* Make the Open message invalid */ + open_object->open_deadtimer = + session.pcc_config.max_dead_timer_seconds + 1; + dll_append(message->obj_list, open_object); + session.pce_open_received = false; + session.pce_open_accepted = false; + session.pce_open_rejected = false; + session.session_state = SESSION_STATE_PCEP_CONNECTING; + + handle_socket_comm_event(&event); + + CU_ASSERT_TRUE(session.pce_open_received); + CU_ASSERT_TRUE(session.pce_open_rejected); + CU_ASSERT_FALSE(session.pce_open_accepted); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING); + /* An error response should be sent, rejecting the Open */ + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 1); + pcep_event *e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCC_RCVD_INVALID_OPEN, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + destroy_message_for_test(); + + /* Send the same erroneous Open again */ + create_message_for_test(PCEP_TYPE_OPEN, false, false); + reset_mock_socket_comm_info(); + open_object = pcep_obj_create_open(1, 1, 1, NULL); + /* Make the Open message invalid */ + open_object->open_deadtimer = + session.pcc_config.max_dead_timer_seconds + 1; + dll_append(message->obj_list, open_object); + + handle_socket_comm_event(&event); + + CU_ASSERT_TRUE(session.pce_open_received); + CU_ASSERT_TRUE(session.pce_open_rejected); + CU_ASSERT_FALSE(session.pce_open_accepted); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED); + /* An error response should be sent, rejecting the Open */ + verify_socket_comm_times_called(0, 0, 0, 1, 1, 0, 0); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 2); + e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCC_RCVD_INVALID_OPEN, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCC_CONNECTION_FAILURE, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + + destroy_message_for_test(); + + /* + * Test when 2 invalid Open messages are sent that a + * PCC_CONNECTION_FAILURE event is generated. + */ + create_message_for_test(PCEP_TYPE_ERROR, false, false); + reset_mock_socket_comm_info(); + struct pcep_object_error *error_object = pcep_obj_create_error( + PCEP_ERRT_SESSION_FAILURE, PCEP_ERRV_UNACCEPTABLE_OPEN_MSG_NEG); + dll_append(message->obj_list, error_object); + session.pcc_open_accepted = false; + session.pcc_open_rejected = false; + session.session_state = SESSION_STATE_PCEP_CONNECTING; + + handle_socket_comm_event(&event); + + CU_ASSERT_TRUE(session.pcc_open_rejected); + CU_ASSERT_FALSE(session.pcc_open_accepted); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_PCEP_CONNECTING); + /* Another Open should be sent */ + verify_socket_comm_times_called(0, 0, 0, 1, 0, 0, 0); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 2); + e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCC_SENT_INVALID_OPEN, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(MESSAGE_RECEIVED, e->event_type); + CU_ASSERT_EQUAL(PCEP_TYPE_ERROR, e->message->msg_header->type); + pceplib_free(PCEPLIB_INFRA, e); + destroy_message_for_test(); + + /* Send a socket close while connecting, which should + * generate a PCC_CONNECTION_FAILURE event */ + reset_mock_socket_comm_info(); + event.socket_closed = true; + event.received_msg_list = NULL; + + handle_socket_comm_event(&event); + + CU_ASSERT_TRUE(session.pcc_open_rejected); + CU_ASSERT_FALSE(session.pcc_open_accepted); + CU_ASSERT_EQUAL(session.session_state, SESSION_STATE_INITIALIZED); + verify_socket_comm_times_called(0, 0, 0, 0, 0, 1, 0); + CU_ASSERT_EQUAL(session_logic_event_queue_->event_queue->num_entries, + 2); + e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCE_CLOSED_SOCKET, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); + e = queue_dequeue(session_logic_event_queue_->event_queue); + CU_ASSERT_EQUAL(PCC_CONNECTION_FAILURE, e->event_type); + pceplib_free(PCEPLIB_INFRA, e); +} diff --git a/pceplib/test/pcep_session_logic_states_test.h b/pceplib/test/pcep_session_logic_states_test.h new file mode 100644 index 0000000..e42b501 --- /dev/null +++ b/pceplib/test/pcep_session_logic_states_test.h @@ -0,0 +1,52 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_SESSION_LOGIC_STATES_TEST_H +#define PCEP_SESSION_LOGIC_STATES_TEST_H + +int pcep_session_logic_states_test_suite_setup(void); +int pcep_session_logic_states_test_suite_teardown(void); +void pcep_session_logic_states_test_setup(void); +void pcep_session_logic_states_test_teardown(void); +void test_handle_timer_event_dead_timer(void); +void test_handle_timer_event_keep_alive(void); +void test_handle_timer_event_open_keep_wait(void); +void test_handle_socket_comm_event_null_params(void); +void test_handle_socket_comm_event_close(void); +void test_handle_socket_comm_event_open(void); +void test_handle_socket_comm_event_open_error(void); +void test_handle_socket_comm_event_keep_alive(void); +void test_handle_socket_comm_event_pcrep(void); +void test_handle_socket_comm_event_pcreq(void); +void test_handle_socket_comm_event_report(void); +void test_handle_socket_comm_event_update(void); +void test_handle_socket_comm_event_initiate(void); +void test_handle_socket_comm_event_notify(void); +void test_handle_socket_comm_event_error(void); +void test_handle_socket_comm_event_unknown_msg(void); +void test_connection_failure(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_session_logic_test.c b/pceplib/test/pcep_session_logic_test.c new file mode 100644 index 0000000..8339a7a --- /dev/null +++ b/pceplib/test/pcep_session_logic_test.c @@ -0,0 +1,381 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <CUnit/CUnit.h> + +#include "pcep_socket_comm_mock.h" +#include "pcep_session_logic.h" +#include "pcep_session_logic_test.h" + +/* + * Test suite setup and teardown called before AND after the test suite. + */ + +int pcep_session_logic_test_suite_setup(void) +{ + pceplib_memory_reset(); + return 0; +} + +int pcep_session_logic_test_suite_teardown(void) +{ + printf("\n"); + pceplib_memory_dump(); + return 0; +} + +/* + * Test case setup and teardown called before AND after each test. + */ + +void pcep_session_logic_test_setup() +{ + setup_mock_socket_comm_info(); +} + + +void pcep_session_logic_test_teardown() +{ + stop_session_logic(); + teardown_mock_socket_comm_info(); +} + + +/* + * Test cases + */ + +void test_run_stop_session_logic() +{ + CU_ASSERT_TRUE(run_session_logic()); + CU_ASSERT_TRUE(stop_session_logic()); +} + + +void test_run_session_logic_twice() +{ + CU_ASSERT_TRUE(run_session_logic()); + CU_ASSERT_FALSE(run_session_logic()); +} + + +void test_session_logic_without_run() +{ + /* Verify the functions that depend on run_session_logic() being called + */ + CU_ASSERT_FALSE(stop_session_logic()); +} + + +void test_create_pcep_session_null_params() +{ + pcep_configuration config; + struct in_addr pce_ip; + + CU_ASSERT_PTR_NULL(create_pcep_session(NULL, NULL)); + CU_ASSERT_PTR_NULL(create_pcep_session(NULL, &pce_ip)); + CU_ASSERT_PTR_NULL(create_pcep_session(&config, NULL)); +} + + +void test_create_destroy_pcep_session() +{ + pcep_session *session; + pcep_configuration config; + struct in_addr pce_ip; + + run_session_logic(); + + memset(&config, 0, sizeof(pcep_configuration)); + config.keep_alive_seconds = 5; + config.dead_timer_seconds = 5; + config.request_time_seconds = 5; + config.max_unknown_messages = 5; + config.max_unknown_requests = 5; + inet_pton(AF_INET, "127.0.0.1", &(pce_ip)); + + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + session = create_pcep_session(&config, &pce_ip); + CU_ASSERT_PTR_NOT_NULL(session); + /* What gets saved in the mock is the msg byte buffer. The msg struct + * was deleted when it was sent. Instead of inspecting the msg byte + * buffer, lets just decode it. */ + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + struct pcep_message *open_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(open_msg); + assert(open_msg != NULL); + /* Should be an Open, with no TLVs: length = 12 */ + CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN); + CU_ASSERT_EQUAL(open_msg->encoded_message_length, 12); + destroy_pcep_session(session); + pcep_msg_free_message(open_msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + stop_session_logic(); +} + + +void test_create_destroy_pcep_session_ipv6() +{ + pcep_session *session; + pcep_configuration config; + struct in6_addr pce_ip; + + run_session_logic(); + + memset(&config, 0, sizeof(pcep_configuration)); + config.keep_alive_seconds = 5; + config.dead_timer_seconds = 5; + config.request_time_seconds = 5; + config.max_unknown_messages = 5; + config.max_unknown_requests = 5; + config.is_src_ipv6 = true; + inet_pton(AF_INET6, "::1", &pce_ip); + + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + session = create_pcep_session_ipv6(&config, &pce_ip); + CU_ASSERT_PTR_NOT_NULL(session); + assert(session != NULL); + CU_ASSERT_TRUE(session->socket_comm_session->is_ipv6); + /* What gets saved in the mock is the msg byte buffer. The msg struct + * was deleted when it was sent. Instead of inspecting the msg byte + * buffer, lets just decode it. */ + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + struct pcep_message *open_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(open_msg); + assert(open_msg != NULL); + /* Should be an Open, with no TLVs: length = 12 */ + CU_ASSERT_EQUAL(open_msg->msg_header->type, PCEP_TYPE_OPEN); + CU_ASSERT_EQUAL(open_msg->encoded_message_length, 12); + destroy_pcep_session(session); + pcep_msg_free_message(open_msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + stop_session_logic(); +} + + +void test_create_pcep_session_open_tlvs() +{ + pcep_session *session; + struct in_addr pce_ip; + struct pcep_message *open_msg; + struct pcep_object_header *open_obj; + pcep_configuration config; + memset(&config, 0, sizeof(pcep_configuration)); + config.pcep_msg_versioning = create_default_pcep_versioning(); + inet_pton(AF_INET, "127.0.0.1", &(pce_ip)); + + run_session_logic(); + + /* Verify the created Open message only has 1 TLV: + * pcep_tlv_create_stateful_pce_capability() */ + mock_socket_comm_info *mock_info = get_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + config.support_stateful_pce_lsp_update = true; + config.pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = false; + config.support_sr_te_pst = false; + + session = create_pcep_session(&config, &pce_ip); + CU_ASSERT_PTR_NOT_NULL(session); + /* Get and verify the Open Message */ + uint8_t *encoded_msg = + dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + open_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(open_msg); + assert(open_msg != NULL); + /* Get and verify the Open Message objects */ + CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list); + assert(open_msg->obj_list != NULL); + CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0); + /* Get and verify the Open object */ + open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN); + CU_ASSERT_PTR_NOT_NULL(open_obj); + /* Get and verify the Open object TLVs */ + CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list); + assert(open_obj->tlv_list != NULL); + CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 1); + CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *) + open_obj->tlv_list->head->data) + ->type, + PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY); + + destroy_pcep_session(session); + pcep_msg_free_message(open_msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + /* Verify the created Open message only has 2 TLVs: + * pcep_tlv_create_stateful_pce_capability() + * pcep_tlv_create_lsp_db_version() */ + reset_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + config.support_include_db_version = true; + config.lsp_db_version = 100; + + session = create_pcep_session(&config, &pce_ip); + CU_ASSERT_PTR_NOT_NULL(session); + /* Get and verify the Open Message */ + encoded_msg = dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + open_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(open_msg); + /* Get and verify the Open Message objects */ + CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list); + assert(open_msg != NULL); + CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0); + /* Get and verify the Open object */ + open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN); + CU_ASSERT_PTR_NOT_NULL(open_obj); + /* Get and verify the Open object TLVs */ + CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list); + assert(open_obj->tlv_list != NULL); + CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 2); + CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *) + open_obj->tlv_list->head->data) + ->type, + PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY); + CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *) + open_obj->tlv_list->head->next_node->data) + ->type, + PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION); + + destroy_pcep_session(session); + pcep_msg_free_message(open_msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + + /* Verify the created Open message only has 4 TLVs: + * pcep_tlv_create_stateful_pce_capability() + * pcep_tlv_create_lsp_db_version() + * pcep_tlv_create_sr_pce_capability() + * pcep_tlv_create_path_setup_type_capability() */ + reset_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + config.support_sr_te_pst = true; + + session = create_pcep_session(&config, &pce_ip); + CU_ASSERT_PTR_NOT_NULL(session); + /* Get and verify the Open Message */ + encoded_msg = dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + open_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(open_msg); + assert(open_msg != NULL); + /* Get and verify the Open Message objects */ + CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list); + assert(open_msg->obj_list != NULL); + CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0); + /* Get and verify the Open object */ + open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN); + CU_ASSERT_PTR_NOT_NULL(open_obj); + /* Get and verify the Open object TLVs */ + CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list); + assert(open_obj->tlv_list != NULL); + CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 3); + double_linked_list_node *tlv_node = open_obj->tlv_list->head; + CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type, + PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY); + tlv_node = tlv_node->next_node; + CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type, + PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION); + tlv_node = tlv_node->next_node; + CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type, + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY); + + destroy_pcep_session(session); + pcep_msg_free_message(open_msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + /* Verify the created Open message only has 4 TLVs: + * pcep_tlv_create_stateful_pce_capability() + * pcep_tlv_create_lsp_db_version() + * pcep_tlv_create_sr_pce_capability() + * pcep_tlv_create_path_setup_type_capability() */ + reset_mock_socket_comm_info(); + mock_info->send_message_save_message = true; + config.pcep_msg_versioning->draft_ietf_pce_segment_routing_07 = true; + + session = create_pcep_session(&config, &pce_ip); + CU_ASSERT_PTR_NOT_NULL(session); + /* Get and verify the Open Message */ + encoded_msg = dll_delete_first_node(mock_info->sent_message_list); + CU_ASSERT_PTR_NOT_NULL(encoded_msg); + assert(encoded_msg != NULL); + open_msg = pcep_decode_message(encoded_msg); + CU_ASSERT_PTR_NOT_NULL(open_msg); + assert(open_msg != NULL); + /* Get and verify the Open Message objects */ + CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list); + assert(open_msg->obj_list != NULL); + CU_ASSERT_TRUE(open_msg->obj_list->num_entries > 0); + /* Get and verify the Open object */ + open_obj = pcep_obj_get(open_msg->obj_list, PCEP_OBJ_CLASS_OPEN); + CU_ASSERT_PTR_NOT_NULL(open_obj); + assert(open_obj != NULL); + /* Get and verify the Open object TLVs */ + CU_ASSERT_PTR_NOT_NULL(open_obj->tlv_list); + assert(open_obj->tlv_list != NULL); + CU_ASSERT_EQUAL(open_obj->tlv_list->num_entries, 4); + tlv_node = open_obj->tlv_list->head; + CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type, + PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY); + tlv_node = tlv_node->next_node; + CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type, + PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION); + tlv_node = tlv_node->next_node; + CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type, + PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY); + tlv_node = tlv_node->next_node; + CU_ASSERT_EQUAL(((struct pcep_object_tlv_header *)tlv_node->data)->type, + PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY); + + destroy_pcep_versioning(config.pcep_msg_versioning); + destroy_pcep_session(session); + pcep_msg_free_message(open_msg); + pceplib_free(PCEPLIB_MESSAGES, encoded_msg); + + stop_session_logic(); +} + + +void test_destroy_pcep_session_null_session() +{ + /* Just testing that it does not core dump */ + destroy_pcep_session(NULL); +} diff --git a/pceplib/test/pcep_session_logic_test.h b/pceplib/test/pcep_session_logic_test.h new file mode 100644 index 0000000..6cc1963 --- /dev/null +++ b/pceplib/test/pcep_session_logic_test.h @@ -0,0 +1,43 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_SESSION_LOGIC_TEST_H_ +#define PCEP_SESSION_LOGIC_TEST_H_ + +int pcep_session_logic_test_suite_setup(void); +int pcep_session_logic_test_suite_teardown(void); +void pcep_session_logic_test_setup(void); +void pcep_session_logic_test_teardown(void); +void test_run_stop_session_logic(void); +void test_run_session_logic_twice(void); +void test_session_logic_without_run(void); +void test_create_pcep_session_null_params(void); +void test_create_destroy_pcep_session(void); +void test_create_destroy_pcep_session_ipv6(void); +void test_create_pcep_session_open_tlvs(void); +void test_destroy_pcep_session_null_session(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_session_logic_tests.c b/pceplib/test/pcep_session_logic_tests.c new file mode 100644 index 0000000..9a15390 --- /dev/null +++ b/pceplib/test/pcep_session_logic_tests.c @@ -0,0 +1,205 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <CUnit/Basic.h> +#include <CUnit/CUnit.h> +#include <CUnit/TestDB.h> + +#include "pcep_session_logic_loop_test.h" +#include "pcep_session_logic_states_test.h" +#include "pcep_session_logic_test.h" + + +int main(int argc, char **argv) +{ + /* Unused parameters cause compilation warnings */ + (void)argc; + (void)argv; + + CU_initialize_registry(); + + /* + * Tests defined in pcep_socket_comm_test.c + */ + CU_pSuite test_session_logic_suite = + CU_add_suite_with_setup_and_teardown( + "PCEP Session Logic Test Suite", + pcep_session_logic_test_suite_setup, // suite setup and + // cleanup function + // pointers + pcep_session_logic_test_suite_teardown, + pcep_session_logic_test_setup, // test case setup + // function pointer + pcep_session_logic_test_teardown); // test case teardown + // function pointer + + CU_add_test(test_session_logic_suite, "test_run_stop_session_logic", + test_run_stop_session_logic); + CU_add_test(test_session_logic_suite, "test_run_session_logic_twice", + test_run_session_logic_twice); + CU_add_test(test_session_logic_suite, "test_session_logic_without_run", + test_session_logic_without_run); + CU_add_test(test_session_logic_suite, + "test_create_pcep_session_null_params", + test_create_pcep_session_null_params); + CU_add_test(test_session_logic_suite, + "test_create_destroy_pcep_session", + test_create_destroy_pcep_session); + CU_add_test(test_session_logic_suite, + "test_create_destroy_pcep_session_ipv6", + test_create_destroy_pcep_session_ipv6); + CU_add_test(test_session_logic_suite, + "test_create_pcep_session_open_tlvs", + test_create_pcep_session_open_tlvs); + CU_add_test(test_session_logic_suite, + "test_destroy_pcep_session_null_session", + test_destroy_pcep_session_null_session); + + CU_pSuite test_session_logic_loop_suite = + CU_add_suite_with_setup_and_teardown( + "PCEP Session Logic Loop Test Suite", + pcep_session_logic_loop_test_suite_setup, // suite setup + // and cleanup + // function + // pointers + pcep_session_logic_loop_test_suite_teardown, + pcep_session_logic_loop_test_setup, // test case setup + // function pointer + pcep_session_logic_loop_test_teardown); // test case + // teardown + // function + // pointer + + CU_add_test(test_session_logic_loop_suite, + "test_session_logic_loop_null_data", + test_session_logic_loop_null_data); + CU_add_test(test_session_logic_loop_suite, + "test_session_logic_loop_inactive", + test_session_logic_loop_inactive); + CU_add_test(test_session_logic_loop_suite, + "test_session_logic_msg_ready_handler", + test_session_logic_msg_ready_handler); + CU_add_test(test_session_logic_loop_suite, + "test_session_logic_conn_except_notifier", + test_session_logic_conn_except_notifier); + CU_add_test(test_session_logic_loop_suite, + "test_session_logic_timer_expire_handler", + test_session_logic_timer_expire_handler); + + CU_pSuite test_session_logic_states_suite = + CU_add_suite_with_setup_and_teardown( + "PCEP Session Logic States Test Suite", + pcep_session_logic_states_test_suite_setup, // suite + // setup and + // cleanup + // function + // pointers + pcep_session_logic_states_test_suite_teardown, + pcep_session_logic_states_test_setup, // test case setup + // function + // pointer + pcep_session_logic_states_test_teardown); // test case + // teardown + // function + // pointer + + CU_add_test(test_session_logic_states_suite, + "test_handle_timer_event_dead_timer", + test_handle_timer_event_dead_timer); + CU_add_test(test_session_logic_states_suite, + "test_handle_timer_event_keep_alive", + test_handle_timer_event_keep_alive); + CU_add_test(test_session_logic_states_suite, + "test_handle_timer_event_open_keep_wait", + test_handle_timer_event_open_keep_wait); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_null_params", + test_handle_socket_comm_event_null_params); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_close", + test_handle_socket_comm_event_close); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_open", + test_handle_socket_comm_event_open); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_open_error", + test_handle_socket_comm_event_open_error); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_keep_alive", + test_handle_socket_comm_event_keep_alive); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_pcrep", + test_handle_socket_comm_event_pcrep); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_pcreq", + test_handle_socket_comm_event_pcreq); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_report", + test_handle_socket_comm_event_report); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_update", + test_handle_socket_comm_event_update); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_initiate", + test_handle_socket_comm_event_initiate); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_notify", + test_handle_socket_comm_event_notify); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_error", + test_handle_socket_comm_event_error); + CU_add_test(test_session_logic_states_suite, + "test_handle_socket_comm_event_unknown_msg", + test_handle_socket_comm_event_unknown_msg); + CU_add_test(test_session_logic_states_suite, "test_connection_failure", + test_connection_failure); + + /* + * Run the tests and cleanup. + */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_FailureRecord *failure_record = CU_get_failure_list(); + if (failure_record != NULL) { + printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n"); + do { + printf("\t [%s] [%s] [%s:%d]\n", + failure_record->pSuite->pName, + failure_record->pTest->pName, + failure_record->strFileName, + failure_record->uiLineNumber); + failure_record = failure_record->pNext; + + } while (failure_record != NULL); + } + + CU_pRunSummary run_summary = CU_get_run_summary(); + int result = run_summary->nTestsFailed; + CU_cleanup_registry(); + + return result; +} diff --git a/pceplib/test/pcep_session_logic_tests_valgrind.sh b/pceplib/test/pcep_session_logic_tests_valgrind.sh new file mode 100755 index 0000000..435bb3d --- /dev/null +++ b/pceplib/test/pcep_session_logic_tests_valgrind.sh @@ -0,0 +1,2 @@ +source pceplib/test/pcep_tests_valgrind.sh +valgrind_test pceplib/test/pcep_session_logic_tests diff --git a/pceplib/test/pcep_socket_comm_loop_test.c b/pceplib/test/pcep_socket_comm_loop_test.c new file mode 100644 index 0000000..748cf43 --- /dev/null +++ b/pceplib/test/pcep_socket_comm_loop_test.c @@ -0,0 +1,198 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <pthread.h> +#include <stdlib.h> + +#include <CUnit/CUnit.h> + +#include "pcep_socket_comm_internals.h" +#include "pcep_socket_comm_loop.h" +#include "pcep_socket_comm_loop_test.h" +#include "pcep_socket_comm.h" +#include "pcep_utils_memory.h" + +void test_loop_conn_except_notifier(void *session_data, int socket_fd); + +/* + * Functions to be tested, implemented in pcep_socket_comm_loop.c + */ + +typedef struct ready_to_read_handler_info_ { + bool handler_called; + bool except_handler_called; + void *data; + int socket_fd; + int bytes_read; + +} ready_to_read_handler_info; + +static ready_to_read_handler_info read_handler_info; +static pcep_socket_comm_session *test_comm_session; +static pcep_socket_comm_handle *test_socket_comm_handle = NULL; + +static int test_loop_message_ready_to_read_handler(void *session_data, + int socket_fd) +{ + read_handler_info.handler_called = true; + read_handler_info.data = session_data; + read_handler_info.socket_fd = socket_fd; + + return read_handler_info.bytes_read; +} + + +void test_loop_conn_except_notifier(void *session_data, int socket_fd) +{ + (void)session_data; + (void)socket_fd; + read_handler_info.except_handler_called = true; +} + + +/* + * Test case setup and teardown called before AND after each test. + */ +void pcep_socket_comm_loop_test_setup() +{ + test_socket_comm_handle = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_handle)); + memset(test_socket_comm_handle, 0, sizeof(pcep_socket_comm_handle)); + test_socket_comm_handle->active = false; + test_socket_comm_handle->read_list = + ordered_list_initialize(socket_fd_node_compare); + test_socket_comm_handle->write_list = + ordered_list_initialize(socket_fd_node_compare); + test_socket_comm_handle->session_list = + ordered_list_initialize(pointer_compare_function); + pthread_mutex_init(&test_socket_comm_handle->socket_comm_mutex, NULL); + test_socket_comm_handle->num_active_sessions = 0; + + test_comm_session = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_socket_comm_session)); + memset(test_comm_session, 0, sizeof(pcep_socket_comm_session)); + test_comm_session->message_ready_to_read_handler = + test_loop_message_ready_to_read_handler; + ordered_list_add_node(test_socket_comm_handle->session_list, + test_comm_session); + + read_handler_info.handler_called = false; + read_handler_info.except_handler_called = false; + read_handler_info.data = NULL; + read_handler_info.socket_fd = -1; + read_handler_info.bytes_read = 0; +} + + +void pcep_socket_comm_loop_test_teardown() +{ + pthread_mutex_destroy(&test_socket_comm_handle->socket_comm_mutex); + ordered_list_destroy(test_socket_comm_handle->read_list); + ordered_list_destroy(test_socket_comm_handle->write_list); + ordered_list_destroy(test_socket_comm_handle->session_list); + pceplib_free(PCEPLIB_INFRA, test_socket_comm_handle); + test_socket_comm_handle = NULL; + + if (test_comm_session != NULL) { + pceplib_free(PCEPLIB_INFRA, test_comm_session); + test_comm_session = NULL; + } +} + + +/* + * Test cases + */ + +void test_socket_comm_loop_null_handle() +{ + /* Verify that socket_comm_loop() correctly handles a NULL + * timers_context */ + socket_comm_loop(NULL); +} + + +void test_socket_comm_loop_not_active() +{ + /* Verify that event_loop() correctly handles an inactive flag */ + pcep_socket_comm_handle handle; + handle.active = false; + socket_comm_loop(&handle); +} + + +void test_handle_reads_no_read() +{ + CU_ASSERT_PTR_NULL(test_socket_comm_handle->read_list->head); + + handle_reads(test_socket_comm_handle); + + CU_ASSERT_FALSE(read_handler_info.handler_called); + CU_ASSERT_FALSE(read_handler_info.except_handler_called); + CU_ASSERT_PTR_NULL(test_socket_comm_handle->read_list->head); +} + + +void test_handle_reads_read_message() +{ + /* Setup the comm session so that it can read. + * It should read 100 bytes, which simulates a successful read */ + test_comm_session->socket_fd = 10; + read_handler_info.bytes_read = 100; + FD_SET(test_comm_session->socket_fd, + &test_socket_comm_handle->read_master_set); + ordered_list_add_node(test_socket_comm_handle->read_list, + test_comm_session); + + handle_reads(test_socket_comm_handle); + + CU_ASSERT_TRUE(read_handler_info.handler_called); + CU_ASSERT_FALSE(read_handler_info.except_handler_called); + CU_ASSERT_EQUAL(test_comm_session->received_bytes, + read_handler_info.bytes_read); +} + + +void test_handle_reads_read_message_close() +{ + /* Setup the comm session so that it can read. + * It should read 0 bytes, which simulates that the socket closed */ + test_comm_session->socket_fd = 11; + read_handler_info.bytes_read = 0; + FD_SET(test_comm_session->socket_fd, + &test_socket_comm_handle->read_master_set); + ordered_list_add_node(test_socket_comm_handle->read_list, + test_comm_session); + + handle_reads(test_socket_comm_handle); + + CU_ASSERT_TRUE(read_handler_info.handler_called); + CU_ASSERT_FALSE(read_handler_info.except_handler_called); + CU_ASSERT_EQUAL(test_comm_session->received_bytes, + read_handler_info.bytes_read); + CU_ASSERT_PTR_NULL(test_socket_comm_handle->read_list->head); +} diff --git a/pceplib/test/pcep_socket_comm_loop_test.h b/pceplib/test/pcep_socket_comm_loop_test.h new file mode 100644 index 0000000..d2e3f21 --- /dev/null +++ b/pceplib/test/pcep_socket_comm_loop_test.h @@ -0,0 +1,38 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_SOCKET_COMM_LOOP_TEST_H_ +#define PCEP_SOCKET_COMM_LOOP_TEST_H_ + +void pcep_socket_comm_loop_test_setup(void); +void pcep_socket_comm_loop_test_teardown(void); +void test_socket_comm_loop_null_handle(void); +void test_socket_comm_loop_not_active(void); +void test_handle_reads_no_read(void); +void test_handle_reads_read_message(void); +void test_handle_reads_read_message_close(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_socket_comm_test.c b/pceplib/test/pcep_socket_comm_test.c new file mode 100644 index 0000000..116531f --- /dev/null +++ b/pceplib/test/pcep_socket_comm_test.c @@ -0,0 +1,325 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <netinet/in.h> + +#include <CUnit/CUnit.h> + +#include "pcep_socket_comm.h" +#include "pcep_socket_comm_internals.h" +#include "pcep_socket_comm_test.h" + +extern pcep_socket_comm_handle *socket_comm_handle_; + +static pcep_socket_comm_session *test_session = NULL; +static struct in_addr test_host_ip; +static struct in_addr test_src_ip; +static struct in6_addr test_host_ipv6; +static struct in6_addr test_src_ipv6; +static short test_port = 4789; +static short test_src_port = 4999; +static uint32_t connect_timeout_millis = 500; + +/* + * Unit Test Basic pcep_socket_comm API usage. + * Testing sending messages, etc via sockets should be done + * with integration tests, not unit tests. + */ + +/* + * Different socket_comm handler test implementations + */ +static void test_message_received_handler(void *session_data, + const char *message_data, + unsigned int message_length) +{ + (void)session_data; + (void)message_data; + (void)message_length; +} + +static int test_message_ready_to_read_handler(void *session_data, int socket_fd) +{ + (void)session_data; + (void)socket_fd; + return 1; +} + +static void test_message_sent_handler(void *session_data, int socket_fd) +{ + (void)session_data; + (void)socket_fd; + return; +} + +static void test_connection_except_notifier(void *session_data, int socket_fd) +{ + (void)session_data; + (void)socket_fd; +} + + +/* + * Test case setup and teardown called before AND after each test. + */ +void pcep_socket_comm_test_setup() +{ + inet_pton(AF_INET, "127.0.0.1", &(test_host_ip)); + inet_pton(AF_INET, "127.0.0.1", &(test_src_ip)); + inet_pton(AF_INET6, "::1", &(test_host_ipv6)); + inet_pton(AF_INET6, "::1", &(test_src_ipv6)); +} + +void pcep_socket_comm_test_teardown() +{ + socket_comm_session_teardown(test_session); + test_session = NULL; +} + + +/* + * Test cases + */ + +void test_pcep_socket_comm_initialize() +{ + test_session = socket_comm_session_initialize( + test_message_received_handler, NULL, NULL, + test_connection_except_notifier, &test_host_ip, test_port, + connect_timeout_millis, NULL, false, NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_FALSE(test_session->is_ipv6); +} + + +void test_pcep_socket_comm_initialize_ipv6() +{ + test_session = socket_comm_session_initialize_ipv6( + test_message_received_handler, NULL, NULL, + test_connection_except_notifier, &test_host_ipv6, test_port, + connect_timeout_millis, NULL, false, NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_TRUE(test_session->is_ipv6); +} + + +void test_pcep_socket_comm_initialize_with_src() +{ + /* Test that INADDR_ANY will be used when src_ip is NULL */ + test_session = socket_comm_session_initialize_with_src( + test_message_received_handler, NULL, NULL, + test_connection_except_notifier, NULL, 0, &test_host_ip, + test_port, connect_timeout_millis, NULL, false, NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_EQUAL( + test_session->src_sock_addr.src_sock_addr_ipv4.sin_addr.s_addr, + INADDR_ANY); + CU_ASSERT_FALSE(test_session->is_ipv6); + + socket_comm_session_teardown(test_session); + test_session = socket_comm_session_initialize_with_src( + test_message_received_handler, NULL, NULL, + test_connection_except_notifier, &test_src_ip, test_src_port, + &test_host_ip, test_port, connect_timeout_millis, NULL, false, + NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_EQUAL( + test_session->src_sock_addr.src_sock_addr_ipv4.sin_addr.s_addr, + test_src_ip.s_addr); + CU_ASSERT_EQUAL(test_session->src_sock_addr.src_sock_addr_ipv4.sin_port, + ntohs(test_src_port)); + CU_ASSERT_FALSE(test_session->is_ipv6); +} + + +void test_pcep_socket_comm_initialize_with_src_ipv6() +{ + /* Test that INADDR6_ANY will be used when src_ip is NULL */ + test_session = socket_comm_session_initialize_with_src_ipv6( + test_message_received_handler, NULL, NULL, + test_connection_except_notifier, NULL, 0, &test_host_ipv6, + test_port, connect_timeout_millis, NULL, false, NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_EQUAL(memcmp(&test_session->src_sock_addr.src_sock_addr_ipv6 + .sin6_addr, + &in6addr_any, sizeof(struct in6_addr)), + 0); + CU_ASSERT_TRUE(test_session->is_ipv6); + + socket_comm_session_teardown(test_session); + test_session = socket_comm_session_initialize_with_src_ipv6( + test_message_received_handler, NULL, NULL, + test_connection_except_notifier, &test_src_ipv6, test_src_port, + &test_host_ipv6, test_port, connect_timeout_millis, NULL, false, + NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_EQUAL(memcmp(&test_session->src_sock_addr.src_sock_addr_ipv6 + .sin6_addr, + &test_src_ipv6, sizeof(struct in6_addr)), + 0); + CU_ASSERT_EQUAL( + test_session->src_sock_addr.src_sock_addr_ipv6.sin6_port, + ntohs(test_src_port)); + CU_ASSERT_TRUE(test_session->is_ipv6); +} + + +void test_pcep_socket_comm_initialize_tcpmd5() +{ + char tcp_md5_str[] = "hello"; + int tcp_md5_strlen = strlen(tcp_md5_str); + + test_session = socket_comm_session_initialize( + test_message_received_handler, NULL, NULL, + test_connection_except_notifier, &test_host_ip, test_port, 1, + tcp_md5_str, true, NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str, + test_session->tcp_authentication_str, + tcp_md5_strlen)); + CU_ASSERT_TRUE(test_session->is_tcp_auth_md5); + CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session)); + /* This call does not work, it returns errno=92, Protocol not available + getsockopt(test_session->socket_fd, SOL_SOCKET, TCP_MD5SIG, &sig, + &siglen);*/ + + socket_comm_session_teardown(test_session); + test_session = socket_comm_session_initialize( + test_message_received_handler, NULL, NULL, + test_connection_except_notifier, &test_host_ip, test_port, 1, + tcp_md5_str, false, NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str, + test_session->tcp_authentication_str, + tcp_md5_strlen)); + CU_ASSERT_FALSE(test_session->is_tcp_auth_md5); + CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session)); +} + + +void test_pcep_socket_comm_initialize_ipv6_tcpmd5() +{ + char tcp_md5_str[] = "hello"; + int tcp_md5_strlen = strlen(tcp_md5_str); + + test_session = socket_comm_session_initialize_ipv6( + test_message_received_handler, NULL, NULL, + test_connection_except_notifier, &test_host_ipv6, test_port, 1, + tcp_md5_str, true, NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str, + test_session->tcp_authentication_str, + tcp_md5_strlen)); + CU_ASSERT_TRUE(test_session->is_tcp_auth_md5); + CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session)); + /* This call does not work, it returns errno=92, Protocol not available + getsockopt(test_session->socket_fd, SOL_SOCKET, TCP_MD5SIG, &sig, + &siglen);*/ + + socket_comm_session_teardown(test_session); + test_session = socket_comm_session_initialize_ipv6( + test_message_received_handler, NULL, NULL, + test_connection_except_notifier, &test_host_ipv6, test_port, 1, + tcp_md5_str, false, NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_EQUAL(0, strncmp(tcp_md5_str, + test_session->tcp_authentication_str, + tcp_md5_strlen)); + CU_ASSERT_FALSE(test_session->is_tcp_auth_md5); + CU_ASSERT_FALSE(socket_comm_session_connect_tcp(test_session)); +} + + +void test_pcep_socket_comm_initialize_handlers() +{ + /* Verify incorrect handler usage is correctly handled */ + + /* Both receive handlers cannot be NULL */ + test_session = socket_comm_session_initialize( + NULL, NULL, NULL, test_connection_except_notifier, + &test_host_ip, test_port, connect_timeout_millis, NULL, false, + NULL); + CU_ASSERT_PTR_NULL(test_session); + + /* Both receive handlers cannot be set */ + test_session = socket_comm_session_initialize( + test_message_received_handler, + test_message_ready_to_read_handler, test_message_sent_handler, + test_connection_except_notifier, &test_host_ip, test_port, + connect_timeout_millis, NULL, false, NULL); + CU_ASSERT_PTR_NULL(test_session); + + /* Only one receive handler can be set */ + test_session = socket_comm_session_initialize( + NULL, test_message_ready_to_read_handler, + test_message_sent_handler, test_connection_except_notifier, + &test_host_ip, test_port, connect_timeout_millis, NULL, false, + NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); +} + + +void test_pcep_socket_comm_session_not_initialized() +{ + CU_ASSERT_FALSE(socket_comm_session_connect_tcp(NULL)); + CU_ASSERT_FALSE(socket_comm_session_close_tcp(NULL)); + CU_ASSERT_FALSE(socket_comm_session_close_tcp_after_write(NULL)); + socket_comm_session_send_message(NULL, NULL, 0, true); + CU_ASSERT_FALSE(socket_comm_session_teardown(NULL)); +} + + +void test_pcep_socket_comm_session_destroy() +{ + test_session = socket_comm_session_initialize( + test_message_received_handler, NULL, test_message_sent_handler, + test_connection_except_notifier, &test_host_ip, test_port, + connect_timeout_millis, NULL, false, NULL); + CU_ASSERT_PTR_NOT_NULL(test_session); + assert(test_session != NULL); + CU_ASSERT_PTR_NOT_NULL(socket_comm_handle_); + assert(socket_comm_handle_ != NULL); + CU_ASSERT_EQUAL(socket_comm_handle_->num_active_sessions, 1); + + CU_ASSERT_TRUE(socket_comm_session_teardown(test_session)); + test_session = NULL; + CU_ASSERT_PTR_NOT_NULL(socket_comm_handle_); + + CU_ASSERT_TRUE(destroy_socket_comm_loop()); + CU_ASSERT_PTR_NULL(socket_comm_handle_); +} diff --git a/pceplib/test/pcep_socket_comm_test.h b/pceplib/test/pcep_socket_comm_test.h new file mode 100644 index 0000000..f857af0 --- /dev/null +++ b/pceplib/test/pcep_socket_comm_test.h @@ -0,0 +1,42 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_SOCKET_COMM_TEST_H_ +#define PCEP_SOCKET_COMM_TEST_H_ + +void pcep_socket_comm_test_teardown(void); +void pcep_socket_comm_test_setup(void); +void test_pcep_socket_comm_initialize(void); +void test_pcep_socket_comm_initialize_ipv6(void); +void test_pcep_socket_comm_initialize_with_src(void); +void test_pcep_socket_comm_initialize_with_src_ipv6(void); +void test_pcep_socket_comm_initialize_tcpmd5(void); +void test_pcep_socket_comm_initialize_ipv6_tcpmd5(void); +void test_pcep_socket_comm_initialize_handlers(void); +void test_pcep_socket_comm_session_not_initialized(void); +void test_pcep_socket_comm_session_destroy(void); + +#endif diff --git a/pceplib/test/pcep_socket_comm_tests.c b/pceplib/test/pcep_socket_comm_tests.c new file mode 100644 index 0000000..6a5839d --- /dev/null +++ b/pceplib/test/pcep_socket_comm_tests.c @@ -0,0 +1,132 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <CUnit/Basic.h> +#include <CUnit/CUnit.h> +#include <CUnit/TestDB.h> + +#include "pcep_socket_comm_loop_test.h" +#include "pcep_socket_comm_test.h" + + +int main(int argc, char **argv) +{ + /* Unused parameters cause compilation warnings */ + (void)argc; + (void)argv; + + CU_initialize_registry(); + + /* + * Tests defined in pcep_socket_comm_test.c + */ + CU_pSuite test_socket_comm_suite = CU_add_suite_with_setup_and_teardown( + "PCEP Socket Comm Test Suite", NULL, + NULL, // suite setup and cleanup function pointers + pcep_socket_comm_test_setup, // test case setup function pointer + pcep_socket_comm_test_teardown); // test case teardown function + // pointer + + CU_add_test(test_socket_comm_suite, "test_pcep_socket_comm_initialize", + test_pcep_socket_comm_initialize); + CU_add_test(test_socket_comm_suite, + "test_pcep_socket_comm_initialize_ipv6", + test_pcep_socket_comm_initialize_ipv6); + CU_add_test(test_socket_comm_suite, + "test_pcep_socket_comm_initialize_with_src", + test_pcep_socket_comm_initialize_with_src); + CU_add_test(test_socket_comm_suite, + "test_pcep_socket_comm_initialize_with_src_ipv6", + test_pcep_socket_comm_initialize_with_src_ipv6); + CU_add_test(test_socket_comm_suite, + "test_pcep_socket_comm_initialize_tcpmd5", + test_pcep_socket_comm_initialize_tcpmd5); + CU_add_test(test_socket_comm_suite, + "test_pcep_socket_comm_initialize_ipv6_tcpmd5", + test_pcep_socket_comm_initialize_ipv6_tcpmd5); + CU_add_test(test_socket_comm_suite, + "test_pcep_socket_comm_initialize_handlers", + test_pcep_socket_comm_initialize_handlers); + CU_add_test(test_socket_comm_suite, + "test_pcep_socket_comm_session_not_initialized", + test_pcep_socket_comm_session_not_initialized); + CU_add_test(test_socket_comm_suite, + "test_pcep_socket_comm_session_destroy", + test_pcep_socket_comm_session_destroy); + + /* + * Tests defined in pcep_socket_comm_loop_test.c + */ + CU_pSuite test_socket_comm_loop_suite = + CU_add_suite_with_setup_and_teardown( + "PCEP Socket Comm Loop Test Suite", NULL, NULL, + pcep_socket_comm_loop_test_setup, // suite setup + // function pointer + pcep_socket_comm_loop_test_teardown); // suite cleanup + // function + // pointer + + CU_add_test(test_socket_comm_loop_suite, + "test_socket_comm_loop_null_handle", + test_socket_comm_loop_null_handle); + CU_add_test(test_socket_comm_loop_suite, + "test_socket_comm_loop_not_active", + test_socket_comm_loop_not_active); + CU_add_test(test_socket_comm_loop_suite, "test_handle_reads_no_read", + test_handle_reads_no_read); + CU_add_test(test_socket_comm_loop_suite, + "test_handle_reads_read_message", + test_handle_reads_read_message); + CU_add_test(test_socket_comm_loop_suite, + "test_handle_reads_read_message_close", + test_handle_reads_read_message_close); + + /* + * Run the tests and cleanup. + */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_FailureRecord *failure_record = CU_get_failure_list(); + if (failure_record != NULL) { + printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n"); + do { + printf("\t [%s] [%s] [%s:%d]\n", + failure_record->pSuite->pName, + failure_record->pTest->pName, + failure_record->strFileName, + failure_record->uiLineNumber); + failure_record = failure_record->pNext; + + } while (failure_record != NULL); + } + + CU_pRunSummary run_summary = CU_get_run_summary(); + int result = run_summary->nTestsFailed; + CU_cleanup_registry(); + + return result; +} diff --git a/pceplib/test/pcep_socket_comm_tests_valgrind.sh b/pceplib/test/pcep_socket_comm_tests_valgrind.sh new file mode 100755 index 0000000..d9e95e4 --- /dev/null +++ b/pceplib/test/pcep_socket_comm_tests_valgrind.sh @@ -0,0 +1,2 @@ +source pceplib/test/pcep_tests_valgrind.sh +valgrind_test pceplib/test/pcep_socket_comm_tests diff --git a/pceplib/test/pcep_tests_valgrind.sh b/pceplib/test/pcep_tests_valgrind.sh new file mode 100755 index 0000000..ca4772c --- /dev/null +++ b/pceplib/test/pcep_tests_valgrind.sh @@ -0,0 +1,15 @@ +# +# Common function definition for PCEPlib valgrind tests +# + +function valgrind_test() +{ + local test_suite=$1 + [[ -z ${test_suite} ]] && { echo "${FUNCNAME}(): test_suite not specified."; exit 1; } + [[ ! -x "${test_suite}" ]] && { echo "${test_suite} is not an executable file."; exit 1; } + + G_SLICE=always-malloc + G_DEBUG=gc-friendly + VALGRIND="valgrind -v --tool=memcheck --leak-check=full --num-callers=40 --error-exitcode=1" + ${VALGRIND} --log-file=${test_suite}.val.log ./${test_suite} || ({ echo "Valgrind memory check error"; exit 1; }) +} diff --git a/pceplib/test/pcep_timers_event_loop_test.c b/pceplib/test/pcep_timers_event_loop_test.c new file mode 100644 index 0000000..79ed84b --- /dev/null +++ b/pceplib/test/pcep_timers_event_loop_test.c @@ -0,0 +1,166 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> + +#include <CUnit/CUnit.h> + +#include "pcep_timers.h" +#include "pcep_utils_memory.h" +#include "pcep_timers_event_loop.h" +#include "pcep_timers_event_loop_test.h" + + +typedef struct timer_expire_handler_info_ { + bool handler_called; + void *data; + int timerId; + +} timer_expire_handler_info; + +static pcep_timers_context *test_timers_context = NULL; +static timer_expire_handler_info expire_handler_info; +#define TEST_EVENT_LOOP_TIMER_ID 500 + + +/* Called when a timer expires */ +static void test_timer_expire_handler(void *data, int timerId) +{ + expire_handler_info.handler_called = true; + expire_handler_info.data = data; + expire_handler_info.timerId = timerId; +} + + +/* Test case setup called before each test. + * Declared in pcep_timers_tests.c */ +void pcep_timers_event_loop_test_setup() +{ + test_timers_context = + pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timers_context)); + memset(test_timers_context, 0, sizeof(pcep_timers_context)); + if (pthread_mutex_init(&(test_timers_context->timer_list_lock), NULL) + != 0) { + fprintf(stderr, + "ERROR initializing timers, cannot initialize the mutex\n"); + } + test_timers_context->active = false; + test_timers_context->expire_handler = test_timer_expire_handler; + test_timers_context->timer_list = + ordered_list_initialize(timer_list_node_timer_id_compare); + + expire_handler_info.handler_called = false; + expire_handler_info.data = NULL; + expire_handler_info.timerId = -1; +} + + +/* Test case teardown called after each test. + * Declared in pcep_timers_tests.c */ +void pcep_timers_event_loop_test_teardown() +{ + pthread_mutex_unlock(&test_timers_context->timer_list_lock); + pthread_mutex_destroy(&(test_timers_context->timer_list_lock)); + ordered_list_destroy(test_timers_context->timer_list); + pceplib_free(PCEPLIB_INFRA, test_timers_context); + test_timers_context = NULL; +} + + +/* + * Test functions + */ + +void test_walk_and_process_timers_no_timers() +{ + CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 0); + CU_ASSERT_PTR_NULL(test_timers_context->timer_list->head); + + walk_and_process_timers(test_timers_context); + + CU_ASSERT_FALSE(expire_handler_info.handler_called); + CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 0); + CU_ASSERT_PTR_NULL(test_timers_context->timer_list->head); +} + + +void test_walk_and_process_timers_timer_not_expired() +{ + pcep_timer timer; + timer.data = &timer; + // Set the timer to expire 100 seconds from now + timer.expire_time = time(NULL) + 100; + timer.timer_id = TEST_EVENT_LOOP_TIMER_ID; + ordered_list_add_node(test_timers_context->timer_list, &timer); + + walk_and_process_timers(test_timers_context); + + /* The timer should still be in the list, since it hasnt expired yet */ + CU_ASSERT_FALSE(expire_handler_info.handler_called); + CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 1); + CU_ASSERT_PTR_NOT_NULL(test_timers_context->timer_list->head); +} + + +void test_walk_and_process_timers_timer_expired() +{ + /* We need to alloc it, since it will be free'd in + * walk_and_process_timers */ + pcep_timer *timer = pceplib_malloc(PCEPLIB_INFRA, sizeof(pcep_timer)); + timer->data = timer; + // Set the timer to expire 10 seconds ago + timer->expire_time = time(NULL) - 10; + pthread_mutex_lock(&test_timers_context->timer_list_lock); + timer->timer_id = TEST_EVENT_LOOP_TIMER_ID; + pthread_mutex_unlock(&test_timers_context->timer_list_lock); + ordered_list_add_node(test_timers_context->timer_list, timer); + + walk_and_process_timers(test_timers_context); + + /* Since the timer expired, the expire_handler should have been called + * and the timer should have been removed from the timer list */ + CU_ASSERT_TRUE(expire_handler_info.handler_called); + CU_ASSERT_PTR_EQUAL(expire_handler_info.data, timer); + CU_ASSERT_EQUAL(expire_handler_info.timerId, TEST_EVENT_LOOP_TIMER_ID); + CU_ASSERT_EQUAL(test_timers_context->timer_list->num_entries, 0); + CU_ASSERT_PTR_NULL(test_timers_context->timer_list->head); +} + +void test_event_loop_null_handle() +{ + /* Verify that event_loop() correctly handles a NULL timers_context */ + event_loop(NULL); +} + + +void test_event_loop_not_active() +{ + /* Verify that event_loop() correctly handles an inactive timers_context + * flag */ + test_timers_context->active = false; + event_loop(test_timers_context); +} diff --git a/pceplib/test/pcep_timers_event_loop_test.h b/pceplib/test/pcep_timers_event_loop_test.h new file mode 100644 index 0000000..19fd264 --- /dev/null +++ b/pceplib/test/pcep_timers_event_loop_test.h @@ -0,0 +1,38 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_TIMERS_EVENT_LOOP_TEST_H_ +#define PCEP_TIMERS_EVENT_LOOP_TEST_H_ + +void pcep_timers_event_loop_test_setup(void); +void pcep_timers_event_loop_test_teardown(void); +void test_walk_and_process_timers_no_timers(void); +void test_walk_and_process_timers_timer_not_expired(void); +void test_walk_and_process_timers_timer_expired(void); +void test_event_loop_null_handle(void); +void test_event_loop_not_active(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_timers_test.c b/pceplib/test/pcep_timers_test.c new file mode 100644 index 0000000..e5be90a --- /dev/null +++ b/pceplib/test/pcep_timers_test.c @@ -0,0 +1,113 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdbool.h> +#include <CUnit/CUnit.h> + +#include "pcep_timers.h" +#include "pcep_timers_test.h" + +/* Test case teardown called after each test. + * Declared in pcep_timers_tests.c */ +void pcep_timers_test_teardown() +{ + teardown_timers(); +} + +static void test_timer_expire_handler(void *data, int timerId) +{ + (void)data; + (void)timerId; +} + + +void test_double_initialization(void) +{ + CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true); + CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), false); +} + + +void test_initialization_null_callback(void) +{ + CU_ASSERT_EQUAL(initialize_timers(NULL), false); +} + + +void test_not_initialized(void) +{ + /* All of these should fail if initialize_timers() hasnt been called */ + CU_ASSERT_EQUAL(create_timer(5, NULL), -1); + CU_ASSERT_EQUAL(cancel_timer(7), false); + CU_ASSERT_EQUAL(reset_timer(7), false); + CU_ASSERT_EQUAL(teardown_timers(), false); +} + + +void test_create_timer(void) +{ + CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true); + + int timer_id = create_timer(0, NULL); + CU_ASSERT_TRUE(timer_id > -1); +} + + +void test_cancel_timer(void) +{ + CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true); + + int timer_id = create_timer(10, NULL); + CU_ASSERT_TRUE(timer_id > -1); + + CU_ASSERT_EQUAL(cancel_timer(timer_id), true); +} + + +void test_cancel_timer_invalid(void) +{ + CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true); + CU_ASSERT_EQUAL(cancel_timer(1), false); +} + + +void test_reset_timer(void) +{ + CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true); + + int timer_id = create_timer(10, NULL); + CU_ASSERT_TRUE(timer_id > -1); + + CU_ASSERT_EQUAL(reset_timer(timer_id), true); +} + + +void test_reset_timer_invalid(void) +{ + CU_ASSERT_EQUAL(initialize_timers(test_timer_expire_handler), true); + CU_ASSERT_EQUAL(reset_timer(1), false); +} diff --git a/pceplib/test/pcep_timers_test.h b/pceplib/test/pcep_timers_test.h new file mode 100644 index 0000000..6ac9a90 --- /dev/null +++ b/pceplib/test/pcep_timers_test.h @@ -0,0 +1,40 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_TIMERS_TEST_H_ +#define PCEP_TIMERS_TEST_H_ + +void pcep_timers_test_teardown(void); +void test_double_initialization(void); +void test_initialization_null_callback(void); +void test_not_initialized(void); +void test_create_timer(void); +void test_cancel_timer(void); +void test_cancel_timer_invalid(void); +void test_reset_timer(void); +void test_reset_timer_invalid(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_timers_tests.c b/pceplib/test/pcep_timers_tests.c new file mode 100644 index 0000000..f3aa830 --- /dev/null +++ b/pceplib/test/pcep_timers_tests.c @@ -0,0 +1,117 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <CUnit/Basic.h> +#include <CUnit/CUnit.h> +#include <CUnit/TestDB.h> + +#include "pcep_timers_test.h" +#include "pcep_timers_event_loop_test.h" + + +int main(int argc, char **argv) +{ + /* Unused parameters cause compilation warnings */ + (void)argc; + (void)argv; + + CU_initialize_registry(); + + /* + * Tests defined in pcep_timers_test.c + */ + CU_pSuite test_timers_suite = CU_add_suite_with_setup_and_teardown( + "PCEP Timers Test Suite", NULL, + NULL, // suite setup and cleanup function pointers + NULL, pcep_timers_test_teardown); // test case setup and + // teardown function pointers + CU_add_test(test_timers_suite, "test_double_initialization", + test_double_initialization); + CU_add_test(test_timers_suite, "test_initialization_null_callback", + test_initialization_null_callback); + CU_add_test(test_timers_suite, "test_not_initialized", + test_not_initialized); + CU_add_test(test_timers_suite, "test_create_timer", test_create_timer); + CU_add_test(test_timers_suite, "test_cancel_timer", test_cancel_timer); + CU_add_test(test_timers_suite, "test_cancel_timer_invalid", + test_cancel_timer_invalid); + CU_add_test(test_timers_suite, "test_reset_timer", test_reset_timer); + CU_add_test(test_timers_suite, "test_reset_timer_invalid", + test_reset_timer_invalid); + + /* + * Tests defined in pcep_timers_event_loop_test.c + */ + CU_pSuite test_timers_event_loop_suite = + CU_add_suite_with_setup_and_teardown( + "PCEP Timers Event Loop Test Suite", NULL, + NULL, // suite setup and cleanup function pointers + pcep_timers_event_loop_test_setup, // test case setup + // function pointer + pcep_timers_event_loop_test_teardown); // test case + // teardown + // function + // pointer + CU_add_test(test_timers_event_loop_suite, + "test_walk_and_process_timers_no_timers", + test_walk_and_process_timers_no_timers); + CU_add_test(test_timers_event_loop_suite, + "test_walk_and_process_timers_timer_not_expired", + test_walk_and_process_timers_timer_not_expired); + CU_add_test(test_timers_event_loop_suite, + "test_walk_and_process_timers_timer_expired", + test_walk_and_process_timers_timer_expired); + CU_add_test(test_timers_event_loop_suite, "test_event_loop_null_handle", + test_event_loop_null_handle); + CU_add_test(test_timers_event_loop_suite, "test_event_loop_not_active", + test_event_loop_not_active); + + /* + * Run the tests and cleanup. + */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_FailureRecord *failure_record = CU_get_failure_list(); + if (failure_record != NULL) { + printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n"); + do { + printf("\t [%s] [%s] [%s:%d]\n", + failure_record->pSuite->pName, + failure_record->pTest->pName, + failure_record->strFileName, + failure_record->uiLineNumber); + failure_record = failure_record->pNext; + + } while (failure_record != NULL); + } + + CU_pRunSummary run_summary = CU_get_run_summary(); + int result = run_summary->nTestsFailed; + CU_cleanup_registry(); + + return result; +} diff --git a/pceplib/test/pcep_timers_tests_valgrind.sh b/pceplib/test/pcep_timers_tests_valgrind.sh new file mode 100755 index 0000000..f9bff3b --- /dev/null +++ b/pceplib/test/pcep_timers_tests_valgrind.sh @@ -0,0 +1,2 @@ +source pceplib/test/pcep_tests_valgrind.sh +valgrind_test pceplib/test/pcep_timers_tests diff --git a/pceplib/test/pcep_utils_counters_test.c b/pceplib/test/pcep_utils_counters_test.c new file mode 100644 index 0000000..b31715f --- /dev/null +++ b/pceplib/test/pcep_utils_counters_test.c @@ -0,0 +1,261 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> + +#include <CUnit/CUnit.h> + +#include "pcep_utils_counters.h" +#include "pcep_utils_counters_test.h" + + +void test_create_counters_group() +{ + const char group_name[] = "group"; + uint16_t num_subgroups = 10; + + struct counters_group *group = + create_counters_group(NULL, num_subgroups); + CU_ASSERT_PTR_NULL(group); + + group = create_counters_group(group_name, MAX_COUNTER_GROUPS + 1); + CU_ASSERT_PTR_NULL(group); + + group = create_counters_group(group_name, num_subgroups); + CU_ASSERT_PTR_NOT_NULL(group); + assert(group != NULL); + + CU_ASSERT_EQUAL(group->num_subgroups, 0); + CU_ASSERT_EQUAL(group->max_subgroups, num_subgroups); + CU_ASSERT_EQUAL(strcmp(group->counters_group_name, group_name), 0); + + delete_counters_group(group); +} + +void test_create_counters_subgroup() +{ + const char subgroup_name[] = "subgroup"; + uint16_t subgroup_id = 10; + uint16_t num_counters = 20; + + struct counters_subgroup *subgroup = + create_counters_subgroup(NULL, subgroup_id, num_counters); + CU_ASSERT_PTR_NULL(subgroup); + + subgroup = create_counters_subgroup( + subgroup_name, MAX_COUNTER_GROUPS + 1, num_counters); + CU_ASSERT_PTR_NULL(subgroup); + + subgroup = create_counters_subgroup(subgroup_name, subgroup_id, + MAX_COUNTERS + 1); + CU_ASSERT_PTR_NULL(subgroup); + + subgroup = create_counters_subgroup(subgroup_name, subgroup_id, + num_counters); + CU_ASSERT_PTR_NOT_NULL(subgroup); + assert(subgroup != NULL); + + CU_ASSERT_EQUAL(subgroup->subgroup_id, subgroup_id); + CU_ASSERT_EQUAL(subgroup->num_counters, 0); + CU_ASSERT_EQUAL(subgroup->max_counters, num_counters); + CU_ASSERT_EQUAL(strcmp(subgroup->counters_subgroup_name, subgroup_name), + 0); + + delete_counters_subgroup(subgroup); +} + +void test_add_counters_subgroup() +{ + struct counters_group *group = create_counters_group("group", 1); + struct counters_subgroup *subgroup1 = + create_counters_subgroup("subgroup", 0, 5); + struct counters_subgroup *subgroup2 = + create_counters_subgroup("subgroup", 1, 5); + + CU_ASSERT_FALSE(add_counters_subgroup(NULL, NULL)); + CU_ASSERT_FALSE(add_counters_subgroup(NULL, subgroup1)); + CU_ASSERT_FALSE(add_counters_subgroup(group, NULL)); + + CU_ASSERT_EQUAL(group->num_subgroups, 0); + CU_ASSERT_TRUE(add_counters_subgroup(group, subgroup1)); + CU_ASSERT_EQUAL(group->num_subgroups, 1); + /* Cant add more than num_subgroups to the group */ + CU_ASSERT_FALSE(add_counters_subgroup(group, subgroup2)); + + CU_ASSERT_PTR_NOT_NULL(find_subgroup(group, 0)); + CU_ASSERT_PTR_NULL(find_subgroup(group, 1)); + + delete_counters_group(group); + delete_counters_subgroup(subgroup2); +} + +void test_create_subgroup_counter() +{ + uint16_t counter_id = 1; + char counter_name[] = "my counter"; + struct counters_subgroup *subgroup = + create_counters_subgroup("subgroup", 1, 2); + + CU_ASSERT_FALSE( + create_subgroup_counter(NULL, counter_id, counter_name)); + CU_ASSERT_FALSE(create_subgroup_counter(subgroup, counter_id + 1, + counter_name)); + CU_ASSERT_FALSE(create_subgroup_counter(subgroup, counter_id, NULL)); + CU_ASSERT_EQUAL(subgroup->num_counters, 0); + CU_ASSERT_TRUE( + create_subgroup_counter(subgroup, counter_id, counter_name)); + CU_ASSERT_EQUAL(subgroup->num_counters, 1); + + delete_counters_subgroup(subgroup); +} + +void test_delete_counters_group() +{ + struct counters_group *group = create_counters_group("group", 1); + + CU_ASSERT_FALSE(delete_counters_group(NULL)); + CU_ASSERT_TRUE(delete_counters_group(group)); +} + +void test_delete_counters_subgroup() +{ + struct counters_subgroup *subgroup = + create_counters_subgroup("subgroup", 1, 1); + + CU_ASSERT_FALSE(delete_counters_subgroup(NULL)); + CU_ASSERT_TRUE(delete_counters_subgroup(subgroup)); +} + +void test_reset_group_counters() +{ + uint16_t subgroup_id = 1; + uint16_t counter_id = 1; + struct counters_group *group = create_counters_group("group", 10); + struct counters_subgroup *subgroup = + create_counters_subgroup("subgroup", subgroup_id, 10); + create_subgroup_counter(subgroup, counter_id, "counter"); + add_counters_subgroup(group, subgroup); + + struct counter *counter = subgroup->counters[counter_id]; + counter->counter_value = 100; + + CU_ASSERT_FALSE(reset_group_counters(NULL)); + CU_ASSERT_TRUE(reset_group_counters(group)); + CU_ASSERT_EQUAL(counter->counter_value, 0); + + delete_counters_group(group); +} + +void test_reset_subgroup_counters() +{ + uint16_t counter_id = 1; + struct counters_subgroup *subgroup = + create_counters_subgroup("subgroup", 1, 10); + create_subgroup_counter(subgroup, counter_id, "counter"); + + struct counter *counter = subgroup->counters[counter_id]; + counter->counter_value = 100; + + CU_ASSERT_FALSE(reset_subgroup_counters(NULL)); + CU_ASSERT_TRUE(reset_subgroup_counters(subgroup)); + CU_ASSERT_EQUAL(counter->counter_value, 0); + + delete_counters_subgroup(subgroup); +} + +void test_increment_counter() +{ + uint16_t subgroup_id = 1; + uint16_t counter_id = 1; + struct counters_group *group = create_counters_group("group", 10); + struct counters_subgroup *subgroup = + create_counters_subgroup("subgroup", subgroup_id, 10); + create_subgroup_counter(subgroup, counter_id, "counter"); + add_counters_subgroup(group, subgroup); + + struct counter *counter = subgroup->counters[counter_id]; + counter->counter_value = 100; + + CU_ASSERT_FALSE(increment_counter(NULL, subgroup_id, counter_id)); + CU_ASSERT_FALSE(increment_counter(group, 100, counter_id)); + CU_ASSERT_FALSE(increment_counter(group, subgroup_id, 123)); + CU_ASSERT_TRUE(increment_counter(group, subgroup_id, counter_id)); + CU_ASSERT_EQUAL(counter->counter_value, 101); + CU_ASSERT_EQUAL(subgroup_counters_total(subgroup), 101); + + delete_counters_group(group); +} + +void test_increment_subgroup_counter() +{ + int counter_id = 1; + uint32_t counter_value = 100; + struct counters_subgroup *subgroup = + create_counters_subgroup("subgroup", 1, 10); + create_subgroup_counter(subgroup, counter_id, "counter"); + + struct counter *counter = subgroup->counters[counter_id]; + counter->counter_value = counter_value; + + CU_ASSERT_FALSE(increment_subgroup_counter(NULL, counter_id)); + CU_ASSERT_FALSE(increment_subgroup_counter(subgroup, counter_id + 1)); + CU_ASSERT_TRUE(increment_subgroup_counter(subgroup, counter_id)); + CU_ASSERT_EQUAL(counter->counter_value, counter_value + 1); + + delete_counters_subgroup(subgroup); +} + +void test_dump_counters_group_to_log() +{ + uint16_t subgroup_id = 1; + uint16_t counter_id = 1; + struct counters_group *group = create_counters_group("group", 10); + struct counters_subgroup *subgroup = + create_counters_subgroup("subgroup", subgroup_id, 10); + create_subgroup_counter(subgroup, counter_id, "counter"); + add_counters_subgroup(group, subgroup); + + CU_ASSERT_FALSE(dump_counters_group_to_log(NULL)); + CU_ASSERT_TRUE(dump_counters_group_to_log(group)); + + delete_counters_group(group); +} + +void test_dump_counters_subgroup_to_log() +{ + uint16_t subgroup_id = 1; + uint16_t counter_id = 1; + struct counters_subgroup *subgroup = + create_counters_subgroup("subgroup", subgroup_id, 10); + create_subgroup_counter(subgroup, counter_id, "counter"); + + CU_ASSERT_FALSE(dump_counters_subgroup_to_log(NULL)); + CU_ASSERT_TRUE(dump_counters_subgroup_to_log(subgroup)); + + delete_counters_subgroup(subgroup); +} diff --git a/pceplib/test/pcep_utils_counters_test.h b/pceplib/test/pcep_utils_counters_test.h new file mode 100644 index 0000000..07236dc --- /dev/null +++ b/pceplib/test/pcep_utils_counters_test.h @@ -0,0 +1,43 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_UTILS_COUNTERS_TEST_H_ +#define PCEP_UTILS_COUNTERS_TEST_H_ + +void test_create_counters_group(void); +void test_create_counters_subgroup(void); +void test_add_counters_subgroup(void); +void test_create_subgroup_counter(void); +void test_delete_counters_group(void); +void test_delete_counters_subgroup(void); +void test_reset_group_counters(void); +void test_reset_subgroup_counters(void); +void test_increment_counter(void); +void test_increment_subgroup_counter(void); +void test_dump_counters_group_to_log(void); +void test_dump_counters_subgroup_to_log(void); + +#endif diff --git a/pceplib/test/pcep_utils_double_linked_list_test.c b/pceplib/test/pcep_utils_double_linked_list_test.c new file mode 100644 index 0000000..181cbec --- /dev/null +++ b/pceplib/test/pcep_utils_double_linked_list_test.c @@ -0,0 +1,315 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <CUnit/CUnit.h> + +#include "pcep_utils_double_linked_list.h" +#include "pcep_utils_double_linked_list_test.h" + +typedef struct dll_node_data_ { + int int_data; + +} dll_node_data; + +void test_empty_dl_list() +{ + double_linked_list *handle = dll_initialize(); + + CU_ASSERT_PTR_NULL(dll_delete_first_node(handle)); + CU_ASSERT_PTR_NULL(dll_delete_last_node(handle)); + CU_ASSERT_PTR_NULL(dll_delete_node(handle, NULL)); + + dll_destroy(handle); +} + +void test_null_dl_list_handle() +{ + dll_destroy(NULL); + CU_ASSERT_PTR_NULL(dll_prepend(NULL, NULL)); + CU_ASSERT_PTR_NULL(dll_append(NULL, NULL)); + CU_ASSERT_PTR_NULL(dll_delete_first_node(NULL)); + CU_ASSERT_PTR_NULL(dll_delete_last_node(NULL)); + CU_ASSERT_PTR_NULL(dll_delete_node(NULL, NULL)); +} + +void test_dll_prepend_data() +{ + dll_node_data data1, data2, data3; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + + double_linked_list *handle = dll_initialize(); + + CU_ASSERT_PTR_NOT_NULL(dll_prepend(handle, &data3)); + CU_ASSERT_PTR_NOT_NULL(dll_prepend(handle, &data2)); + CU_ASSERT_PTR_NOT_NULL(dll_prepend(handle, &data1)); + + CU_ASSERT_EQUAL(handle->num_entries, 3); + + double_linked_list_node *node = handle->head; + CU_ASSERT_PTR_NOT_NULL(node); + assert(node != NULL); + CU_ASSERT_PTR_EQUAL(node->data, &data1); + CU_ASSERT_PTR_NULL(node->prev_node); + CU_ASSERT_PTR_NOT_NULL(node->next_node); + + node = node->next_node; + CU_ASSERT_PTR_NOT_NULL(node); + assert(node != NULL); + CU_ASSERT_PTR_EQUAL(node->data, &data2); + CU_ASSERT_PTR_NOT_NULL(node->prev_node); + CU_ASSERT_PTR_NOT_NULL(node->next_node); + + node = node->next_node; + CU_ASSERT_PTR_NOT_NULL(node); + assert(node != NULL); + CU_ASSERT_PTR_EQUAL(node->data, &data3); + CU_ASSERT_PTR_NOT_NULL(node->prev_node); + CU_ASSERT_PTR_NULL(node->next_node); + CU_ASSERT_PTR_EQUAL(handle->tail, node); + + dll_destroy(handle); +} + + +void test_dll_append_data() +{ + dll_node_data data1, data2, data3; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + + double_linked_list *handle = dll_initialize(); + + CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1)); + CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data2)); + CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data3)); + + CU_ASSERT_EQUAL(handle->num_entries, 3); + + double_linked_list_node *node = handle->head; + CU_ASSERT_PTR_EQUAL(node->data, &data1); + CU_ASSERT_PTR_NULL(node->prev_node); + CU_ASSERT_PTR_NOT_NULL(node->next_node); + + node = node->next_node; + CU_ASSERT_PTR_NOT_NULL(node); + assert(node != NULL); + CU_ASSERT_PTR_EQUAL(node->data, &data2); + CU_ASSERT_PTR_NOT_NULL(node->prev_node); + CU_ASSERT_PTR_NOT_NULL(node->next_node); + + node = node->next_node; + CU_ASSERT_PTR_NOT_NULL(node); + assert(node != NULL); + CU_ASSERT_PTR_EQUAL(node->data, &data3); + CU_ASSERT_PTR_NOT_NULL(node->prev_node); + CU_ASSERT_PTR_NULL(node->next_node); + CU_ASSERT_PTR_EQUAL(handle->tail, node); + + dll_destroy(handle); +} + + +void test_dll_delete_first_node() +{ + dll_node_data data1, data2; + data1.int_data = 1; + data2.int_data = 2; + + double_linked_list *handle = dll_initialize(); + + /* Test deleting with just 1 node in the list */ + CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1)); + CU_ASSERT_EQUAL(handle->num_entries, 1); + + void *deleted_data = dll_delete_first_node(handle); + CU_ASSERT_PTR_NOT_NULL(deleted_data); + CU_ASSERT_PTR_EQUAL(&data1, deleted_data); + + CU_ASSERT_EQUAL(handle->num_entries, 0); + CU_ASSERT_PTR_NULL(handle->head); + CU_ASSERT_PTR_NULL(handle->tail); + + /* Test deleting with 2 nodes in the list */ + CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1)); + CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data2)); + CU_ASSERT_EQUAL(handle->num_entries, 2); + + deleted_data = dll_delete_first_node(handle); + CU_ASSERT_PTR_NOT_NULL(deleted_data); + CU_ASSERT_PTR_EQUAL(&data1, deleted_data); + + CU_ASSERT_EQUAL(handle->num_entries, 1); + CU_ASSERT_PTR_EQUAL(handle->head->data, &data2); + CU_ASSERT_PTR_EQUAL(handle->head, handle->tail); + CU_ASSERT_PTR_NULL(handle->head->prev_node); + CU_ASSERT_PTR_NULL(handle->head->next_node); + + dll_destroy(handle); +} + + +void test_dll_delete_last_node() +{ + dll_node_data data1, data2; + data1.int_data = 1; + data2.int_data = 2; + + double_linked_list *handle = dll_initialize(); + + /* Test deleting with just 1 node in the list */ + CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1)); + CU_ASSERT_EQUAL(handle->num_entries, 1); + + void *deleted_data = dll_delete_last_node(handle); + CU_ASSERT_PTR_NOT_NULL(deleted_data); + CU_ASSERT_PTR_EQUAL(&data1, deleted_data); + + CU_ASSERT_EQUAL(handle->num_entries, 0); + CU_ASSERT_PTR_NULL(handle->head); + CU_ASSERT_PTR_NULL(handle->tail); + + /* Test deleting with 2 nodes in the list */ + CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data1)); + CU_ASSERT_PTR_NOT_NULL(dll_append(handle, &data2)); + CU_ASSERT_EQUAL(handle->num_entries, 2); + + deleted_data = dll_delete_last_node(handle); + CU_ASSERT_PTR_NOT_NULL(deleted_data); + CU_ASSERT_PTR_EQUAL(&data2, deleted_data); + + CU_ASSERT_EQUAL(handle->num_entries, 1); + CU_ASSERT_PTR_EQUAL(handle->head->data, &data1); + CU_ASSERT_PTR_EQUAL(handle->head, handle->tail); + CU_ASSERT_PTR_NULL(handle->head->prev_node); + CU_ASSERT_PTR_NULL(handle->head->next_node); + + dll_destroy(handle); +} + + +void test_dll_delete_node() +{ + dll_node_data data1, data2, data3; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + double_linked_list_node *node1, *node2, *node3; + double_linked_list *handle; + + /* Test deleting with just 1 node in the list */ + handle = dll_initialize(); + node1 = dll_append(handle, &data1); + CU_ASSERT_PTR_NOT_NULL(node1); + CU_ASSERT_EQUAL(handle->num_entries, 1); + + void *deleted_data = dll_delete_node(handle, node1); + CU_ASSERT_PTR_NOT_NULL(deleted_data); + CU_ASSERT_PTR_EQUAL(&data1, deleted_data); + + CU_ASSERT_EQUAL(handle->num_entries, 0); + CU_ASSERT_PTR_NULL(handle->head); + CU_ASSERT_PTR_NULL(handle->tail); + + /* + * Test deleting the head with 2 nodes in the list + */ + node1 = dll_append(handle, &data1); + node2 = dll_append(handle, &data2); + CU_ASSERT_PTR_NOT_NULL(node1); + CU_ASSERT_PTR_NOT_NULL(node2); + CU_ASSERT_EQUAL(handle->num_entries, 2); + + /* Delete the head entry */ + deleted_data = dll_delete_node(handle, node1); + CU_ASSERT_PTR_NOT_NULL(deleted_data); + CU_ASSERT_PTR_EQUAL(&data1, deleted_data); + + CU_ASSERT_EQUAL(handle->num_entries, 1); + CU_ASSERT_PTR_EQUAL(handle->head->data, &data2); + CU_ASSERT_PTR_EQUAL(handle->head, handle->tail); + CU_ASSERT_PTR_NULL(handle->head->prev_node); + CU_ASSERT_PTR_NULL(handle->head->next_node); + dll_destroy(handle); + + /* + * Test deleting the tail with 2 nodes in the list + */ + handle = dll_initialize(); + node1 = dll_append(handle, &data1); + node2 = dll_append(handle, &data2); + CU_ASSERT_PTR_NOT_NULL(node1); + CU_ASSERT_PTR_NOT_NULL(node2); + CU_ASSERT_EQUAL(handle->num_entries, 2); + + /* Delete the tail entry */ + deleted_data = dll_delete_node(handle, node2); + CU_ASSERT_PTR_NOT_NULL(deleted_data); + CU_ASSERT_PTR_EQUAL(&data2, deleted_data); + + CU_ASSERT_EQUAL(handle->num_entries, 1); + CU_ASSERT_PTR_EQUAL(handle->head->data, &data1); + CU_ASSERT_PTR_EQUAL(handle->head, handle->tail); + CU_ASSERT_PTR_NULL(handle->head->prev_node); + CU_ASSERT_PTR_NULL(handle->head->next_node); + dll_destroy(handle); + + /* + * Test deleting in the middle with 3 nodes in the list + */ + handle = dll_initialize(); + node1 = dll_append(handle, &data1); + node2 = dll_append(handle, &data2); + node3 = dll_append(handle, &data3); + CU_ASSERT_PTR_NOT_NULL(node1); + assert(node1 != NULL); + CU_ASSERT_PTR_NOT_NULL(node2); + assert(node2 != NULL); + CU_ASSERT_PTR_NOT_NULL(node3); + assert(node3 != NULL); + CU_ASSERT_EQUAL(handle->num_entries, 3); + + /* Delete the middle entry */ + deleted_data = dll_delete_node(handle, node2); + CU_ASSERT_PTR_NOT_NULL(deleted_data); + CU_ASSERT_PTR_EQUAL(&data2, deleted_data); + + CU_ASSERT_EQUAL(handle->num_entries, 2); + CU_ASSERT_PTR_EQUAL(handle->head, node1); + CU_ASSERT_PTR_EQUAL(handle->tail, node3); + CU_ASSERT_PTR_EQUAL(node1->data, &data1); + CU_ASSERT_PTR_EQUAL(node3->data, &data3); + CU_ASSERT_PTR_EQUAL(node1->next_node, node3); + CU_ASSERT_PTR_EQUAL(node3->prev_node, node1); + CU_ASSERT_PTR_NULL(node1->prev_node); + CU_ASSERT_PTR_NULL(node3->next_node); + + dll_destroy(handle); +} diff --git a/pceplib/test/pcep_utils_double_linked_list_test.h b/pceplib/test/pcep_utils_double_linked_list_test.h new file mode 100644 index 0000000..ddb6467 --- /dev/null +++ b/pceplib/test/pcep_utils_double_linked_list_test.h @@ -0,0 +1,38 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_UTILS_DOUBLE_LINKED_LIST_TEST_H_ +#define PCEP_UTILS_DOUBLE_LINKED_LIST_TEST_H_ + +void test_empty_dl_list(void); +void test_null_dl_list_handle(void); +void test_dll_prepend_data(void); +void test_dll_append_data(void); +void test_dll_delete_first_node(void); +void test_dll_delete_last_node(void); +void test_dll_delete_node(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_utils_memory_test.c b/pceplib/test/pcep_utils_memory_test.c new file mode 100644 index 0000000..0958626 --- /dev/null +++ b/pceplib/test/pcep_utils_memory_test.c @@ -0,0 +1,245 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdint.h> + +#include <CUnit/CUnit.h> + +#include "pcep_utils_memory.h" +#include "pcep_utils_memory_test.h" + +void *test_pceplib_malloc(void *mem_type, size_t size); +void *test_pceplib_calloc(void *mem_type, size_t size); +void *test_pceplib_realloc(void *mem_type, void *ptr, size_t size); +void *test_pceplib_strdup(void *mem_type, const char *str); +void test_pceplib_free(void *mem_type, void *ptr); +void verify_memory_type(struct pceplib_memory_type *mt, uint32_t num_alloc, + uint32_t alloc_bytes, uint32_t num_free, + uint32_t free_bytes); +void verify_ext_memory_type(void *mt, int num_malloc_calls, + int num_calloc_calls, int num_realloc_calls, + int num_strdup_calls, int num_free_calls); + +struct test_memory_type { + int num_malloc_calls; + int num_calloc_calls; + int num_realloc_calls; + int num_strdup_calls; + int num_free_calls; +}; + +void *test_pceplib_malloc(void *mem_type, size_t size) +{ + ((struct test_memory_type *)mem_type)->num_malloc_calls++; + return malloc(size); +} + +void *test_pceplib_calloc(void *mem_type, size_t size) +{ + ((struct test_memory_type *)mem_type)->num_calloc_calls++; + return calloc(1, size); +} + +void *test_pceplib_realloc(void *mem_type, void *ptr, size_t size) +{ + ((struct test_memory_type *)mem_type)->num_realloc_calls++; + return realloc(ptr, size); +} + +void *test_pceplib_strdup(void *mem_type, const char *str) +{ + ((struct test_memory_type *)mem_type)->num_strdup_calls++; + return strdup(str); +} + +void test_pceplib_free(void *mem_type, void *ptr) +{ + ((struct test_memory_type *)mem_type)->num_free_calls++; + free(ptr); +} + +void verify_memory_type(struct pceplib_memory_type *mt, uint32_t num_alloc, + uint32_t alloc_bytes, uint32_t num_free, + uint32_t free_bytes) +{ + CU_ASSERT_EQUAL(num_alloc, mt->num_allocates); + CU_ASSERT_EQUAL(alloc_bytes, mt->total_bytes_allocated); + CU_ASSERT_EQUAL(num_free, mt->num_frees); + CU_ASSERT_EQUAL(free_bytes, mt->total_bytes_freed); +} + +void verify_ext_memory_type(void *mt, int num_malloc_calls, + int num_calloc_calls, int num_realloc_calls, + int num_strdup_calls, int num_free_calls) +{ + struct test_memory_type *mt_ptr = (struct test_memory_type *)mt; + CU_ASSERT_EQUAL(num_malloc_calls, mt_ptr->num_malloc_calls); + CU_ASSERT_EQUAL(num_calloc_calls, mt_ptr->num_calloc_calls); + CU_ASSERT_EQUAL(num_realloc_calls, mt_ptr->num_realloc_calls); + CU_ASSERT_EQUAL(num_strdup_calls, mt_ptr->num_strdup_calls); + CU_ASSERT_EQUAL(num_free_calls, mt_ptr->num_free_calls); +} + +void test_memory_internal_impl() +{ + int alloc_size = 100; + struct pceplib_memory_type *pceplib_infra_ptr = + (struct pceplib_memory_type *)PCEPLIB_INFRA; + struct pceplib_memory_type *pceplib_messages_ptr = + (struct pceplib_memory_type *)PCEPLIB_MESSAGES; + int alloc_counter = 1; + int free_counter = 1; + + /* reset the memory type counters for easier testing */ + pceplib_infra_ptr->num_allocates = + pceplib_infra_ptr->total_bytes_allocated = + pceplib_infra_ptr->num_frees = + pceplib_infra_ptr->total_bytes_freed = 0; + pceplib_messages_ptr->num_allocates = + pceplib_messages_ptr->total_bytes_allocated = + pceplib_messages_ptr->num_frees = + pceplib_messages_ptr->total_bytes_freed = 0; + + /* Make sure nothing crashes when all these are set NULL, since the + * internal default values should still be used. */ + pceplib_memory_initialize(NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + /* Test malloc() */ + void *ptr = pceplib_malloc(PCEPLIB_INFRA, alloc_size); + CU_ASSERT_PTR_NOT_NULL(ptr); + pceplib_free(PCEPLIB_INFRA, ptr); + verify_memory_type(pceplib_infra_ptr, alloc_counter, alloc_size, + free_counter++, 0); + + /* Test calloc() */ + ptr = pceplib_calloc(PCEPLIB_INFRA, alloc_size); + CU_ASSERT_PTR_NOT_NULL(ptr); + pceplib_free(PCEPLIB_INFRA, ptr); + alloc_counter++; + verify_memory_type(pceplib_infra_ptr, alloc_counter, + alloc_size * alloc_counter, free_counter++, 0); + + /* Test realloc() */ + ptr = pceplib_malloc(PCEPLIB_INFRA, alloc_size); + CU_ASSERT_PTR_NOT_NULL(ptr); + ptr = pceplib_realloc(PCEPLIB_INFRA, ptr, alloc_size); + CU_ASSERT_PTR_NOT_NULL(ptr); + pceplib_free(PCEPLIB_INFRA, ptr); + alloc_counter += 2; + verify_memory_type(pceplib_infra_ptr, alloc_counter, + alloc_size * alloc_counter, free_counter++, 0); + + /* Test strdup() */ + ptr = pceplib_malloc(PCEPLIB_INFRA, alloc_size); + /* Make strdup duplicate (alloc_size - 1) bytes */ + memset(ptr, 'a', alloc_size); + ((char *)ptr)[alloc_size - 1] = '\0'; + char *str = pceplib_strdup(PCEPLIB_INFRA, (char *)ptr); + CU_ASSERT_PTR_NOT_NULL(ptr); + pceplib_free(PCEPLIB_INFRA, ptr); + pceplib_free(PCEPLIB_INFRA, str); + alloc_counter += 2; + free_counter++; + verify_memory_type(pceplib_infra_ptr, alloc_counter, + (alloc_size * alloc_counter) - 1, free_counter, 0); + + /* Make sure only the pceplib_infra_ptr memory counters are incremented + */ + verify_memory_type(pceplib_messages_ptr, 0, 0, 0, 0); +} + +void test_memory_external_impl() +{ + int alloc_size = 100; + struct pceplib_memory_type *pceplib_infra_ptr = + (struct pceplib_memory_type *)PCEPLIB_INFRA; + struct pceplib_memory_type *pceplib_messages_ptr = + (struct pceplib_memory_type *)PCEPLIB_MESSAGES; + + /* reset the internal memory type counters to later verify they are NOT + * incremented since an external impl was provided */ + pceplib_infra_ptr->num_allocates = + pceplib_infra_ptr->total_bytes_allocated = + pceplib_infra_ptr->num_frees = + pceplib_infra_ptr->total_bytes_freed = 0; + pceplib_messages_ptr->num_allocates = + pceplib_messages_ptr->total_bytes_allocated = + pceplib_messages_ptr->num_frees = + pceplib_messages_ptr->total_bytes_freed = 0; + + /* Setup the external memory type */ + struct test_memory_type infra_mt, messages_mt; + void *infra_ptr = &infra_mt; + void *messages_ptr = &messages_mt; + memset(infra_ptr, 0, sizeof(struct test_memory_type)); + memset(messages_ptr, 0, sizeof(struct test_memory_type)); + int free_counter = 1; + + /* Initialize the PCEPlib memory system with an external implementation + */ + pceplib_memory_initialize(infra_ptr, messages_ptr, test_pceplib_malloc, + test_pceplib_calloc, test_pceplib_realloc, + test_pceplib_strdup, test_pceplib_free); + + /* Test malloc() */ + void *ptr = pceplib_malloc(PCEPLIB_MESSAGES, alloc_size); + CU_ASSERT_PTR_NOT_NULL(ptr); + pceplib_free(PCEPLIB_MESSAGES, ptr); + verify_ext_memory_type(messages_ptr, 1, 0, 0, 0, free_counter++); + + /* Test calloc() */ + ptr = pceplib_calloc(PCEPLIB_MESSAGES, alloc_size); + CU_ASSERT_PTR_NOT_NULL(ptr); + pceplib_free(PCEPLIB_MESSAGES, ptr); + verify_ext_memory_type(messages_ptr, 1, 1, 0, 0, free_counter++); + + /* Test realloc() */ + ptr = pceplib_malloc(PCEPLIB_MESSAGES, alloc_size); + CU_ASSERT_PTR_NOT_NULL(ptr); + ptr = pceplib_realloc(PCEPLIB_MESSAGES, ptr, alloc_size); + CU_ASSERT_PTR_NOT_NULL(ptr); + pceplib_free(PCEPLIB_MESSAGES, ptr); + verify_ext_memory_type(messages_ptr, 2, 1, 1, 0, free_counter++); + + /* Test strdup() */ + ptr = pceplib_malloc(PCEPLIB_MESSAGES, alloc_size); + /* Make strdup duplicate (alloc_size - 1) bytes */ + memset(ptr, 'a', alloc_size); + ((char *)ptr)[alloc_size - 1] = '\0'; + char *str = pceplib_strdup(PCEPLIB_MESSAGES, (char *)ptr); + CU_ASSERT_PTR_NOT_NULL(ptr); + pceplib_free(PCEPLIB_MESSAGES, ptr); + pceplib_free(PCEPLIB_MESSAGES, str); + verify_ext_memory_type(messages_ptr, 3, 1, 1, 1, free_counter + 1); + + /* Make sure the internal memory counters are NOT incremented */ + verify_memory_type(pceplib_infra_ptr, 0, 0, 0, 0); + verify_memory_type(pceplib_messages_ptr, 0, 0, 0, 0); + + verify_ext_memory_type(infra_ptr, 0, 0, 0, 0, 0); +} diff --git a/pceplib/test/pcep_utils_memory_test.h b/pceplib/test/pcep_utils_memory_test.h new file mode 100644 index 0000000..4e0c3fa --- /dev/null +++ b/pceplib/test/pcep_utils_memory_test.h @@ -0,0 +1,33 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_MEMORY_TEST_H_ +#define PCEP_MEMORY_TEST_H_ + +void test_memory_internal_impl(void); +void test_memory_external_impl(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_utils_ordered_list_test.c b/pceplib/test/pcep_utils_ordered_list_test.c new file mode 100644 index 0000000..12ac456 --- /dev/null +++ b/pceplib/test/pcep_utils_ordered_list_test.c @@ -0,0 +1,257 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <CUnit/CUnit.h> + +#include "pcep_utils_ordered_list.h" +#include "pcep_utils_ordered_list_test.h" + +typedef struct node_data_ { + int int_data; + +} node_data; + + +int node_data_compare(void *list_entry, void *new_entry) +{ + /* + * < 0 if new_entry < list_entry + * == 0 if new_entry == list_entry (new_entry will be inserted after + * list_entry) > 0 if new_entry > list_entry + */ + + return ((node_data *)new_entry)->int_data + - ((node_data *)list_entry)->int_data; +} + + +void test_empty_list() +{ + ordered_list_handle *handle = + ordered_list_initialize(node_data_compare); + + CU_ASSERT_PTR_NOT_NULL(handle); + assert(handle != NULL); + CU_ASSERT_PTR_NULL(handle->head); + CU_ASSERT_PTR_NOT_NULL(handle->compare_function); + CU_ASSERT_EQUAL(handle->num_entries, 0); + + ordered_list_destroy(handle); +} + + +void test_null_list_handle() +{ + node_data data; + ordered_list_node node_data; + + void *ptr = ordered_list_add_node(NULL, &data); + CU_ASSERT_PTR_NULL(ptr); + + ptr = ordered_list_find(NULL, &data); + CU_ASSERT_PTR_NULL(ptr); + + ptr = ordered_list_remove_first_node(NULL); + CU_ASSERT_PTR_NULL(ptr); + + ptr = ordered_list_remove_first_node_equals(NULL, &data); + CU_ASSERT_PTR_NULL(ptr); + + ptr = ordered_list_remove_node(NULL, &node_data, &node_data); + CU_ASSERT_PTR_NULL(ptr); +} + + +void test_add_to_list() +{ + node_data data1, data2, data3; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + + ordered_list_handle *handle = + ordered_list_initialize(node_data_compare); + + ordered_list_add_node(handle, &data3); + ordered_list_add_node(handle, &data1); + ordered_list_add_node(handle, &data2); + + CU_ASSERT_EQUAL(handle->num_entries, 3); + + ordered_list_node *node = handle->head; + CU_ASSERT_PTR_EQUAL(node->data, &data1); + + node = node->next_node; + CU_ASSERT_PTR_EQUAL(node->data, &data2); + + node = node->next_node; + CU_ASSERT_PTR_EQUAL(node->data, &data3); + + node = node->next_node; + CU_ASSERT_PTR_EQUAL(node, NULL); + + ordered_list_destroy(handle); +} + + +void test_find() +{ + node_data data1, data2, data3, data_not_inList; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + data_not_inList.int_data = 5; + + ordered_list_handle *handle = + ordered_list_initialize(node_data_compare); + + ordered_list_add_node(handle, &data3); + ordered_list_add_node(handle, &data2); + ordered_list_add_node(handle, &data1); + + ordered_list_node *node = ordered_list_find(handle, &data1); + CU_ASSERT_PTR_NOT_NULL(node); + assert(node != NULL); + CU_ASSERT_PTR_EQUAL(node->data, &data1); + + node = ordered_list_find(handle, &data2); + CU_ASSERT_PTR_NOT_NULL(node); + assert(node != NULL); + CU_ASSERT_PTR_EQUAL(node->data, &data2); + + node = ordered_list_find(handle, &data3); + CU_ASSERT_PTR_NOT_NULL(node); + assert(node != NULL); + CU_ASSERT_PTR_EQUAL(node->data, &data3); + + node = ordered_list_find(handle, &data_not_inList); + CU_ASSERT_PTR_NULL(node); + + ordered_list_destroy(handle); +} + + +void test_remove_first_node() +{ + node_data data1, data2, data3; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + + ordered_list_handle *handle = + ordered_list_initialize(node_data_compare); + + ordered_list_add_node(handle, &data1); + ordered_list_add_node(handle, &data2); + ordered_list_add_node(handle, &data3); + + void *node_data = ordered_list_remove_first_node(handle); + CU_ASSERT_PTR_NOT_NULL(node_data); + CU_ASSERT_PTR_EQUAL(node_data, &data1); + CU_ASSERT_EQUAL(handle->num_entries, 2); + + node_data = ordered_list_remove_first_node(handle); + CU_ASSERT_PTR_NOT_NULL(node_data); + CU_ASSERT_PTR_EQUAL(node_data, &data2); + CU_ASSERT_EQUAL(handle->num_entries, 1); + + node_data = ordered_list_remove_first_node(handle); + CU_ASSERT_PTR_NOT_NULL(node_data); + CU_ASSERT_PTR_EQUAL(node_data, &data3); + CU_ASSERT_EQUAL(handle->num_entries, 0); + CU_ASSERT_PTR_NULL(handle->head); + + node_data = ordered_list_remove_first_node(handle); + CU_ASSERT_PTR_NULL(node_data); + + ordered_list_destroy(handle); +} + + +void test_remove_first_node_equals() +{ + node_data data1, data2, data3; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + + ordered_list_handle *handle = + ordered_list_initialize(node_data_compare); + + ordered_list_add_node(handle, &data1); + ordered_list_add_node(handle, &data2); + ordered_list_add_node(handle, &data3); + + void *node_data = ordered_list_remove_first_node_equals(handle, &data2); + CU_ASSERT_PTR_NOT_NULL(node_data); + CU_ASSERT_PTR_EQUAL(node_data, &data2); + CU_ASSERT_EQUAL(handle->num_entries, 2); + + node_data = ordered_list_remove_first_node_equals(handle, &data3); + CU_ASSERT_PTR_NOT_NULL(node_data); + CU_ASSERT_PTR_EQUAL(node_data, &data3); + CU_ASSERT_EQUAL(handle->num_entries, 1); + + node_data = ordered_list_remove_first_node_equals(handle, &data1); + CU_ASSERT_PTR_NOT_NULL(node_data); + CU_ASSERT_PTR_EQUAL(node_data, &data1); + CU_ASSERT_EQUAL(handle->num_entries, 0); + + node_data = ordered_list_remove_first_node_equals(handle, &data1); + CU_ASSERT_PTR_NULL(node_data); + + ordered_list_destroy(handle); +} + + +void test_remove_node() +{ + node_data data1, data2, data3; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + + ordered_list_handle *handle = + ordered_list_initialize(node_data_compare); + + ordered_list_node *node1 = ordered_list_add_node(handle, &data1); + ordered_list_node *node2 = ordered_list_add_node(handle, &data2); + ordered_list_node *node3 = ordered_list_add_node(handle, &data3); + + void *node_data = ordered_list_remove_node(handle, node2, node3); + CU_ASSERT_PTR_NOT_NULL(node_data); + CU_ASSERT_PTR_EQUAL(node_data, &data3); + CU_ASSERT_EQUAL(handle->num_entries, 2); + + node_data = ordered_list_remove_node(handle, node1, node2); + CU_ASSERT_PTR_NOT_NULL(node_data); + CU_ASSERT_PTR_EQUAL(node_data, &data2); + CU_ASSERT_EQUAL(handle->num_entries, 1); + + ordered_list_destroy(handle); +} diff --git a/pceplib/test/pcep_utils_ordered_list_test.h b/pceplib/test/pcep_utils_ordered_list_test.h new file mode 100644 index 0000000..3686848 --- /dev/null +++ b/pceplib/test/pcep_utils_ordered_list_test.h @@ -0,0 +1,39 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_UTILS_ORDERED_LIST_TEST_H_ +#define PCEP_UTILS_ORDERED_LIST_TEST_H_ + +void test_empty_list(void); +void test_null_list_handle(void); +void test_add_to_list(void); +void test_find(void); +void test_remove_first_node(void); +void test_remove_first_node_equals(void); +void test_remove_node(void); +int node_data_compare(void *list_entry, void *new_entry); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_utils_queue_test.c b/pceplib/test/pcep_utils_queue_test.c new file mode 100644 index 0000000..6e62c75 --- /dev/null +++ b/pceplib/test/pcep_utils_queue_test.c @@ -0,0 +1,163 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <assert.h> +#include <CUnit/CUnit.h> + +#include "pcep_utils_queue.h" +#include "pcep_utils_queue_test.h" + +typedef struct node_data_ { + int int_data; + +} node_data; + + +void test_empty_queue() +{ + queue_handle *handle = queue_initialize(); + + CU_ASSERT_PTR_NOT_NULL(handle); + assert(handle != NULL); + CU_ASSERT_PTR_NULL(handle->head); + CU_ASSERT_EQUAL(handle->num_entries, 0); + + queue_destroy(handle); +} + + +void test_null_queue_handle() +{ + /* test each method handles a NULL handle without crashing */ + node_data data; + queue_destroy(NULL); + void *ptr = queue_enqueue(NULL, &data); + CU_ASSERT_PTR_NULL(ptr); + + ptr = queue_dequeue(NULL); + CU_ASSERT_PTR_NULL(ptr); +} + + +void test_enqueue() +{ + node_data data1, data2, data3; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + + queue_handle *handle = queue_initialize(); + + queue_enqueue(handle, &data1); + queue_enqueue(handle, &data2); + queue_enqueue(handle, &data3); + + CU_ASSERT_EQUAL(handle->num_entries, 3); + + queue_node *node = handle->head; + CU_ASSERT_PTR_EQUAL(node->data, &data1); + + node = node->next_node; + CU_ASSERT_PTR_EQUAL(node->data, &data2); + + node = node->next_node; + CU_ASSERT_PTR_EQUAL(node->data, &data3); + + node = node->next_node; + CU_ASSERT_PTR_NULL(node); + + queue_destroy(handle); +} + + +void test_enqueue_with_limit() +{ + node_data data1, data2, data3; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + + queue_handle *handle = queue_initialize_with_size(2); + + queue_node *node = queue_enqueue(handle, &data1); + CU_ASSERT_PTR_NOT_NULL(node); + + node = queue_enqueue(handle, &data2); + CU_ASSERT_PTR_NOT_NULL(node); + + node = queue_enqueue(handle, &data3); + CU_ASSERT_PTR_NULL(node); + + CU_ASSERT_EQUAL(handle->num_entries, 2); + + node = handle->head; + CU_ASSERT_PTR_EQUAL(node->data, &data1); + + node = node->next_node; + CU_ASSERT_PTR_EQUAL(node->data, &data2); + + node = node->next_node; + CU_ASSERT_PTR_NULL(node); + + queue_destroy(handle); +} + + +void test_dequeue() +{ + node_data data1, data2, data3; + data1.int_data = 1; + data2.int_data = 2; + data3.int_data = 3; + + queue_handle *handle = queue_initialize(); + + /* first test dequeue handles an empty queue */ + void *node_data = queue_dequeue(handle); + CU_ASSERT_PTR_NULL(node_data); + + queue_enqueue(handle, &data1); + queue_enqueue(handle, &data2); + queue_enqueue(handle, &data3); + + node_data = queue_dequeue(handle); + CU_ASSERT_PTR_EQUAL(node_data, &data1); + CU_ASSERT_EQUAL(handle->num_entries, 2); + + node_data = queue_dequeue(handle); + CU_ASSERT_PTR_EQUAL(node_data, &data2); + CU_ASSERT_EQUAL(handle->num_entries, 1); + + node_data = queue_dequeue(handle); + CU_ASSERT_PTR_EQUAL(node_data, &data3); + CU_ASSERT_EQUAL(handle->num_entries, 0); + + node_data = queue_dequeue(handle); + CU_ASSERT_PTR_NULL(node_data); + + queue_destroy(handle); +} diff --git a/pceplib/test/pcep_utils_queue_test.h b/pceplib/test/pcep_utils_queue_test.h new file mode 100644 index 0000000..16236d0 --- /dev/null +++ b/pceplib/test/pcep_utils_queue_test.h @@ -0,0 +1,36 @@ +/* + * 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 : Javier Garcia <javier.garcia@voltanet.io> + * + */ + +/* + * Timer definitions to be used internally by the pcep_timers library. + */ + +#ifndef PCEP_UTILS_QUEUE_TEST_H_ +#define PCEP_UTILS_QUEUE_TEST_H_ + +void test_empty_queue(void); +void test_null_queue_handle(void); +void test_enqueue(void); +void test_enqueue_with_limit(void); +void test_dequeue(void); + +#endif /* PCEPTIMERINTERNALS_H_ */ diff --git a/pceplib/test/pcep_utils_tests.c b/pceplib/test/pcep_utils_tests.c new file mode 100644 index 0000000..ad9f769 --- /dev/null +++ b/pceplib/test/pcep_utils_tests.c @@ -0,0 +1,140 @@ +/* + * 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> + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <CUnit/Basic.h> +#include <CUnit/CUnit.h> +#include <CUnit/TestDB.h> +#include "pcep_utils_ordered_list_test.h" +#include "pcep_utils_queue_test.h" +#include "pcep_utils_double_linked_list_test.h" +#include "pcep_utils_counters_test.h" +#include "pcep_utils_memory_test.h" + + +int main(int argc, char **argv) +{ + /* Unused parameters cause compilation warnings */ + (void)argc; + (void)argv; + + CU_initialize_registry(); + + CU_pSuite test_queue_suite = + CU_add_suite("PCEP Utils Queue Test Suite", NULL, NULL); + CU_add_test(test_queue_suite, "test_empty_queue", test_empty_queue); + CU_add_test(test_queue_suite, "test_null_queue_handle", + test_null_queue_handle); + CU_add_test(test_queue_suite, "test_enqueue", test_enqueue); + CU_add_test(test_queue_suite, "test_enqueue_with_limit", + test_enqueue_with_limit); + CU_add_test(test_queue_suite, "test_dequeue", test_dequeue); + + CU_pSuite test_list_suite = + CU_add_suite("PCEP Utils Ordered List Test Suite", NULL, NULL); + CU_add_test(test_list_suite, "test_empty_list", test_empty_list); + CU_add_test(test_list_suite, "test_null_handle", test_null_list_handle); + CU_add_test(test_list_suite, "test_add_toList", test_add_to_list); + CU_add_test(test_list_suite, "test_find", test_find); + CU_add_test(test_list_suite, "test_remove_first_node", + test_remove_first_node); + CU_add_test(test_list_suite, "test_remove_first_node_equals", + test_remove_first_node_equals); + CU_add_test(test_list_suite, "test_remove_node", test_remove_node); + + CU_pSuite test_dl_list_suite = CU_add_suite( + "PCEP Utils Double Linked List Test Suite", NULL, NULL); + CU_add_test(test_dl_list_suite, "test_empty_dl_list", + test_empty_dl_list); + CU_add_test(test_dl_list_suite, "test_null_dl_handle", + test_null_dl_list_handle); + CU_add_test(test_dl_list_suite, "test_dll_prepend_data", + test_dll_prepend_data); + CU_add_test(test_dl_list_suite, "test_dll_append_data", + test_dll_append_data); + CU_add_test(test_dl_list_suite, "test_dll_delete_first_node", + test_dll_delete_first_node); + CU_add_test(test_dl_list_suite, "test_dll_delete_last_node", + test_dll_delete_last_node); + CU_add_test(test_dl_list_suite, "test_dll_delete_node", + test_dll_delete_node); + + CU_pSuite test_counters_suite = + CU_add_suite("PCEP Utils Counters Test Suite", NULL, NULL); + CU_add_test(test_counters_suite, "test_create_counters_group", + test_create_counters_group); + CU_add_test(test_counters_suite, "test_create_counters_subgroup", + test_create_counters_subgroup); + CU_add_test(test_counters_suite, "test_add_counters_subgroup", + test_add_counters_subgroup); + CU_add_test(test_counters_suite, "test_create_subgroup_counter", + test_create_subgroup_counter); + CU_add_test(test_counters_suite, "test_delete_counters_group", + test_delete_counters_group); + CU_add_test(test_counters_suite, "test_delete_counters_subgroup", + test_delete_counters_subgroup); + CU_add_test(test_counters_suite, "test_reset_group_counters", + test_reset_group_counters); + CU_add_test(test_counters_suite, "test_reset_subgroup_counters", + test_reset_subgroup_counters); + CU_add_test(test_counters_suite, "test_increment_counter", + test_increment_counter); + CU_add_test(test_counters_suite, "test_increment_subgroup_counter", + test_increment_subgroup_counter); + CU_add_test(test_counters_suite, "test_dump_counters_group_to_log", + test_dump_counters_group_to_log); + CU_add_test(test_counters_suite, "test_dump_counters_subgroup_to_log", + test_dump_counters_subgroup_to_log); + + CU_pSuite test_memory_suite = + CU_add_suite("PCEP Utils Memory Test Suite", NULL, NULL); + CU_add_test(test_memory_suite, "test_memory_internal_impl", + test_memory_internal_impl); + CU_add_test(test_memory_suite, "test_memory_external_impl", + test_memory_external_impl); + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_FailureRecord *failure_record = CU_get_failure_list(); + if (failure_record != NULL) { + printf("\nFailed tests:\n\t [Suite] [Test] [File:line-number]\n"); + do { + printf("\t [%s] [%s] [%s:%d]\n", + failure_record->pSuite->pName, + failure_record->pTest->pName, + failure_record->strFileName, + failure_record->uiLineNumber); + failure_record = failure_record->pNext; + + } while (failure_record != NULL); + } + + CU_pRunSummary run_summary = CU_get_run_summary(); + int result = run_summary->nTestsFailed; + CU_cleanup_registry(); + + return result; +} diff --git a/pceplib/test/pcep_utils_tests_valgrind.sh b/pceplib/test/pcep_utils_tests_valgrind.sh new file mode 100755 index 0000000..6348d82 --- /dev/null +++ b/pceplib/test/pcep_utils_tests_valgrind.sh @@ -0,0 +1,2 @@ +source pceplib/test/pcep_tests_valgrind.sh +valgrind_test pceplib/test/pcep_utils_tests diff --git a/pceplib/test/subdir.am b/pceplib/test/subdir.am new file mode 100644 index 0000000..88af592 --- /dev/null +++ b/pceplib/test/subdir.am @@ -0,0 +1,122 @@ +if PATHD_PCEP +if PATHD_PCEP_TEST + +# The default Automake target is check, add a test target to call check. +# Also make sure the binaries are current before running the tests. +test: pceplib/test/pcep_msg_tests pceplib/test/pcep_pcc_api_tests pceplib/test/pcep_session_logic_tests pceplib/test/pcep_socket_comm_tests pceplib/test/pcep_timers_tests pceplib/test/pcep_utils_tests + +check_SCRIPTS = pceplib/test/pcep_msg_tests pceplib/test/pcep_pcc_api_tests pceplib/test/pcep_session_logic_tests pceplib/test/pcep_socket_comm_tests pceplib/test/pcep_timers_tests pceplib/test/pcep_utils_tests +TESTS = $(check_SCRIPTS) + + +# Definitions to build the Unit Test binaries with CUnit +noinst_PROGRAMS += pceplib/test/pcep_msg_tests \ + pceplib/test/pcep_pcc_api_tests \ + pceplib/test/pcep_session_logic_tests \ + pceplib/test/pcep_socket_comm_tests \ + pceplib/test/pcep_timers_tests \ + pceplib/test/pcep_utils_tests + +noinst_HEADERS += pceplib/test/pcep_msg_messages_test.h \ + pceplib/test/pcep_msg_object_error_types_test.h \ + pceplib/test/pcep_msg_objects_test.h \ + pceplib/test/pcep_msg_tlvs_test.h \ + pceplib/test/pcep_msg_tools_test.h \ + pceplib/test/pcep_pcc_api_test.h \ + pceplib/test/pcep_session_logic_loop_test.h \ + pceplib/test/pcep_session_logic_states_test.h \ + pceplib/test/pcep_session_logic_test.h \ + pceplib/test/pcep_socket_comm_loop_test.h \ + pceplib/test/pcep_socket_comm_test.h \ + pceplib/test/pcep_timers_event_loop_test.h \ + pceplib/test/pcep_timers_test.h \ + pceplib/test/pcep_utils_counters_test.h \ + pceplib/test/pcep_utils_double_linked_list_test.h \ + pceplib/test/pcep_utils_memory_test.h \ + pceplib/test/pcep_utils_ordered_list_test.h \ + pceplib/test/pcep_utils_queue_test.h + +pceplib_test_pcep_msg_tests_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/pceplib +pceplib_test_pcep_msg_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread +pceplib_test_pcep_msg_tests_SOURCES = pceplib/test/pcep_msg_messages_test.c \ + pceplib/test/pcep_msg_messages_tests.c \ + pceplib/test/pcep_msg_object_error_types_test.c \ + pceplib/test/pcep_msg_objects_test.c \ + pceplib/test/pcep_msg_tlvs_test.c \ + pceplib/test/pcep_msg_tools_test.c + +# The pcc_api_tests and pcep_session_logic_tests use the +# socket_comm_mock, so the LDADD variable needs to be modified +pceplib_test_pcep_pcc_api_tests_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/pceplib +pceplib_test_pcep_pcc_api_tests_LDADD = $(top_builddir)/pceplib/libsocket_comm_mock.la $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread +pceplib_test_pcep_pcc_api_tests_SOURCES = pceplib/test/pcep_pcc_api_test.c pceplib/test/pcep_pcc_api_tests.c + +pceplib_test_pcep_session_logic_tests_CFLAGS = -I$(top_srcdir)/pceplib +pceplib_test_pcep_session_logic_tests_LDADD = $(top_builddir)/pceplib/libsocket_comm_mock.la $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread +pceplib_test_pcep_session_logic_tests_SOURCES = pceplib/test/pcep_session_logic_loop_test.c \ + pceplib/test/pcep_session_logic_states_test.c \ + pceplib/test/pcep_session_logic_test.c \ + pceplib/test/pcep_session_logic_tests.c + +pceplib_test_pcep_socket_comm_tests_CFLAGS = -I$(top_srcdir)/pceplib +pceplib_test_pcep_socket_comm_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread +pceplib_test_pcep_socket_comm_tests_SOURCES = pceplib/test/pcep_socket_comm_loop_test.c \ + pceplib/test/pcep_socket_comm_test.c \ + pceplib/test/pcep_socket_comm_tests.c + +pceplib_test_pcep_timers_tests_CFLAGS = -I$(top_srcdir)/pceplib +pceplib_test_pcep_timers_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread +pceplib_test_pcep_timers_tests_SOURCES = pceplib/test/pcep_timers_event_loop_test.c \ + pceplib/test/pcep_timers_test.c \ + pceplib/test/pcep_timers_tests.c + +pceplib_test_pcep_utils_tests_CFLAGS = -I$(top_srcdir)/pceplib +pceplib_test_pcep_utils_tests_LDADD = $(top_builddir)/pceplib/libpcep_pcc.la lib/libfrr.la -lcunit -lpthread +pceplib_test_pcep_utils_tests_SOURCES = pceplib/test/pcep_utils_counters_test.c \ + pceplib/test/pcep_utils_double_linked_list_test.c \ + pceplib/test/pcep_utils_memory_test.c \ + pceplib/test/pcep_utils_ordered_list_test.c \ + pceplib/test/pcep_utils_queue_test.c \ + pceplib/test/pcep_utils_tests.c + +# These test scripts will call the test binaries +# defined above in noinst_PROGRAMS with Valgrind +if HAVE_VALGRIND_PCEP + +dist_noinst_SCRIPTS = pceplib/test/pcep_pcc_api_tests_valgrind.sh \ + pceplib/test/pcep_session_logic_tests_valgrind.sh \ + pceplib/test/pcep_socket_comm_tests_valgrind.sh \ + pceplib/test/pcep_timers_tests_valgrind.sh \ + pceplib/test/pcep_utils_tests_valgrind.sh \ + pceplib/test/pcep_msg_tests_valgrind.sh \ + pceplib/test/pcep_tests_valgrind.sh + +check_SCRIPTS += pceplib/test/pcep_msg_tests_valgrind.sh \ + pceplib/test/pcep_pcc_api_tests_valgrind.sh \ + pceplib/test/pcep_session_logic_tests_valgrind.sh \ + pceplib/test/pcep_socket_comm_tests_valgrind.sh \ + pceplib/test/pcep_timers_tests_valgrind.sh \ + pceplib/test/pcep_utils_tests_valgrind.sh + +TESTS += $(check_SCRIPTS) + + + +pceplib/test/pcep_msg_tests_valgrind.sh: + chmod +x pceplib/test/pcep_msg_tests_valgrind.sh +pceplib/test/pcep_pcc_api_tests_valgrind.sh: + chmod +x pceplib/test/pcep_pcc_api_tests_valgrind.sh +pceplib/test/pcep_session_logic_tests_valgrind.sh: + chmod +x pceplib/test/pcep_session_logic_tests_valgrind.sh +pceplib/test/pcep_socket_comm_tests_valgrind.sh: + chmod +x pceplib/test/pcep_socket_comm_tests_valgrind.sh +pceplib/test/pcep_timers_tests_valgrind.sh: + chmod +x pceplib/test/pcep_timers_tests_valgrind.sh +pceplib/test/pcep_utils_tests_valgrind.sh: + chmod +x pceplib/test/pcep_utils_tests_valgrind.sh + + +endif + +endif +endif |