diff options
| author | Javier Garcia <javier.garcia@voltanet.io> | 2021-01-22 10:38:12 +0100 | 
|---|---|---|
| committer | Javier Garcia <javier.garcia@voltanet.io> | 2021-03-05 12:12:47 +0100 | 
| commit | 749714731ee9a59ae39be77e7db3915ce3ad0bd8 (patch) | |
| tree | 2a50fcd5ce5c2f01ac381ba769c3d8f145768f9d /pceplib | |
| parent | 40c1b0e6b8ad0a76f159885cb1a866f645b917fd (diff) | |
pceplib: Integrate pcelib into frr
Signed-off-by: Brady Johnson <brady@voltanet.io>
Co-authored-by: Javier Garcia <javier.garcia@voltanet.io>
Signed-off-by: Javier Garcia <javier.garcia@voltanet.io>
Diffstat (limited to 'pceplib')
100 files changed, 26051 insertions, 0 deletions
diff --git a/pceplib/.gitignore b/pceplib/.gitignore new file mode 100644 index 0000000000..5861f25a41 --- /dev/null +++ b/pceplib/.gitignore @@ -0,0 +1,14 @@ +pcep_pcc +test/pcep_msg_tests +test/pcep_pcc_api_tests +test/pcep_session_logic_tests +test/pcep_socket_comm_tests +test/pcep_timers_tests +test/pcep_utils_tests +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 0000000000..278ab9d5dc --- /dev/null +++ b/pceplib/pcep.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> + * + */ + + +#ifndef PCEP_H_ +#define PCEP_H_ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(linux) || defined(GNU_LINUX) +//#include <netinet/in.h> +#define ipv6_u __in6_u +#else +// bsd family +#define TCP_MD5SIG_MAXKEYLEN 80 +//#include <netinet/in.h> +#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> +#endif diff --git a/pceplib/pcep_msg_encoding.h b/pceplib/pcep_msg_encoding.h new file mode 100644 index 0000000000..d835b87f94 --- /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 0000000000..ec2a237f30 --- /dev/null +++ b/pceplib/pcep_msg_messages.c @@ -0,0 +1,308 @@ +/* + * 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. + */ + +#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 0000000000..8542ea10e7 --- /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 0000000000..23ccef480c --- /dev/null +++ b/pceplib/pcep_msg_messages_encoding.c @@ -0,0 +1,351 @@ +/* + * 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. + */ + +#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); + +	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 0000000000..a4fd8151cd --- /dev/null +++ b/pceplib/pcep_msg_object_error_types.c @@ -0,0 +1,389 @@ +/* + * 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> + */ + +#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 0000000000..d62cc7e277 --- /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 0000000000..6c943ddc2a --- /dev/null +++ b/pceplib/pcep_msg_objects.c @@ -0,0 +1,854 @@ +/* + * 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. + */ + +#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 +	 * doesnt 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 0000000000..959a6f8cf6 --- /dev/null +++ b/pceplib/pcep_msg_objects.h @@ -0,0 +1,741 @@ +/* + * 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; +}; + +/* RFC 7470 */ +struct pcep_object_vendor_info { +	struct pcep_object_header header; +	uint32_t enterprise_number; +	uint32_t enterprise_specific_info; +}; + +/* 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 subojbects 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 0000000000..d40b840869 --- /dev/null +++ b/pceplib/pcep_msg_objects_encoding.c @@ -0,0 +1,1720 @@ +/* + * 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. + */ + +#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 doesnt 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 doesnt 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 doesnt 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 doesnt 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))); + +	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 + LENGTH_4WORDS, 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[LENGTH_4WORDS]; +				dll_append(sr_subobj->nai_list, ipv4); + +				ipv6 = pceplib_malloc(PCEPLIB_MESSAGES, +						      sizeof(struct in6_addr)); +				decode_ipv6(uint32_ptr + LENGTH_5WORDS, ipv6); +				dll_append(sr_subobj->nai_list, ipv6); + +				ipv4 = pceplib_malloc(PCEPLIB_MESSAGES, +						      sizeof(struct in_addr)); +				ipv4->s_addr = uint32_ptr[LENGTH_9WORDS]; +				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 0000000000..890da9517f --- /dev/null +++ b/pceplib/pcep_msg_tlvs.c @@ -0,0 +1,464 @@ +/* + * 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. + */ + +#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, 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, 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 0000000000..5197201e40 --- /dev/null +++ b/pceplib/pcep_msg_tlvs.h @@ -0,0 +1,380 @@ +/* + * 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_TLV_TYPE_ARBITRARY = +		65533 /* Max IANA To write arbitrary data */ +}; + +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 0000000000..3322663dc3 --- /dev/null +++ b/pceplib/pcep_msg_tlvs_encoding.c @@ -0,0 +1,1282 @@ +/* + * 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. + */ + +#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() +{ +	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 = 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 = +			(struct pcep_object_tlv_srpag_pol_id *)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 0000000000..1d157ec3f5 --- /dev/null +++ b/pceplib/pcep_msg_tools.c @@ -0,0 +1,465 @@ +/* + * 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> + * + */ + + +#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_MAX_SIZE] = {0}; +	uint16_t buffer_read = 0; + + +	ret = read(sock_fd, &buffer, PCEP_MAX_SIZE); + +	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 ((ret - buffer_read) >= MESSAGE_HEADER_LENGTH) { + +		/* Get the Message header, validate it, and return the msg +		 * length */ +		int32_t msg_hdr_length = +			pcep_decode_validate_msg_header(buffer + buffer_read); +		if (msg_hdr_length < 0) { +			/* 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_hdr_length is longer than what was read, +		 * in which case, we need to read the rest of the message. */ +		if ((ret - buffer_read) < msg_hdr_length) { +			int read_len = (msg_hdr_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); + +			read_ret = read(sock_fd, &buffer[ret], read_len); + +			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_hdr_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; +	} + +	return write(sock_fd, msg->encoded_message, +		     ntohs(msg->encoded_message_length)); +} diff --git a/pceplib/pcep_msg_tools.h b/pceplib/pcep_msg_tools.h new file mode 100644 index 0000000000..b62bdde1cf --- /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 0000000000..2171f883cd --- /dev/null +++ b/pceplib/pcep_pcc.c @@ -0,0 +1,517 @@ +/* + * 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[TCP_MD5SIG_MAXKEYLEN]; /* RFC 2385 */ +	bool is_ipv6; +	bool eventpoll; /* poll for pcep_event's, or use callback (default) */ +}; + +bool pcc_active_ = true; +pcep_session *session = NULL; +struct cmd_line_args *cmd_line_args = NULL; +/* pcep_event callback variables */ +bool pcep_event_condition = false; +struct pcep_event *event = NULL; +pthread_mutex_t pcep_event_mutex; +pthread_cond_t pcep_event_cond_var; + +static const char DEFAULT_DEST_HOSTNAME[] = "localhost"; +static const char DEFAULT_DEST_HOSTNAME_IPV6[] = "ip6-localhost"; +static const short DEFAULT_SRC_TCP_PORT = 4999; + +// Private fn's +struct cmd_line_args *get_cmdline_args(int argc, char *argv[]); +void handle_signal_action(int sig_number); +int setup_signals(void); +void send_pce_path_request_message(pcep_session *session); +void send_pce_report_message(pcep_session *session); +void print_queue_event(struct pcep_event *event); +void pcep_event_callback(void *cb_data, pcep_event *e); + +struct cmd_line_args *get_cmdline_args(int argc, char *argv[]) +{ +	/* Allocate and set default values */ +	struct cmd_line_args *cmd_line_args = +		malloc(sizeof(struct cmd_line_args)); +	memset(cmd_line_args, 0, sizeof(struct cmd_line_args)); +	strlcpy(cmd_line_args->dest_ip_str, DEFAULT_DEST_HOSTNAME, +		MAX_DST_IP_STR); +	cmd_line_args->src_tcp_port = DEFAULT_SRC_TCP_PORT; +	cmd_line_args->is_ipv6 = false; + +	/* Parse the cmd_line args: +	 * -ipv6 +	 * -srcip localhost +	 * -destip 192.168.0.2 +	 * -srcport 4999 +	 * -dstport 4189 +	 * -tcpmd5 hello +	 * -event_poll */ +	int i = 1; +	for (; i < argc; ++i) { +		if (strcmp(argv[i], "-help") == 0 +		    || strcmp(argv[i], "--help") == 0 +		    || strcmp(argv[i], "-h") == 0) { +			pcep_log( +				LOG_INFO, +				"%s: pcep_pcc [-ipv6] [-srcip localhost] [-destip 192.168.0.1] [-srcport 4999] [-dstport 4189] [-tcpmd5 authstr] [-eventpoll]", +				__func__); +			free(cmd_line_args); +			return NULL; +		} else if (strcmp(argv[i], "-ipv6") == 0) { +			cmd_line_args->is_ipv6 = true; +			if (argc == 2) { +				strlcpy(cmd_line_args->dest_ip_str, +					DEFAULT_DEST_HOSTNAME_IPV6, +					MAX_DST_IP_STR); +			} +		} else if (strcmp(argv[i], "-eventpoll") == 0) { +			cmd_line_args->eventpoll = true; +		} else if (strcmp(argv[i], "-srcip") == 0) { +			if (argc >= i + 2) { +				strlcpy(cmd_line_args->src_ip_str, argv[++i], +					MAX_SRC_IP_STR); +			} else { +				pcep_log( +					LOG_ERR, +					"%s: Invalid number of cmd_line_args for \"-srcip\"", +					__func__); +				free(cmd_line_args); +				return NULL; +			} +		} else if (strcmp(argv[i], "-destip") == 0) { +			if (argc >= i + 2) { +				strlcpy(cmd_line_args->dest_ip_str, argv[++i], +					MAX_DST_IP_STR); +			} else { +				pcep_log( +					LOG_ERR, +					"%s: Invalid number of cmd_line_args for \"-destip\"", +					__func__); +				free(cmd_line_args); +				return NULL; +			} +		} else if (strcmp(argv[i], "-srcport") == 0) { +			if (argc >= i + 2) { +				cmd_line_args->src_tcp_port = atoi(argv[++i]); +			} else { +				pcep_log( +					LOG_ERR, +					"%s: Invalid number of cmd_line_args for \"-srcport\"", +					__func__); +				free(cmd_line_args); +				return NULL; +			} +		} else if (strcmp(argv[i], "-destport") == 0) { +			if (argc >= i + 2) { +				cmd_line_args->dest_tcp_port = atoi(argv[++i]); +			} else { +				pcep_log( +					LOG_ERR, +					"%s: Invalid number of cmd_line_args for \"-destport\"", +					__func__); +				free(cmd_line_args); +				return NULL; +			} +		} else if (strcmp(argv[i], "-tcpmd5") == 0) { +			if (argc >= i + 2) { +				strlcpy(cmd_line_args->tcp_md5_str, argv[++i], +					sizeof(cmd_line_args->tcp_md5_str)); +			} else { +				pcep_log( +					LOG_ERR, +					"%s: Invalid number of cmd_line_args for \"-tcpmd5\"", +					__func__); +				free(cmd_line_args); +				return NULL; +			} +		} else { +			pcep_log(LOG_ERR, "%s: Invalid cmd_line_arg[%d] = %s", +				 __func__, i, argv[i]); +			free(cmd_line_args); +			return NULL; +		} +	} + +	return cmd_line_args; +} + +void handle_signal_action(int sig_number) +{ +	if (sig_number == SIGINT) { +		pcep_log(LOG_INFO, "%s: SIGINT was caught!", __func__); +		pcc_active_ = false; +		if (cmd_line_args->eventpoll == false) { +			pthread_mutex_lock(&pcep_event_mutex); +			pcep_event_condition = true; +			pthread_cond_signal(&pcep_event_cond_var); +			pthread_mutex_unlock(&pcep_event_mutex); +		} +	} else if (sig_number == SIGUSR1) { +		pcep_log(LOG_INFO, "%s: SIGUSR1 was caught, dumping counters", +			 __func__); +		dump_pcep_session_counters(session); +		pceplib_memory_dump(); +	} else if (sig_number == SIGUSR2) { +		pcep_log(LOG_INFO, "%s: SIGUSR2 was caught, reseting counters", +			 __func__); +		reset_pcep_session_counters(session); +	} +} + + +int setup_signals() +{ +	struct sigaction sa; +	memset(&sa, 0, sizeof(struct sigaction)); +	sa.sa_handler = handle_signal_action; +	if (sigaction(SIGINT, &sa, 0) != 0) { +		perror("sigaction()"); +		return -1; +	} + +	if (sigaction(SIGUSR1, &sa, 0) != 0) { +		perror("sigaction()"); +		return -1; +	} + +	if (sigaction(SIGUSR2, &sa, 0) != 0) { +		perror("sigaction()"); +		return -1; +	} + +	return 0; +} + +void send_pce_path_request_message(pcep_session *session) +{ +	struct in_addr src_ipv4; +	struct in_addr dst_ipv4; +	inet_pton(AF_INET, "1.2.3.4", &src_ipv4); +	inet_pton(AF_INET, "10.20.30.40", &dst_ipv4); + +	struct pcep_object_rp *rp_object = +		pcep_obj_create_rp(1, false, false, false, false, 42, NULL); +	struct pcep_object_endpoints_ipv4 *ep_object = +		pcep_obj_create_endpoint_ipv4(&src_ipv4, &dst_ipv4); + +	struct pcep_message *path_request = +		pcep_msg_create_request(rp_object, ep_object, NULL); +	send_message(session, path_request, true); +} + +void send_pce_report_message(pcep_session *session) +{ +	double_linked_list *report_list = dll_initialize(); + +	/* SRP Path Setup Type TLV */ +	struct pcep_object_tlv_path_setup_type *pst_tlv = +		pcep_tlv_create_path_setup_type(SR_TE_PST); +	double_linked_list *srp_tlv_list = dll_initialize(); +	dll_append(srp_tlv_list, pst_tlv); + +	/* +	 * Create the SRP object +	 */ +	uint32_t srp_id_number = 0x10203040; +	struct pcep_object_header *obj = +		(struct pcep_object_header *)pcep_obj_create_srp( +			false, srp_id_number, srp_tlv_list); +	if (obj == NULL) { +		pcep_log(LOG_WARNING, +			 "%s: send_pce_report_message SRP object was NULL", +			 __func__); +		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__); +		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__); +		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 0000000000..b7813c5a05 --- /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 0000000000..5756e23efc --- /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 0000000000..5e4dae4900 --- /dev/null +++ b/pceplib/pcep_session_logic.c @@ -0,0 +1,683 @@ +/* + * 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> + * + */ + + +#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() +{ +	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_logic_condition = false; +	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; +	} + +	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() +{ +	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( +				session->pcc_config +					.support_stateful_pce_lsp_update, /* U +									     flag +									   */ +				session->pcc_config +					.support_include_db_version, /* S flag +								      */ +				session->pcc_config +					.support_lsp_triggered_resync, /* T flag +									*/ +				session->pcc_config +					.support_lsp_delta_sync, /* D flag */ +				session->pcc_config +					.support_pce_triggered_initial_sync, /* F flag */ +				session->pcc_config +					.support_pce_lsp_instantiation)); /* I +									     flag +									   */ +	} + +	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 0000000000..a082ec66da --- /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[TCP_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 0000000000..a6bd41b4f1 --- /dev/null +++ b/pceplib/pcep_session_logic_counters.c @@ -0,0 +1,450 @@ +/* + * 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. + */ + +#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 0000000000..004459b918 --- /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 0000000000..5705ff2000 --- /dev/null +++ b/pceplib/pcep_session_logic_loop.c @@ -0,0 +1,360 @@ +/* + * 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> + * + */ + +#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); +	} 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 0000000000..5fac667655 --- /dev/null +++ b/pceplib/pcep_session_logic_states.c @@ -0,0 +1,1133 @@ +/* + * 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> + * + */ + +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.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); + +	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 0000000000..e22eb6e675 --- /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 0000000000..797ffda860 --- /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 */ +	char tcp_authentication_str[TCP_MD5SIG_MAXKEYLEN +				    + 1]; /* should be used with is_tcp_auth_md5 +					     flag */ +	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 0000000000..4445a14fac --- /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 0000000000..8346c93025 --- /dev/null +++ b/pceplib/pcep_socket_comm_loop.c @@ -0,0 +1,486 @@ +/* + * 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> + * + */ + + +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <string.h> +#include <unistd.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; +		} + +		/*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; +		} + +		/*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 doesnt 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 0000000000..3ca2c037fa --- /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 0000000000..069d0cf998 --- /dev/null +++ b/pceplib/pcep_socket_comm_mock.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> + * + */ + + +/* + * 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. + */ + +#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 0000000000..ca0e38b803 --- /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 0000000000..8221c78baa --- /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 0000000000..e9d9d4b21d --- /dev/null +++ b/pceplib/pcep_timers.c @@ -0,0 +1,482 @@ +/* + * 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. + */ + +#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_() +{ +	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: Reseting 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); +	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); + +	pthread_mutex_lock(&timers_context_->timer_list_lock); +	ordered_list_remove_node2(timers_context_->timer_list, timer_node); +	pthread_mutex_unlock(&timers_context_->timer_list_lock); + +	pceplib_free(PCEPLIB_INFRA, timer); +} diff --git a/pceplib/pcep_timers.h b/pceplib/pcep_timers.h new file mode 100644 index 0000000000..b2cc6ec546 --- /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 0000000000..932a53eb2a --- /dev/null +++ b/pceplib/pcep_timers_event_loop.c @@ -0,0 +1,106 @@ +/* + * 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> + * + */ + + +#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 0000000000..c4a7264da3 --- /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 0000000000..d8078f683f --- /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 0000000000..240e9758b7 --- /dev/null +++ b/pceplib/pcep_utils_counters.h @@ -0,0 +1,232 @@ +/* + * 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> + +#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 0000000000..acdcee0598 --- /dev/null +++ b/pceplib/pcep_utils_double_linked_list.c @@ -0,0 +1,262 @@ +/* + * 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> + * + */ + +#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 0000000000..4fe01726ad --- /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 0000000000..65e1abbc03 --- /dev/null +++ b/pceplib/pcep_utils_logging.c @@ -0,0 +1,82 @@ +/* + * 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> + * + */ + + +#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 0000000000..24ea495bd8 --- /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 0000000000..7362e3433b --- /dev/null +++ b/pceplib/pcep_utils_memory.c @@ -0,0 +1,220 @@ +/* + * 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> + * + */ + +#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 0000000000..4624a91a1c --- /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 0000000000..f5c7f70240 --- /dev/null +++ b/pceplib/pcep_utils_ordered_list.c @@ -0,0 +1,322 @@ +/* + * 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> + * + */ + + +#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 0000000000..ec132dc164 --- /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 0000000000..e8c3f2be0e --- /dev/null +++ b/pceplib/pcep_utils_queue.c @@ -0,0 +1,150 @@ +/* + * 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> + * + */ + + +#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 0000000000..838067640e --- /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 0000000000..eee2ec28c7 --- /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 = -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 0000000000..10b678bcec --- /dev/null +++ b/pceplib/test/pcep_msg_messages_test.c @@ -0,0 +1,498 @@ +/* + * 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> + * + */ + + +#include <stdlib.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); +	CU_ASSERT_PTR_NOT_NULL(message->msg_header); +	CU_ASSERT_PTR_NOT_NULL(message->obj_list); +	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)); +	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); +	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)); +	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); +	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(&ipv6_obj->header)); +	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); +	CU_ASSERT_PTR_NOT_NULL(message->msg_header); +	CU_ASSERT_PTR_NOT_NULL(message->obj_list); +	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)); +	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); +	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(&nopath_obj->header))); +	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); +	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); +	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); +	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); +	CU_ASSERT_PTR_NOT_NULL(message->msg_header); +	CU_ASSERT_PTR_NOT_NULL(message->obj_list); +	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)); +	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; +	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); +	CU_ASSERT_PTR_NOT_NULL(message->msg_header); +	CU_ASSERT_PTR_NOT_NULL(message->obj_list); +	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)); +	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); +	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); +	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); +	CU_ASSERT_PTR_NOT_NULL(message->msg_header); +	CU_ASSERT_PTR_NOT_NULL(message->obj_list); +	CU_ASSERT_EQUAL(message->obj_list->num_entries, 1); +	CU_ASSERT_EQUAL(message->encoded_message_length, +			MESSAGE_HEADER_LENGTH +				+ lsp->header.encoded_object_length); +	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); + +	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); +	message = pcep_msg_create_update(obj_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); +	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); +	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); + +	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); + +	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); +	CU_ASSERT_PTR_NOT_NULL(message->msg_header); +	CU_ASSERT_PTR_NOT_NULL(message->obj_list); +	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); +	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); +	CU_ASSERT_PTR_NOT_NULL(message->obj_list); +	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); +	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); +	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 +				+ 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 0000000000..a3295c74eb --- /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 0000000000..bd85a16530 --- /dev/null +++ b/pceplib/test/pcep_msg_messages_tests.c @@ -0,0 +1,256 @@ +/* + * 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> + * + */ + + +#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 0000000000..7275eaf098 --- /dev/null +++ b/pceplib/test/pcep_msg_object_error_types_test.c @@ -0,0 +1,84 @@ +/* + * 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> + * + */ + + +#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 0000000000..863517d1e3 --- /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 0000000000..a4c069945c --- /dev/null +++ b/pceplib/test/pcep_msg_objects_test.c @@ -0,0 +1,1289 @@ +/* + * 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> + * + */ + + +#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) +{ +	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); +	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); +	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 */ +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	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 0000000000..0f08193a59 --- /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 0000000000..4a9a99939d --- /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 0000000000..878e4d62af --- /dev/null +++ b/pceplib/test/pcep_msg_tlvs_test.c @@ -0,0 +1,671 @@ +/* + * 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> + * + */ + + +#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); + +	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); + +	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); + +	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); +	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); + +	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); +	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); + +	/* 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); + +	/* 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); + +	/* 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); + +	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); +	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); + +	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); + +	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); + +	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); +	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); + +	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); + +	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); + +	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); + +	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); + +	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); + +	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); + +	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); + +	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); + +	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); + +	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); + +	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); + +	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); +	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 0000000000..a961d7e473 --- /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 0000000000..a1260b1186 --- /dev/null +++ b/pceplib/test/pcep_msg_tools_test.c @@ -0,0 +1,1258 @@ +/* + * 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> + * + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <CUnit/CUnit.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(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) +{ +} + +/* 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(const char *hexbyte_strs[], +			      uint16_t hexbyte_strs_length) +{ +	int fd = fileno(tmpfile()); + +	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() +{ +	int fd = convert_hexstrs_to_binary(pcep_initiate_hexbyte_strs, +					   pcep_initiate_hexbyte_strs_length); +	double_linked_list *msg_list = pcep_msg_read(fd); +	CU_ASSERT_PTR_NOT_NULL(msg_list); +	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); +	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); +} + + +void test_pcep_msg_read_pcep_initiate2() +{ +	int fd = convert_hexstrs_to_binary(pcep_initiate2_hexbyte_strs, +					   pcep_initiate2_hexbyte_strs_length); +	double_linked_list *msg_list = pcep_msg_read(fd); +	CU_ASSERT_PTR_NOT_NULL(msg_list); +	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); +	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); +} + +void test_pcep_msg_read_pcep_open() +{ +	int fd = convert_hexstrs_to_binary(pcep_open_odl_hexbyte_strs, +					   pcep_open_hexbyte_strs_length); +	double_linked_list *msg_list = pcep_msg_read(fd); +	CU_ASSERT_PTR_NOT_NULL(msg_list); +	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); +} + +void test_pcep_msg_read_pcep_update() +{ +	int fd = convert_hexstrs_to_binary(pcep_update_hexbyte_strs, +					   pcep_update_hexbyte_strs_length); +	double_linked_list *msg_list = pcep_msg_read(fd); +	CU_ASSERT_PTR_NOT_NULL(msg_list); +	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); +	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); +} + +void test_pcep_msg_read_pcep_open_initiate() +{ +	int fd = convert_hexstrs_to_binary( +		pcep_open_initiate_odl_hexbyte_strs, +		pcep_open_initiate_hexbyte_strs_length); +	double_linked_list *msg_list = pcep_msg_read(fd); +	CU_ASSERT_PTR_NOT_NULL(msg_list); +	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); +} + +void test_pcep_msg_read_pcep_open_cisco_pce() +{ +	int fd = convert_hexstrs_to_binary( +		pcep_open_cisco_pce_hexbyte_strs, +		pcep_open_cisco_pce_hexbyte_strs_length); +	double_linked_list *msg_list = pcep_msg_read(fd); +	CU_ASSERT_PTR_NOT_NULL(msg_list); +	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); +	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); +} + +void test_pcep_msg_read_pcep_update_cisco_pce() +{ +	int fd = convert_hexstrs_to_binary( +		pcep_update_cisco_pce_hexbyte_strs, +		pcep_update_cisco_pce_hexbyte_strs_length); +	double_linked_list *msg_list = pcep_msg_read(fd); +	CU_ASSERT_PTR_NOT_NULL(msg_list); +	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); +	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); +	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); +	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); +} + +void test_pcep_msg_read_pcep_report_cisco_pcc() +{ +	int fd = convert_hexstrs_to_binary( +		pcep_report_cisco_pcc_hexbyte_strs, +		pcep_report_cisco_pcc_hexbyte_strs_length); +	double_linked_list *msg_list = pcep_msg_read(fd); +	CU_ASSERT_PTR_NOT_NULL(msg_list); +	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); +	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 not recognized, and its not in the list */ +	CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 2); +	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); +	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); +} + +void test_pcep_msg_read_pcep_initiate_cisco_pcc() +{ +	int fd = convert_hexstrs_to_binary( +		pcep_initiate_cisco_pcc_hexbyte_strs, +		pcep_initiate_cisco_pcc_hexbyte_strs_length); +	double_linked_list *msg_list = pcep_msg_read(fd); +	CU_ASSERT_PTR_NOT_NULL(msg_list); +	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); +	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); +	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); +	CU_ASSERT_PTR_NOT_NULL(switch_layer->switch_layer_rows); +	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); +} + +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 0000000000..dc66390801 --- /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 0000000000..c227dc1a3d --- /dev/null +++ b/pceplib/test/pcep_pcc_api_test.c @@ -0,0 +1,285 @@ +/* + * 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> + * + */ + + +#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); +	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); +	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); +	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); +	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); +	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); +	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 0000000000..d3db96e0ce --- /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 0000000000..04895d6340 --- /dev/null +++ b/pceplib/test/pcep_pcc_api_tests.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> + * + */ + + +#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 0000000000..74494b7521 --- /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 0000000000..38fabd4ccd --- /dev/null +++ b/pceplib/test/pcep_session_logic_loop_test.c @@ -0,0 +1,219 @@ +/* + * 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> + * + */ + + +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.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_logic_condition = false; +	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); + +	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 */ +	int fd = fileno(tmpfile()); +	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); +	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); +	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); +} + + +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); +	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); +	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 0000000000..ae3c3e3753 --- /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 0000000000..f75c16e397 --- /dev/null +++ b/pceplib/test/pcep_session_logic_states_test.c @@ -0,0 +1,919 @@ +/* + * 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> + * + */ + + +#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 doesnt 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); +	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); +	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); +	/* 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); +	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); +	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); +	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); +	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); +	msg = pcep_decode_message(encoded_msg); +	CU_ASSERT_PTR_NOT_NULL(msg); +	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 0000000000..e42b501ed9 --- /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 0000000000..66db4fbaea --- /dev/null +++ b/pceplib/test/pcep_session_logic_test.c @@ -0,0 +1,360 @@ +/* + * 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> + * + */ + + +#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); +	/* 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); +	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); +	/* 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); +	/* Get and verify the Open Message objects */ +	CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list); +	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); +	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); +	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); +	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); +	/* Get and verify the Open Message objects */ +	CU_ASSERT_PTR_NOT_NULL(open_msg->obj_list); +	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); +	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); +	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); +	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); +	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 0000000000..6cc1963250 --- /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 0000000000..67bf6e22ef --- /dev/null +++ b/pceplib/test/pcep_session_logic_tests.c @@ -0,0 +1,201 @@ +/* + * 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> + * + */ + + +#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 0000000000..435bb3d5c4 --- /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 0000000000..94f0983ca7 --- /dev/null +++ b/pceplib/test/pcep_socket_comm_loop_test.c @@ -0,0 +1,194 @@ +/* + * 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> + * + */ + + +#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 0000000000..d2e3f21ee1 --- /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 0000000000..35afbcbb13 --- /dev/null +++ b/pceplib/test/pcep_socket_comm_test.c @@ -0,0 +1,308 @@ +/* + * 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> + * + */ + + +#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); +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	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); +	CU_ASSERT_PTR_NOT_NULL(socket_comm_handle_); +	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 0000000000..f857af087a --- /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 0000000000..293678f1a7 --- /dev/null +++ b/pceplib/test/pcep_socket_comm_tests.c @@ -0,0 +1,128 @@ +/* + * 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> + * + */ + + +#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 0000000000..d9e95e4c1c --- /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 0000000000..ca4772cb67 --- /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 0000000000..9fcacaf0f2 --- /dev/null +++ b/pceplib/test/pcep_timers_event_loop_test.c @@ -0,0 +1,160 @@ +/* + * 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> + * + */ + + +#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; +	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); + +	/* 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 0000000000..19fd264858 --- /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 0000000000..9d9e0f6c1b --- /dev/null +++ b/pceplib/test/pcep_timers_test.c @@ -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 Lesser General Public License + * along with this program.  If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +#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 0000000000..6ac9a90e49 --- /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 0000000000..adfea17e29 --- /dev/null +++ b/pceplib/test/pcep_timers_tests.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> + * + */ + + +#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 0000000000..f9bff3b2a6 --- /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 0000000000..6f53e4d400 --- /dev/null +++ b/pceplib/test/pcep_utils_counters_test.c @@ -0,0 +1,254 @@ +/* + * 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> + * + */ + + +#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); + +	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); + +	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 0000000000..07236dcb53 --- /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 0000000000..d2600e66c4 --- /dev/null +++ b/pceplib/test/pcep_utils_double_linked_list_test.c @@ -0,0 +1,297 @@ +/* + * 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> + * + */ + + +#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_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_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_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_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_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); +	CU_ASSERT_PTR_NOT_NULL(node2); +	CU_ASSERT_PTR_NOT_NULL(node3); +	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 0000000000..ddb6467cb6 --- /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 0000000000..b0b528f084 --- /dev/null +++ b/pceplib/test/pcep_utils_memory_test.c @@ -0,0 +1,241 @@ +/* + * 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> + * + */ + + +#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 0000000000..4e0c3fadf1 --- /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 0000000000..fe9ee58825 --- /dev/null +++ b/pceplib/test/pcep_utils_ordered_list_test.c @@ -0,0 +1,248 @@ +/* + * 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> + * + */ + + +#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); +	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); +	CU_ASSERT_PTR_EQUAL(node->data, &data1); + +	node = ordered_list_find(handle, &data2); +	CU_ASSERT_PTR_NOT_NULL(node); +	CU_ASSERT_PTR_EQUAL(node->data, &data2); + +	node = ordered_list_find(handle, &data3); +	CU_ASSERT_PTR_NOT_NULL(node); +	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 0000000000..3686848b69 --- /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 0000000000..1731457789 --- /dev/null +++ b/pceplib/test/pcep_utils_queue_test.c @@ -0,0 +1,157 @@ +/* + * 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> + * + */ + + +#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); +	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 0000000000..16236d0d9d --- /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 0000000000..452b9fa09c --- /dev/null +++ b/pceplib/test/pcep_utils_tests.c @@ -0,0 +1,136 @@ +/* + * 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> + * + */ + + +#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 0000000000..6348d82708 --- /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 0000000000..0ae61d1bce --- /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 =  -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 =  -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  | 
