/*
* This file is part of the PCEPlib, a PCEP protocol library.
*
* Copyright (C) 2020 Volta Networks https://voltanet.io/
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* Author : Brady Johnson
*
*/
/*
* Encoding and decoding for PCEP messages.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include
#include
#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);
}