diff options
Diffstat (limited to 'pceplib/pcep_msg_messages_encoding.c')
-rw-r--r-- | pceplib/pcep_msg_messages_encoding.c | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/pceplib/pcep_msg_messages_encoding.c b/pceplib/pcep_msg_messages_encoding.c new file mode 100644 index 0000000..e90ca1c --- /dev/null +++ b/pceplib/pcep_msg_messages_encoding.c @@ -0,0 +1,363 @@ +/* + * This file is part of the PCEPlib, a PCEP protocol library. + * + * Copyright (C) 2020 Volta Networks https://voltanet.io/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Author : Brady Johnson <brady@voltanet.io> + * + */ + + +/* + * Encoding and decoding for PCEP messages. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pcep_msg_encoding.h" +#include "pcep_msg_messages.h" +#include "pcep_msg_objects.h" +#include "pcep_msg_tools.h" +#include "pcep_utils_logging.h" +#include "pcep_utils_memory.h" + +#define ANY_OBJECT 0 +#define NO_OBJECT -1 +#define NUM_CHECKED_OBJECTS 4 +/* It wont compile with this definition: + static const int + MANDATORY_MESSAGE_OBJECT_CLASSES[PCEP_TYPE_INITIATE+1][NUM_CHECKED_OBJECTS] + */ +static const enum pcep_object_classes MANDATORY_MESSAGE_OBJECT_CLASSES[13][4] = + { + {NO_OBJECT, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* unsupported message ID = 0 */ + {PCEP_OBJ_CLASS_OPEN, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* PCEP_TYPE_OPEN = 1 */ + {NO_OBJECT, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* PCEP_TYPE_KEEPALIVE = 2 */ + {PCEP_OBJ_CLASS_RP, PCEP_OBJ_CLASS_ENDPOINTS, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_PCREQ = 3 */ + {PCEP_OBJ_CLASS_RP, ANY_OBJECT, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_PCREP = 4 */ + {PCEP_OBJ_CLASS_NOTF, ANY_OBJECT, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_PCNOTF = 5 */ + {PCEP_OBJ_CLASS_ERROR, ANY_OBJECT, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_ERROR = 6 */ + {PCEP_OBJ_CLASS_CLOSE, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* PCEP_TYPE_CLOSE = 7 */ + {NO_OBJECT, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* unsupported message ID = 8 */ + {NO_OBJECT, NO_OBJECT, NO_OBJECT, + NO_OBJECT}, /* unsupported message ID = 9 */ + {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_REPORT = 10 */ + {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_UPDATE = 11 */ + {PCEP_OBJ_CLASS_SRP, PCEP_OBJ_CLASS_LSP, ANY_OBJECT, + ANY_OBJECT}, /* PCEP_TYPE_INITIATE = 12 */ +}; + +/* PCEP Message Common Header, According to RFC 5440 + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ver | Flags | Message-Type | Message-Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Ver (Version - 3 bits): PCEP version number. Current version is version 1. + * + * Flags (5 bits): No flags are currently defined. Unassigned bits are + * considered as reserved. They MUST be set to zero on transmission + * and MUST be ignored on receipt. + */ +void pcep_encode_message(struct pcep_message *message, + struct pcep_versioning *versioning) +{ + if (message == NULL) { + return; + } + + if (message->msg_header == NULL) { + return; + } + + /* Internal buffer used for the entire message. Later, once the entire + * length is known, memory will be allocated and this buffer will be + * copied. */ + uint8_t message_buffer[PCEP_MESSAGE_LENGTH] = {0}; + + /* Write the message header. The message header length will be + * written when the entire length is known. */ + uint32_t message_length = MESSAGE_HEADER_LENGTH; + uint16_t net_order_length = 0; + message_buffer[0] = (message->msg_header->pcep_version << 5) & 0xf0; + message_buffer[1] = message->msg_header->type; + + if (message->obj_list == NULL) { + net_order_length = htons(message_length); + memcpy(message_buffer + 2, &net_order_length, + sizeof(net_order_length)); + message->encoded_message = + pceplib_malloc(PCEPLIB_MESSAGES, message_length); + memcpy(message->encoded_message, message_buffer, + message_length); + message->encoded_message_length = message_length; + + return; + } + + /* Encode each of the objects */ + double_linked_list_node *node = message->obj_list->head; + for (; node != NULL; node = node->next_node) { + message_length += + pcep_encode_object(node->data, versioning, + message_buffer + message_length); + if (message_length >= PCEP_MESSAGE_LENGTH) { + message->encoded_message = NULL; + message->encoded_message_length = 0; + return; + } + } + + net_order_length = htons(message_length); + memcpy(message_buffer + 2, &net_order_length, sizeof(net_order_length)); + message->encoded_message = + pceplib_malloc(PCEPLIB_MESSAGES, message_length); + memcpy(message->encoded_message, message_buffer, message_length); + message->encoded_message_length = message_length; +} + +/* + * Decoding functions + */ + +/* Expecting Host byte ordered header */ +static bool validate_msg_header(uint8_t msg_version, uint8_t msg_flags, + uint8_t msg_type, uint16_t msg_length) +{ + /* Invalid message if the length is less than the header + * size or if its not a multiple of 4 */ + if (msg_length < MESSAGE_HEADER_LENGTH || (msg_length % 4) != 0) { + pcep_log(LOG_INFO, + "%s: Invalid PCEP message header length [%d]", + __func__, msg_length); + return false; + } + + if (msg_version != PCEP_MESSAGE_HEADER_VERSION) { + pcep_log( + LOG_INFO, + "%s: Invalid PCEP message header version [0x%x] expected version [0x%x]", + __func__, msg_version, PCEP_MESSAGE_HEADER_VERSION); + return false; + } + + if (msg_flags != 0) { + pcep_log(LOG_INFO, + "%s: Invalid PCEP message header flags [0x%x]", + __func__, msg_flags); + return false; + } + + switch (msg_type) { + /* Supported message types */ + case PCEP_TYPE_OPEN: + case PCEP_TYPE_KEEPALIVE: + case PCEP_TYPE_PCREQ: + case PCEP_TYPE_PCREP: + case PCEP_TYPE_PCNOTF: + case PCEP_TYPE_ERROR: + case PCEP_TYPE_CLOSE: + case PCEP_TYPE_REPORT: + case PCEP_TYPE_UPDATE: + case PCEP_TYPE_INITIATE: + break; + default: + pcep_log(LOG_INFO, "%s: Invalid PCEP message header type [%d]", + __func__, msg_type); + return false; + break; + } + + return true; +} + +/* Internal util function */ +static uint16_t pcep_decode_msg_header(const uint8_t *msg_buf, + uint8_t *msg_version, uint8_t *msg_flags, + uint8_t *msg_type) +{ + // Check RFC 5440 for version and flags position. + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + //| Ver | Flags | Message-Type | Message-Length | + //+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + *msg_version = (msg_buf[0] >> 5) & 0x07; + *msg_flags = (msg_buf[0] & 0x1f); + *msg_type = msg_buf[1]; + uint16_t host_order_length; + memcpy(&host_order_length, msg_buf + 2, sizeof(host_order_length)); + return ntohs(host_order_length); +} + +/* Decode the message header and return the message length */ +int32_t pcep_decode_validate_msg_header(const uint8_t *msg_buf) +{ + uint8_t msg_version; + uint8_t msg_flags; + uint8_t msg_type; + uint32_t msg_length; + + msg_length = pcep_decode_msg_header(msg_buf, &msg_version, &msg_flags, + &msg_type); + + return ((validate_msg_header(msg_version, msg_flags, msg_type, + msg_length) + == false) + ? -1 + : (int32_t)msg_length); +} + +bool validate_message_objects(struct pcep_message *msg) +{ + if (msg->msg_header->type >= PCEP_TYPE_START_TLS) { + pcep_log( + LOG_INFO, + "%s: Rejecting received message: Unknown message type [%d]", + __func__, msg->msg_header->type); + return false; + } + + const enum pcep_object_classes *object_classes = + MANDATORY_MESSAGE_OBJECT_CLASSES[msg->msg_header->type]; + double_linked_list_node *node; + int index; + for (node = (msg->obj_list == NULL ? NULL : msg->obj_list->head), + index = 0; + index < NUM_CHECKED_OBJECTS; + index++, (node = (node == NULL ? NULL : node->next_node))) { + struct pcep_object_header *obj = + ((node == NULL) + ? NULL + : (struct pcep_object_header *)node->data); + + if ((int)object_classes[index] == NO_OBJECT) { + if (node != NULL) { + pcep_log( + LOG_INFO, + "%s: Rejecting received message: Unexpected object [%d] present", + __func__, obj->object_class); + return false; + } + } else if (object_classes[index] != ANY_OBJECT) { + if (node == NULL) { + pcep_log( + LOG_INFO, + "%s: Rejecting received message: Expecting object in position [%d], but none received", + __func__, index); + return false; + } else if (object_classes[index] != obj->object_class) { + pcep_log( + LOG_INFO, + "%s: Rejecting received message: Unexpected Object Class received [%d]", + __func__, object_classes[index]); + return false; + } + } + } + + return true; +} + +struct pcep_message *pcep_decode_message(const uint8_t *msg_buf) +{ + uint8_t msg_version; + uint8_t msg_flags; + uint8_t msg_type; + uint16_t msg_length; + + msg_length = pcep_decode_msg_header(msg_buf, &msg_version, &msg_flags, + &msg_type); + if (msg_length == 0) { + pcep_log(LOG_INFO, "%s: Discarding empty message", __func__); + return NULL; + } + if (msg_length >= PCEP_MESSAGE_LENGTH) { + pcep_log(LOG_INFO, "%s: Discarding message too big", __func__); + return NULL; + } + + struct pcep_message *msg = + pceplib_calloc(PCEPLIB_MESSAGES, sizeof(struct pcep_message)); + + msg->msg_header = pceplib_malloc(PCEPLIB_MESSAGES, + sizeof(struct pcep_message_header)); + msg->msg_header->pcep_version = msg_version; + msg->msg_header->type = msg_type; + + msg->obj_list = dll_initialize(); + msg->encoded_message = pceplib_malloc(PCEPLIB_MESSAGES, msg_length); + memcpy(msg->encoded_message, msg_buf, msg_length); + msg->encoded_message_length = msg_length; + + uint16_t bytes_read = MESSAGE_HEADER_LENGTH; + while ((msg_length - bytes_read) >= OBJECT_HEADER_LENGTH) { + struct pcep_object_header *obj_hdr = + pcep_decode_object(msg_buf + bytes_read); + + if (obj_hdr == NULL) { + pcep_log(LOG_INFO, "%s: Discarding invalid message", + __func__); + pcep_msg_free_message(msg); + + return NULL; + } + + dll_append(msg->obj_list, obj_hdr); + bytes_read += obj_hdr->encoded_object_length; + } + + if (validate_message_objects(msg) == false) { + pcep_log(LOG_INFO, "%s: Discarding invalid message", __func__); + pcep_msg_free_message(msg); + + return NULL; + } + + return msg; +} + +struct pcep_versioning *create_default_pcep_versioning() +{ + struct pcep_versioning *versioning = + pceplib_malloc(PCEPLIB_INFRA, sizeof(struct pcep_versioning)); + memset(versioning, 0, sizeof(struct pcep_versioning)); + + return versioning; +} + +void destroy_pcep_versioning(struct pcep_versioning *versioning) +{ + pceplib_free(PCEPLIB_INFRA, versioning); +} |