2363 lines
90 KiB
C
2363 lines
90 KiB
C
/**
|
|
* @file
|
|
* @brief API for encoding/decoding of BACnet/SC BVLC messages
|
|
* @author Kirill Neznamov <kirill.neznamov@dsr-corporation.com>
|
|
* @date May 2022
|
|
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
|
*/
|
|
#include "bacnet/datalink/bsc/bvlc-sc.h"
|
|
|
|
typedef enum BACNet_Option_Validation_Type {
|
|
BACNET_USER_OPTION_VALIDATION,
|
|
BACNET_PDU_DEST_OPTION_VALIDATION,
|
|
BACNET_PDU_DATA_OPTION_VALIDATION
|
|
} BACNET_OPTION_VALIDATION_TYPE;
|
|
|
|
static const char *s_message_is_incompleted = "header options is truncated";
|
|
static const char *s_invalid_header_option_type =
|
|
"header option type must be 'Secure Path' or 'Proprietary Header'";
|
|
static const char *s_invalid_header_1 =
|
|
"'Secure Path' header option can be added only to data options in bvlc "
|
|
"message";
|
|
static const char *s_invalid_header_2 =
|
|
"'Secure Path' header option must not have header data";
|
|
static const char *s_invalid_header_3 =
|
|
"'Proprietary Header' option must have header data";
|
|
static const char *s_result_incomplete =
|
|
"BVLC-Result message has incomplete payload";
|
|
static const char *s_result_incorrect_bvlc_function =
|
|
"parameter 'Result For BVLC Function' is out of range";
|
|
static const char *s_result_incorrect_result_code =
|
|
"parameter 'Result Code' must be 0x00 (ACK) or 0x01(NAK)";
|
|
static const char *s_result_inconsistent =
|
|
"BVLC-Result message has data inconsistency in payload";
|
|
static const char *s_result_unexpected_data =
|
|
"BVLC-Result message is longer than expected";
|
|
static const char *s_advertisiment_incomplete =
|
|
"advertisiment message has incomplete payload";
|
|
static const char *s_advertisiment_unexpected =
|
|
"advertisiment message is longer than expected";
|
|
static const char *s_advertisiment_param1_error =
|
|
"parameter 'Hub Connection Status' in advertisiment message must be in "
|
|
"range [0, 2]";
|
|
static const char *s_advertisiment_param2_error =
|
|
"parameter 'Accept Direct Connections'in advertisiment message must be in "
|
|
"range [0, 1]";
|
|
static const char *s_connect_request_incomplete =
|
|
"connect-request message has incomplete payload";
|
|
static const char *s_connect_request_unexpected =
|
|
"connect-request message is longer than expected";
|
|
static const char *s_connect_accept_incomplete =
|
|
"connect-accept message has incomplete payload";
|
|
static const char *s_connect_accept_unexpected =
|
|
"connect-accept is longer than expected";
|
|
static const char *s_proprietary_incomplete =
|
|
"proprietary message has incomplete payload";
|
|
static const char *s_hdr_incomplete1 =
|
|
"message is incomplete, 'Originating Virtual Address' field is truncated";
|
|
static const char *s_hdr_incomplete2 =
|
|
"message is incomplete, 'Destination Virtual Address' field is truncated";
|
|
static const char *s_unknown_bvlc_function =
|
|
"unknown value of 'BVLC Function' field in message";
|
|
static const char *s_dest_options_list_too_long =
|
|
"message contains more than #BVLC_SC_HEADER_OPTION_MAX options in destion "
|
|
"options list";
|
|
static const char *s_data_options_list_too_long =
|
|
"message contains more than #BVLC_SC_HEADER_OPTION_MAX options in data "
|
|
"options list";
|
|
static const char *s_result_unexpected_data_options =
|
|
"BVLC-Result message must not have data options";
|
|
static const char *s_result_payload_expected =
|
|
"BVLC-Result message must have payload";
|
|
static const char *s_encapsulated_npdu_payload_expected =
|
|
"encapsulated-npdu message must have payload";
|
|
static const char *s_address_resolution_data_options =
|
|
"address-resolution message must not have data options";
|
|
static const char *s_address_resolution_unexpected =
|
|
"address-resolution message is longer than expected";
|
|
static const char *s_address_resolution_ack_data_options =
|
|
"address-resolutio-ack message must not have data options";
|
|
static const char *s_advertisiment_data_options =
|
|
"advertisiment message must not have data options";
|
|
static const char *s_advertisiment_payload_expected =
|
|
"advertisiment message must have payload";
|
|
static const char *s_advertisiment_solicitation_data_options =
|
|
"advertisiment solicitation message must not have data options";
|
|
static const char *s_advertisiment_solicitation_payload_expected =
|
|
"advertisiment solicitation message must have payload";
|
|
static const char *s_origin_unexpected =
|
|
"'Originating Virtual Address' field must be absent in message";
|
|
static const char *s_dest_unexpected =
|
|
"'Destination Virtual Address' field must be absent in message";
|
|
static const char *s_data_option_unexpected =
|
|
"message must not have data options";
|
|
static const char *s_message_too_long = "message is longer than expected";
|
|
static const char *s_absent_payload = "payload is absent in the message";
|
|
static const char *s_proprietary_data_options =
|
|
"proprietary message must not have data options";
|
|
static const char *s_proprietary_payload =
|
|
"proprietary message must have payload";
|
|
|
|
/**
|
|
* @brief Validate BVLC-SC header options
|
|
* @param validation_type - type of validation
|
|
* @param option_headers - pointer to the option headers
|
|
* @param option_headers_max_len - maximum length of the option headers
|
|
* @param out_option_headers_real_length - pointer to the real length of the
|
|
* option headers
|
|
* @param out_option_header_num - pointer to the number of the option headers
|
|
* @param error_code - pointer to the error code
|
|
* @param error_class - pointer to the error class
|
|
* @param error_desc_string - pointer to the error description string
|
|
* @return true if the option headers are valid, false otherwise
|
|
*/
|
|
static bool bvlc_sc_validate_options_headers(
|
|
BACNET_OPTION_VALIDATION_TYPE validation_type,
|
|
uint8_t *option_headers,
|
|
size_t option_headers_max_len,
|
|
size_t *out_option_headers_real_length,
|
|
size_t *out_option_header_num,
|
|
uint16_t *error_code,
|
|
uint16_t *error_class,
|
|
const char **error_desc_string)
|
|
{
|
|
int options_len = 0;
|
|
uint8_t flags = 0;
|
|
uint8_t option;
|
|
uint16_t hdr_len;
|
|
|
|
if (!option_headers_max_len || !out_option_headers_real_length) {
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*error_desc_string = s_message_is_incompleted;
|
|
return false;
|
|
}
|
|
|
|
if (out_option_header_num) {
|
|
*out_option_header_num = 0;
|
|
}
|
|
|
|
while (options_len < option_headers_max_len) {
|
|
flags = option_headers[options_len];
|
|
|
|
option = flags & BVLC_SC_HEADER_OPTION_TYPE_MASK;
|
|
|
|
if (option != BVLC_SC_OPTION_TYPE_SECURE_PATH &&
|
|
option != BVLC_SC_OPTION_TYPE_PROPRIETARY) {
|
|
*error_code = ERROR_CODE_HEADER_ENCODING_ERROR;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*error_desc_string = s_invalid_header_option_type;
|
|
return false;
|
|
}
|
|
|
|
if (option == BVLC_SC_OPTION_TYPE_SECURE_PATH) {
|
|
if (validation_type == BACNET_PDU_DEST_OPTION_VALIDATION) {
|
|
/* According BACNet standard secure path header option can be
|
|
added only to data options. (AB.2.3.1 Secure Path Header
|
|
Option) */
|
|
*error_code = ERROR_CODE_HEADER_ENCODING_ERROR;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*error_desc_string = s_invalid_header_1;
|
|
return false;
|
|
}
|
|
if (flags & BVLC_SC_HEADER_DATA) {
|
|
/* securepath option header does not have data header
|
|
according bacnet stadard */
|
|
*error_code = ERROR_CODE_HEADER_ENCODING_ERROR;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*error_desc_string = s_invalid_header_2;
|
|
return false;
|
|
}
|
|
options_len++;
|
|
} else if (option == BVLC_SC_OPTION_TYPE_PROPRIETARY) {
|
|
if (!(flags & BVLC_SC_HEADER_DATA)) {
|
|
/* proprietary option must have header data */
|
|
*error_code = ERROR_CODE_HEADER_ENCODING_ERROR;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*error_desc_string = s_invalid_header_3;
|
|
return false;
|
|
}
|
|
options_len++;
|
|
if (options_len + 2 > option_headers_max_len) {
|
|
/* not enough data to process header that means
|
|
that probably message is incomplete */
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*error_desc_string = s_message_is_incompleted;
|
|
return false;
|
|
}
|
|
memcpy(&hdr_len, &option_headers[options_len], 2);
|
|
options_len +=
|
|
2 /* header length */ + hdr_len /* length of data header */;
|
|
}
|
|
|
|
if (options_len > option_headers_max_len) {
|
|
/* not enough data to process header that means
|
|
that probably message is incomplete */
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*error_desc_string = s_message_is_incompleted;
|
|
return false;
|
|
}
|
|
|
|
if (out_option_header_num && (*out_option_header_num < USHRT_MAX)) {
|
|
*out_option_header_num += 1;
|
|
}
|
|
|
|
if (!(flags & BVLC_SC_HEADER_MORE)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
*out_option_headers_real_length = options_len;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Function adds header option to data option list
|
|
* into provided pdu and stores the result in out_pdu.
|
|
* Note that last added option will be the first option in
|
|
* the list.
|
|
* @param out_pdu - buffer where modified pdu will be stored in
|
|
* @param out_pdu_size - size of the out_pdu
|
|
* @param pdu - input pdu to which user wants to add option
|
|
* @param pdu_size - size of input pdu
|
|
* @param sc_option - pointer to encoded header option
|
|
* @param sc_option_len - length of encoded header option
|
|
* @return returns length (>0) in bytes of a pdu with added option
|
|
*/
|
|
static size_t bvlc_sc_add_option(
|
|
bool to_data_option,
|
|
uint8_t *pdu,
|
|
size_t pdu_size,
|
|
uint8_t *in_pdu,
|
|
size_t in_pdu_len,
|
|
uint8_t *sc_option,
|
|
size_t sc_option_len)
|
|
{
|
|
size_t offs = 4;
|
|
uint8_t flags = 0;
|
|
size_t options_len = 0;
|
|
uint8_t mask;
|
|
uint16_t error_code;
|
|
uint16_t error_class;
|
|
BACNET_OPTION_VALIDATION_TYPE vt;
|
|
const char *err_desc;
|
|
|
|
if (!in_pdu_len || !in_pdu || !sc_option_len || !pdu_size || !pdu ||
|
|
!sc_option) {
|
|
return 0;
|
|
}
|
|
|
|
if (pdu_size < 4 || in_pdu_len < 4) {
|
|
return 0;
|
|
}
|
|
|
|
if (sc_option_len + in_pdu_len > USHRT_MAX) {
|
|
return 0;
|
|
}
|
|
|
|
if (pdu_size < in_pdu_len) {
|
|
return 0;
|
|
}
|
|
|
|
if (pdu_size < sc_option_len + in_pdu_len) {
|
|
return 0;
|
|
}
|
|
|
|
/* ensure that sc_option does not have flag more options */
|
|
|
|
if (sc_option[0] & BVLC_SC_HEADER_MORE) {
|
|
return 0;
|
|
}
|
|
|
|
if (!to_data_option &&
|
|
(sc_option[0] & BVLC_SC_HEADER_OPTION_TYPE_MASK) ==
|
|
BVLC_SC_OPTION_TYPE_SECURE_PATH) {
|
|
/* According BACNet standard secure path header option can be added
|
|
only to data options. (AB.2.3.1 Secure Path Header Option) */
|
|
return 0;
|
|
}
|
|
|
|
/* ensure that user wants to add valid option */
|
|
if (!bvlc_sc_validate_options_headers(
|
|
BACNET_USER_OPTION_VALIDATION, sc_option, sc_option_len,
|
|
&options_len, NULL, &error_code, &error_class, &err_desc)) {
|
|
return 0;
|
|
}
|
|
|
|
flags = in_pdu[1];
|
|
|
|
if (flags & BVLC_SC_CONTROL_DEST_VADDR) {
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
}
|
|
|
|
if (flags & BVLC_SC_CONTROL_ORIG_VADDR) {
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
}
|
|
|
|
if (offs > in_pdu_len) {
|
|
return 0;
|
|
}
|
|
|
|
/* insert options */
|
|
|
|
if (to_data_option) {
|
|
mask = BVLC_SC_CONTROL_DATA_OPTIONS;
|
|
vt = BACNET_PDU_DATA_OPTION_VALIDATION;
|
|
|
|
if (flags & BVLC_SC_CONTROL_DEST_OPTIONS) {
|
|
/* some options are already presented in message.
|
|
Validate them at first. */
|
|
if (!bvlc_sc_validate_options_headers(
|
|
BACNET_PDU_DEST_OPTION_VALIDATION, &in_pdu[offs],
|
|
in_pdu_len - offs, &options_len, NULL, &error_code,
|
|
&error_class, &err_desc)) {
|
|
return 0;
|
|
}
|
|
offs += options_len;
|
|
}
|
|
} else {
|
|
mask = BVLC_SC_CONTROL_DEST_OPTIONS;
|
|
vt = BACNET_PDU_DEST_OPTION_VALIDATION;
|
|
}
|
|
|
|
if ((flags & mask)) {
|
|
/* some options are already presented in message.
|
|
Validate them at first. */
|
|
if (!bvlc_sc_validate_options_headers(
|
|
vt, &in_pdu[offs], in_pdu_len - offs, &options_len, NULL,
|
|
&error_code, &error_class, &err_desc)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (pdu == in_pdu) {
|
|
/* user would like to use the same buffer where in_pdu
|
|
is already stored. So we need to expand it for sc_option_len */
|
|
memmove(&pdu[offs + sc_option_len], &pdu[offs], in_pdu_len - offs);
|
|
memcpy(&pdu[offs], sc_option, sc_option_len);
|
|
} else {
|
|
/* user would like to use a new buffer for in_pdu with option.*/
|
|
memcpy(pdu, in_pdu, offs);
|
|
memcpy(&pdu[offs], sc_option, sc_option_len);
|
|
if ((flags & mask)) {
|
|
pdu[offs] |= BVLC_SC_HEADER_MORE;
|
|
}
|
|
memcpy(&pdu[offs + sc_option_len], &in_pdu[offs], in_pdu_len - offs);
|
|
}
|
|
if ((flags & mask)) {
|
|
pdu[offs] |= BVLC_SC_HEADER_MORE;
|
|
} else {
|
|
pdu[1] |= mask;
|
|
}
|
|
return in_pdu_len + sc_option_len;
|
|
}
|
|
|
|
/**
|
|
* @brief Function adds header option to destination option list
|
|
into provided pdu and stores the result in out_pdu.
|
|
Note that last added option will be the first option in
|
|
the list.
|
|
* @param out_pdu - buffer where modified pdu will be stored in
|
|
* @param out_pdu_size - size of the out_pdu
|
|
* @param pdu - input pdu to which user wants to add option
|
|
* @param pdu_size - size of input pdu
|
|
* @param sc_option - pointer to encoded header option
|
|
* @param sc_option_len - length of encoded header option
|
|
* @return returns length (>0) in bytes of a pdu with added option
|
|
or 0 in a case of error (validation of pdu or sc_option fails)
|
|
*/
|
|
size_t bvlc_sc_add_option_to_destination_options(
|
|
uint8_t *out_pdu,
|
|
size_t out_pdu_size,
|
|
uint8_t *pdu,
|
|
size_t pdu_size,
|
|
uint8_t *sc_option,
|
|
size_t sc_option_len)
|
|
{
|
|
return bvlc_sc_add_option(
|
|
false, out_pdu, out_pdu_size, pdu, pdu_size, sc_option, sc_option_len);
|
|
}
|
|
|
|
/**
|
|
* @brief Function adds header option to data option list
|
|
into provided pdu and stores the result in out_pdu.
|
|
Note that last added option will be the first option in
|
|
the list.
|
|
* @param out_pdu - buffer where modified pdu will be stored in
|
|
* @param out_pdu_size - size of the out_pdu
|
|
* @param pdu - input pdu to which user wants to add option
|
|
* @param pdu_size - size of input pdu
|
|
* @param sc_option - pointer to encoded header option
|
|
* @param sc_option_len - length of encoded header option
|
|
* @return returns length (>0) in bytes of a pdu with added option
|
|
or 0 in a case of error (validation of pdu or sc_option fails)
|
|
*/
|
|
size_t bvlc_sc_add_option_to_data_options(
|
|
uint8_t *out_pdu,
|
|
size_t out_pdu_size,
|
|
uint8_t *pdu,
|
|
size_t pdu_size,
|
|
uint8_t *sc_option,
|
|
size_t sc_option_len)
|
|
{
|
|
return bvlc_sc_add_option(
|
|
true, out_pdu, out_pdu_size, pdu, pdu_size, sc_option, sc_option_len);
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes proprietary header option in correspondence
|
|
* of BACNet standard AB.2.3.2 Proprietary Header Options.
|
|
* Any proprietary header option shall consist of the
|
|
* following fields:
|
|
*
|
|
* Header Marker 1-octet 'More Options' = 0 or 1,
|
|
* 'Must Understand' = 0 or 1,
|
|
* 'Header Data Flag' = 1,
|
|
* 'Header Option Type' = 31
|
|
* Header Length 2-octets Length of 'Header Data' field,
|
|
* in octets.
|
|
* Header Data 3-N octets Required to include:
|
|
* Vendor Identifier 2-octets Vendor Identifier, with most
|
|
* significant octet first, of the
|
|
* organization defining this option.
|
|
* Proprietary Option Type 1-octet An indication of the proprietary
|
|
* header option type.
|
|
* Proprietary Header data Variable A proprietary string of octets.
|
|
* Can be zero length.
|
|
*
|
|
* @param pdu - buffer to store encoded option
|
|
* @param pdu_size - size of pdu
|
|
* @param must_understand - value for 'Must Understand' flag.
|
|
* @param pdu_size - size of input pdu
|
|
* @param vendor_id - vendor identifier
|
|
* @param proprietary_option_type - proprietary header option type.
|
|
* @param proprietary_data - buffer with proprietary data
|
|
* @param proprietary_data_len - size of buffer with proprietary data
|
|
* @return value (>0) in bytes of a encoded options or 0 in a case of
|
|
* an error
|
|
*/
|
|
size_t bvlc_sc_encode_proprietary_option(
|
|
uint8_t *pdu,
|
|
size_t pdu_size,
|
|
bool must_understand,
|
|
uint16_t vendor_id,
|
|
uint8_t proprietary_option_type,
|
|
uint8_t *proprietary_data,
|
|
size_t proprietary_data_len)
|
|
{
|
|
size_t total_len =
|
|
sizeof(vendor_id) + proprietary_data_len + sizeof(uint8_t);
|
|
|
|
if (proprietary_data_len > BVLC_SC_NPDU_SIZE -
|
|
3 /* header marker len + header length len) */ -
|
|
sizeof(vendor_id)) {
|
|
return 0;
|
|
}
|
|
|
|
if (pdu_size < total_len + 3 /* header marker len + header data len) */) {
|
|
return 0;
|
|
}
|
|
|
|
/* reset More Options, Must Understand and Header Data flags
|
|
they will be updated as a result of call bvlc_sc_add_sc_option() */
|
|
|
|
pdu[0] = BVLC_SC_OPTION_TYPE_PROPRIETARY;
|
|
|
|
if (must_understand) {
|
|
pdu[0] |= BVLC_SC_HEADER_MUST_UNDERSTAND;
|
|
}
|
|
|
|
pdu[0] |= BVLC_SC_HEADER_DATA;
|
|
memcpy(&pdu[1], &total_len, sizeof(total_len));
|
|
memcpy(&pdu[3], &vendor_id, sizeof(vendor_id));
|
|
memcpy(&pdu[5], &proprietary_option_type, sizeof(proprietary_option_type));
|
|
memcpy(&pdu[6], proprietary_data, proprietary_data_len);
|
|
return total_len + 3;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes security path header option in correspondence
|
|
* of BACNet standard AB.2.3.1 Secure Path Header Option.
|
|
* Any proprietary header option shall consist of the
|
|
* following fields:
|
|
*
|
|
* Header Marker 1-octet 'Last Option' = 0 or 1,
|
|
* 'Must Understand' = 0 or 1,
|
|
* 'Header Data Flag' = 1,
|
|
* 'Header Option Type' = 31
|
|
*
|
|
* @param pdu - buffer to store encoded option
|
|
* @param pdu_size - size of pdu
|
|
* @param must_understand - value for 'Must Understand' flag.
|
|
* @return value (>0) in bytes of a encoded options or 0 in a case of
|
|
* an error
|
|
*/
|
|
size_t bvlc_sc_encode_secure_path_option(
|
|
uint8_t *pdu, size_t pdu_size, bool must_understand)
|
|
{
|
|
if (pdu_size < 1) {
|
|
return 0;
|
|
}
|
|
|
|
pdu[0] = BVLC_SC_OPTION_TYPE_SECURE_PATH;
|
|
|
|
if (must_understand) {
|
|
pdu[0] |= BVLC_SC_HEADER_MUST_UNDERSTAND;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Decodes BVLC header options marker. Function assumes to be called
|
|
* iteratively until end of option list will be reached. User must
|
|
* call this function on previously validated options list only
|
|
* because sanity checks are omitted.That valided option list can
|
|
* be get by some bvlc_sc_encode_ function calls.
|
|
*
|
|
* @param in_option_list - buffer containing list of header options.It must
|
|
point to head list item to be decoded.
|
|
* @param out_opt_type - pointer to store decoded option type, must not
|
|
be NULL
|
|
* @param out_must_understand - pointer to store decoded 'must understand'
|
|
* flag from options marker, must not be NULL.
|
|
* @param out_next_option - pointer to next option list item to be decoded,
|
|
* can be NULL if no any option items left.
|
|
*
|
|
* @return 0 in a case if decoding failed, otherwise returns
|
|
* length value of decoded option in bytes.
|
|
*
|
|
* Typically code for parsing of option list looks like following:
|
|
*
|
|
* uint8_t *current = in_options_list;
|
|
* int option_len = 0;
|
|
* while(current) {
|
|
* bvlc_sc_decode_option_hdr(current,
|
|
* &type, ...., ¤t);
|
|
* }
|
|
*
|
|
*/
|
|
|
|
static void bvlc_sc_decode_option_hdr(
|
|
uint8_t *in_options_list,
|
|
BVLC_SC_OPTION_TYPE *out_opt_type,
|
|
bool *out_must_understand,
|
|
uint8_t **out_next_option)
|
|
{
|
|
*out_next_option = NULL;
|
|
*out_opt_type = (BVLC_SC_OPTION_TYPE)(in_options_list[0] &
|
|
BVLC_SC_HEADER_OPTION_TYPE_MASK);
|
|
*out_must_understand =
|
|
(in_options_list[0] & BVLC_SC_HEADER_MUST_UNDERSTAND) ? true : false;
|
|
|
|
if (*out_opt_type == BVLC_SC_OPTION_TYPE_SECURE_PATH) {
|
|
if (in_options_list[0] & BVLC_SC_HEADER_MORE) {
|
|
*out_next_option = in_options_list + 1;
|
|
}
|
|
} else if (*out_opt_type == BVLC_SC_OPTION_TYPE_PROPRIETARY) {
|
|
uint16_t hdr_len;
|
|
memcpy(&hdr_len, &in_options_list[1], sizeof(hdr_len));
|
|
hdr_len += sizeof(hdr_len) + 1;
|
|
if (in_options_list[0] & BVLC_SC_HEADER_MORE) {
|
|
*out_next_option = in_options_list + hdr_len;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Decodes BVLC header proprietary option
|
|
* Function assumes to be called iteratively until end of option list
|
|
* will be reached. User must call this function on previously validated
|
|
* options list only because sanity checks are omitted. That valided
|
|
* option list can be get by some bvlc_sc_encode_ function calls.
|
|
* @param in_options_list - buffer containing list of header options.It must
|
|
* point to head list item to be decoded.
|
|
* @param out_vendor_id - pointer to store vendor id, must not be NULL
|
|
* @param out_proprietary_option_type - pointer to store proprietary option
|
|
* type, must not be NULL
|
|
* @param out_proprietary_data - pointer to store proprietary data, can be NULL
|
|
* @param out_proprietary_data_len - pointer to store length of proprietary
|
|
* data, can be NULL
|
|
* @return 0 in a case if decoding failed, otherwise return
|
|
*/
|
|
static void bvlc_sc_decode_proprietary_option(
|
|
uint8_t *in_options_list,
|
|
uint16_t *out_vendor_id,
|
|
uint8_t *out_proprietary_option_type,
|
|
uint8_t **out_proprietary_data,
|
|
size_t *out_proprietary_data_len)
|
|
{
|
|
uint16_t hdr_len;
|
|
|
|
*out_proprietary_data = NULL;
|
|
*out_proprietary_data_len = 0;
|
|
memcpy(&hdr_len, &in_options_list[1], sizeof(hdr_len));
|
|
memcpy(out_vendor_id, &in_options_list[3], sizeof(uint16_t));
|
|
*out_proprietary_option_type = in_options_list[5];
|
|
|
|
if (hdr_len > 3) {
|
|
*out_proprietary_data = &in_options_list[6];
|
|
*out_proprietary_data_len = hdr_len - 3;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief encode BVLC-SC header
|
|
*
|
|
* BACNet standard AB.2.2 BVLC-SC Header Format:
|
|
* BVLC Function 1-octet BVLC function code
|
|
* Control Flags 1-octet Control flags
|
|
* Message ID 2-octets Message identifier
|
|
* Originating Virtual Address 0 or 6-octets If absent, message is from
|
|
* connection peer node
|
|
* Destination Virtual Address 0 or 6-octets If absent, message is for
|
|
*
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param bvlc_function - BVLC function code, check BVLC_SC_MESSAGE_TYPE enum.
|
|
* @param message_id - The message identifier
|
|
* @param origin - Originating virtual address, can be NULL
|
|
* @param dest - Destination virtual address, can be NULL
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
static size_t bvlc_sc_encode_common(
|
|
uint8_t *pdu,
|
|
size_t pdu_len,
|
|
uint8_t bvlc_function,
|
|
uint16_t message_id,
|
|
BACNET_SC_VMAC_ADDRESS *origin,
|
|
BACNET_SC_VMAC_ADDRESS *dest)
|
|
{
|
|
size_t offs = 4;
|
|
if (pdu_len < 4) {
|
|
return 0;
|
|
}
|
|
pdu[0] = bvlc_function;
|
|
pdu[1] = 0;
|
|
memcpy(&pdu[2], &message_id, sizeof(message_id));
|
|
|
|
if (origin) {
|
|
if (pdu_len < offs + BVLC_SC_VMAC_SIZE) {
|
|
return 0;
|
|
}
|
|
pdu[1] |= BVLC_SC_CONTROL_ORIG_VADDR;
|
|
memcpy(&pdu[offs], &origin->address[0], BVLC_SC_VMAC_SIZE);
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
}
|
|
|
|
if (dest) {
|
|
if (pdu_len < offs + BVLC_SC_VMAC_SIZE) {
|
|
return 0;
|
|
}
|
|
pdu[1] |= BVLC_SC_CONTROL_DEST_VADDR;
|
|
memcpy(&pdu[offs], &dest->address[0], BVLC_SC_VMAC_SIZE);
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
}
|
|
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the BVLC-Result message according BACNet standard
|
|
* AB.2.4.1 BVLC-Result Format.
|
|
*
|
|
* BVLC Function 1-octet (X'00') BVLC-Result
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 or 6-octets If absent, message is from
|
|
* connection peer node
|
|
* Destination Virtual Address 0 or 6-octets If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
* Payload
|
|
* Result For BVLC Function 1-octet BVLC function for which this
|
|
* is a result
|
|
* Result Code 1-octet(X'00') ACK: Successful completion. The
|
|
* 'Error Header Marker' and all
|
|
* subsequent parameters shall be
|
|
* absent.
|
|
* (X'01') NAK: The BVLC function failed.
|
|
* The 'Error Header Marker',
|
|
* the 'Error Class', the 'Error
|
|
* Code', and the 'Error Details'
|
|
* shall be present.
|
|
* Error Header Marker 1-octet The header marker of the
|
|
* (Conditional) destination option that caused
|
|
* the BVLC function to fail. If
|
|
* the NAK is unrelated to a header
|
|
* option, this parameter shall be
|
|
* X'00'.
|
|
* Error Class 2-octets The 'Error Class' field of the
|
|
* (Conditional) 'Error' datatype defined in
|
|
* Clause 21.
|
|
* Error Code 2-octets The 'Error Code' field of the
|
|
* (Conditional) 'Error' datatype defined in
|
|
* Clause 21.
|
|
* Error Details Variable UTF-8 reason text. Can be an
|
|
* (Conditional) empty string using no octets.
|
|
* Note that this string is not
|
|
* encoded as defined in Clause
|
|
* 20.2.9, has no character set
|
|
* indication octet, and no trailing
|
|
* zero octets. See BVLC-Result
|
|
* examples in Clause AB.2.17.
|
|
*
|
|
*
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @param origin - Originating virtual address, can be NULL
|
|
* @param dest - Destination virtual address, can be NULL
|
|
* @param bvlc_function - BVLC function for which this is a result.
|
|
* check BVLC_SC_MESSAGE_TYPE enum.
|
|
* @param result_code - 0 (ACK) or 1 (NAK)
|
|
* @param error_header_marker -can be NULL depending on result code.
|
|
* The header marker of the destination option
|
|
* that caused the BVLC function to fail
|
|
* @param error_class - can be NULL depending on result code.
|
|
* Check BACNET_ERROR_CLASS enum.
|
|
* @param error_code - can be NULL depending on result code.
|
|
* check BACNET_ERROR_CODE enum.
|
|
* @param utf8_details_string - can be NULL depending on result code.
|
|
* UTF-8 reason text.
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_result(
|
|
uint8_t *pdu,
|
|
size_t pdu_len,
|
|
uint16_t message_id,
|
|
BACNET_SC_VMAC_ADDRESS *origin,
|
|
BACNET_SC_VMAC_ADDRESS *dest,
|
|
uint8_t bvlc_function,
|
|
uint8_t result_code,
|
|
uint8_t *error_header_marker,
|
|
uint16_t *error_class,
|
|
uint16_t *error_code,
|
|
const char *utf8_details_string)
|
|
{
|
|
size_t offs;
|
|
|
|
if (bvlc_function > (uint16_t)BVLC_SC_PROPRIETARY_MESSAGE) {
|
|
/* unsupported bvlc function */
|
|
return 0;
|
|
}
|
|
|
|
if (result_code != 0 && result_code != 1) {
|
|
/* According AB.2.4.1 BVLC-Result Format result code
|
|
must be 0x00 or 0x01 */
|
|
return 0;
|
|
}
|
|
|
|
if (result_code) {
|
|
if (!error_class || !error_code) {
|
|
/* According AB.2.4.1 BVLC-Result Format error_class
|
|
and error_code must be presented */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_RESULT, message_id, origin, dest);
|
|
|
|
if (!offs) {
|
|
return 0;
|
|
}
|
|
|
|
if (pdu_len < offs + 2) {
|
|
return 0;
|
|
}
|
|
|
|
pdu[offs++] = bvlc_function;
|
|
pdu[offs++] = result_code;
|
|
|
|
if (!result_code) {
|
|
if (error_header_marker || error_class || error_code ||
|
|
utf8_details_string) {
|
|
return 0;
|
|
}
|
|
return offs;
|
|
}
|
|
|
|
if (pdu_len < offs + 4) {
|
|
return 0;
|
|
}
|
|
|
|
if (error_header_marker) {
|
|
pdu[offs++] = *error_header_marker;
|
|
} else {
|
|
pdu[offs++] = 0;
|
|
}
|
|
|
|
memcpy(&pdu[offs], error_class, sizeof(*error_class));
|
|
offs += sizeof(*error_class);
|
|
memcpy(&pdu[offs], error_code, sizeof(*error_code));
|
|
offs += sizeof(*error_code);
|
|
|
|
if (utf8_details_string) {
|
|
if (pdu_len < offs + strlen((const char *)utf8_details_string)) {
|
|
return 0;
|
|
}
|
|
memcpy(
|
|
&pdu[offs], utf8_details_string,
|
|
strlen((const char *)utf8_details_string));
|
|
offs += strlen((const char *)utf8_details_string);
|
|
}
|
|
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function decodes the BVLC-Result message according BACNet standard
|
|
* AB.2.4.1 BVLC-Result Format.
|
|
* @param payload - pointer to the decoded data
|
|
* @param packed_payload - pointer to the packed data
|
|
* @param packed_payload_len - size of the packed data
|
|
* @param error_code - pointer to the error code
|
|
* @param error_class - pointer to the error class
|
|
* @param err_desc - pointer to the error description string
|
|
* @return true if the data is decoded successfully, false otherwise
|
|
*/
|
|
static bool bvlc_sc_decode_result(
|
|
BVLC_SC_DECODED_DATA *payload,
|
|
uint8_t *packed_payload,
|
|
size_t packed_payload_len,
|
|
uint16_t *error_code,
|
|
uint16_t *error_class,
|
|
const char **err_desc)
|
|
{
|
|
size_t i;
|
|
|
|
if (packed_payload_len < 2) {
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_result_incomplete;
|
|
return false;
|
|
}
|
|
|
|
memset(&payload->result, 0, sizeof(payload->result));
|
|
|
|
if (packed_payload[0] > (uint8_t)BVLC_SC_PROPRIETARY_MESSAGE) {
|
|
/* unknown BVLC function */
|
|
*error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_result_incorrect_bvlc_function;
|
|
return false;
|
|
}
|
|
|
|
payload->result.bvlc_function = packed_payload[0];
|
|
if (packed_payload[1] != 0 && packed_payload[1] != 1) {
|
|
/* result code must be 1 or 0 */
|
|
*error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_result_incorrect_result_code;
|
|
return false;
|
|
}
|
|
|
|
payload->result.result = packed_payload[1];
|
|
|
|
if (packed_payload[1] == 1) {
|
|
if (packed_payload_len < 7) {
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_result_incomplete;
|
|
return false;
|
|
}
|
|
|
|
payload->result.error_header_marker = packed_payload[2];
|
|
memcpy(
|
|
&payload->result.error_class, &packed_payload[3],
|
|
sizeof(payload->result.error_class));
|
|
memcpy(
|
|
&payload->result.error_code, &packed_payload[5],
|
|
sizeof(payload->result.error_code));
|
|
|
|
if (packed_payload_len > 7) {
|
|
payload->result.utf8_details_string = &packed_payload[7];
|
|
payload->result.utf8_details_string_len = packed_payload_len - 7;
|
|
for (i = 0; i < packed_payload_len - 7; i++) {
|
|
if (payload->result.utf8_details_string[i] == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (i != packed_payload_len - 7) {
|
|
*error_code = ERROR_CODE_INCONSISTENT_PARAMETERS;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_result_inconsistent;
|
|
return false;
|
|
}
|
|
}
|
|
} else if (packed_payload_len > 2) {
|
|
/* According EA-001-4 'Clarifying BVLC-Result in BACnet/SC */
|
|
/* If a BVLC message is received that is longer than expected, */
|
|
/* a BVLC-Result NAK shall be returned if it was a unicast message, */
|
|
/* indicating an 'Error Class' of COMMUNICATION and 'Error Code' of */
|
|
/* UNEXPECTED_DATA. The message shall be discarded and not be processed.
|
|
*/
|
|
*error_code = ERROR_CODE_UNEXPECTED_DATA;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_result_unexpected_data;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Encapsulated-NPDU message according
|
|
* BACNet standard AB.2.5 Encapsulated-NPDU.
|
|
*
|
|
* BVLC Function 1-octet (X'01') Encapsulated-NPDU
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 or 6-octets If absent, message is from
|
|
* connection peer node
|
|
* Destination Virtual Address 0 or 6-octets If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Optional, 0 to N header options
|
|
* Payload
|
|
* BACnet NPDU Variable
|
|
*
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @param origin - Originating virtual address, can be NULL
|
|
* @param dest - Destination virtual address, can be NULL
|
|
* @param npdu - Buffer which contains BACnet NPDU
|
|
* @param npdu_size - Size of buffer which contains BACnet NPDU
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_encapsulated_npdu(
|
|
uint8_t *pdu,
|
|
size_t pdu_len,
|
|
uint16_t message_id,
|
|
BACNET_SC_VMAC_ADDRESS *origin,
|
|
BACNET_SC_VMAC_ADDRESS *dest,
|
|
uint8_t *npdu,
|
|
size_t npdu_size)
|
|
{
|
|
size_t offs;
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_ENCAPSULATED_NPDU, message_id, origin, dest);
|
|
if (!offs) {
|
|
return 0;
|
|
}
|
|
|
|
if (pdu_len < offs + npdu_size) {
|
|
return 0;
|
|
}
|
|
|
|
memcpy(&pdu[offs], npdu, npdu_size);
|
|
offs += npdu_size;
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Address-Resolution message according BACNet
|
|
* standard AB.2.6 Address-Resolution
|
|
*
|
|
* BVLC Function 1-octet (X'02') Address-Resolution
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 or 6-octets If absent, message is from
|
|
* connection peer node
|
|
* Destination Virtual Address 0 or 6-octets If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent
|
|
*
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @param origin - Originating virtual address, can be NULL
|
|
* @param dest - Destination virtual address, can be NULL
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_address_resolution(
|
|
uint8_t *pdu,
|
|
size_t pdu_len,
|
|
uint16_t message_id,
|
|
BACNET_SC_VMAC_ADDRESS *origin,
|
|
BACNET_SC_VMAC_ADDRESS *dest)
|
|
{
|
|
size_t offs;
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_ADDRESS_RESOLUTION, message_id, origin, dest);
|
|
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Address-Resolution-ACK message according
|
|
* BACNet standard AB.2.7.1 Address-Resolution-ACK Format.
|
|
*
|
|
* BVLC Function 1-octet (X'03') Address-Resolution-ACK
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 or 6-octets If absent, message is from
|
|
* connection peer node
|
|
* Destination Virtual Address 0 or 6-octets If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
* Payload
|
|
* WebSocket-URIs Variable UTF-8 string containing a list
|
|
* of WebSocket URIs as of RFC3986,
|
|
* separated by a single space
|
|
* character (X'20'), where the
|
|
* source BACnet/SC node accepts
|
|
* direct connections. Can be an
|
|
* empty string using zero octets.
|
|
* See Clause AB.3.3.
|
|
*
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @param origin - Originating virtual address, can be NULL
|
|
* @param dest - Destination virtual address, can be NULL
|
|
* @param web_socket_uris - UTF-8 string containing a list of
|
|
* WebSocket URIs as of RFC 3986, separated by a
|
|
* single space character (X'20'), where the source
|
|
* BACnet/SC node accepts direct connections.
|
|
* Can be an empty string using zero octets.
|
|
* @param web_socket_uris_len- length of web_socket_uris without trailing zero.
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_address_resolution_ack(
|
|
uint8_t *pdu,
|
|
size_t pdu_len,
|
|
uint16_t message_id,
|
|
BACNET_SC_VMAC_ADDRESS *origin,
|
|
BACNET_SC_VMAC_ADDRESS *dest,
|
|
uint8_t *web_socket_uris,
|
|
size_t web_socket_uris_len)
|
|
{
|
|
size_t offs;
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_ADDRESS_RESOLUTION_ACK, message_id, origin, dest);
|
|
if (!offs) {
|
|
return 0;
|
|
}
|
|
|
|
if (web_socket_uris && web_socket_uris_len) {
|
|
memcpy(&pdu[offs], web_socket_uris, web_socket_uris_len);
|
|
offs += web_socket_uris_len;
|
|
}
|
|
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Advertisement message according
|
|
* BACNet standard AB.2.8.1 Advertisement Format.
|
|
*
|
|
* BVLC Function 1-octet (X'04') Advertisement
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 or 6-octets If absent, message is from
|
|
* connection peer node
|
|
* Destination Virtual Address 0 or 6-octets If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
*
|
|
* Payload
|
|
* Hub Connection Status 1-octet X'00' No hub connection.
|
|
* X'01' Connected to primary hub.
|
|
* X'02' Connected to failover hub.
|
|
* Accept Direct Connections 1-octet X'00' The node does not support
|
|
* accepting direct
|
|
* connections.
|
|
* X'01' The node supports accepting
|
|
* direct connections.
|
|
* Maximum BVLC Length 2-octet The maximum BVLC message size
|
|
* that can be received and
|
|
* processed by the node, in number
|
|
* of octets.
|
|
* Maximum NPDU Length 2-octet The maximum NPDU message size
|
|
* that can be handled by the node's
|
|
* network entity, in number of
|
|
* octets.
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @param origin - Originating virtual address, can be NULL
|
|
* @param dest - Destination virtual address, can be NULL
|
|
* @param hub_status - hub connection status
|
|
* @param support- accept direct connections
|
|
* @param max_bvlc_len - the maximum BVLC message size
|
|
* @param max_npdu_size - the maximum NPDU message size
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_advertisiment(
|
|
uint8_t *pdu,
|
|
size_t pdu_len,
|
|
uint16_t message_id,
|
|
BACNET_SC_VMAC_ADDRESS *origin,
|
|
BACNET_SC_VMAC_ADDRESS *dest,
|
|
BACNET_SC_HUB_CONNECTOR_STATE hub_status,
|
|
BVLC_SC_DIRECT_CONNECTION_SUPPORT support,
|
|
uint16_t max_bvlc_len,
|
|
uint16_t max_npdu_size)
|
|
{
|
|
size_t offs;
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_ADVERTISIMENT, message_id, origin, dest);
|
|
if (!offs) {
|
|
return 0;
|
|
}
|
|
|
|
pdu[offs++] = (uint8_t)hub_status;
|
|
pdu[offs++] = (uint8_t)support;
|
|
memcpy(&pdu[offs], &max_bvlc_len, sizeof(max_bvlc_len));
|
|
offs += sizeof(max_bvlc_len);
|
|
memcpy(&pdu[offs], &max_npdu_size, sizeof(max_npdu_size));
|
|
offs += sizeof(max_npdu_size);
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Advertisement-Solicitation message according
|
|
* BACNet standard AB.2.9.1 Advertisement-Solicitation Format.
|
|
* @param payload - pointer to the decoded data
|
|
* @param packed_payload - pointer to packed data
|
|
* @param packed_payload_len - size of packed data
|
|
* @param error_code - pointer to the error code
|
|
* @param error_class - pointer to the error class
|
|
* @param err_desc - pointer to the error description string
|
|
* @return true if the data is decoded successfully, false otherwise
|
|
*/
|
|
static bool bvlc_sc_decode_advertisiment(
|
|
BVLC_SC_DECODED_DATA *payload,
|
|
uint8_t *packed_payload,
|
|
size_t packed_payload_len,
|
|
uint16_t *error_code,
|
|
uint16_t *error_class,
|
|
const char **err_desc)
|
|
{
|
|
if (packed_payload_len < 6) {
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_advertisiment_incomplete;
|
|
return false;
|
|
}
|
|
if (packed_payload_len > 6) {
|
|
*error_code = ERROR_CODE_UNEXPECTED_DATA;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_advertisiment_unexpected;
|
|
return false;
|
|
}
|
|
if (packed_payload[0] > BACNET_SC_HUB_CONNECTOR_STATE_MAX) {
|
|
*error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_advertisiment_param1_error;
|
|
return false;
|
|
}
|
|
|
|
if (packed_payload[1] > BVLC_SC_DIRECT_CONNECTION_SUPPORT_MAX) {
|
|
*error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_advertisiment_param2_error;
|
|
return false;
|
|
}
|
|
|
|
payload->advertisiment.hub_status = packed_payload[0];
|
|
payload->advertisiment.support = packed_payload[1];
|
|
memcpy(
|
|
&payload->advertisiment.max_bvlc_len, &packed_payload[2],
|
|
sizeof(payload->advertisiment.max_bvlc_len));
|
|
memcpy(
|
|
&payload->advertisiment.max_npdu_len, &packed_payload[4],
|
|
sizeof(payload->advertisiment.max_npdu_len));
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Advertisement-Solicitation message according
|
|
* BACNet standard AB.2.9.1 Advertisement-Solicitation Format.
|
|
*
|
|
* BVLC Function 1-octet (X'05') Advertisement
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 or 6-octets If absent, message is from
|
|
* connection peer node
|
|
* Destination Virtual Address 0 or 6-octets If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
*
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @param origin - Originating virtual address, can be NULL
|
|
* @param dest - Destination virtual address, can be NULL
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_advertisiment_solicitation(
|
|
uint8_t *pdu,
|
|
size_t pdu_len,
|
|
uint16_t message_id,
|
|
BACNET_SC_VMAC_ADDRESS *origin,
|
|
BACNET_SC_VMAC_ADDRESS *dest)
|
|
{
|
|
size_t offs;
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_ADVERTISIMENT_SOLICITATION, message_id, origin,
|
|
dest);
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Connect-Request message according
|
|
* BACNet standard AB.2.10.1 Connect-Request Format.
|
|
*
|
|
* BVLC Function 1-octet (X'06') Advertisement
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 Absent, is always for
|
|
* connection peer node.
|
|
* Destination Virtual Address 0 If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
*
|
|
* Payload
|
|
* VMAC Address 6-octets The VMAC address of the
|
|
* requesting node.
|
|
* Device UUID 16-octet The device UUID of the
|
|
* requesting node.
|
|
* Maximum BVLC Length 2-octet The maximum BVLC message size
|
|
* that can be received and
|
|
* processed by the node, in number
|
|
* of octets.
|
|
* Maximum NPDU Length 2-octet The maximum NPDU message size
|
|
* that can be handled by the node's
|
|
* network entity, in number of
|
|
* octets.
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @param local_vmac - VMAC address
|
|
* @param local_uuid - The device UUID
|
|
* @param max_bvlc_len - the maximum BVLC message size
|
|
* @param max_npdu_size - the maximum NPDU message size
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_connect_request(
|
|
uint8_t *pdu,
|
|
size_t pdu_len,
|
|
uint16_t message_id,
|
|
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
|
BACNET_SC_UUID *local_uuid,
|
|
uint16_t max_bvlc_len,
|
|
uint16_t max_npdu_size)
|
|
{
|
|
size_t offs;
|
|
|
|
if (!local_vmac || !local_uuid) {
|
|
return 0;
|
|
}
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_CONNECT_REQUEST, message_id, NULL, NULL);
|
|
if (!offs) {
|
|
return 0;
|
|
}
|
|
|
|
if (pdu_len <
|
|
(offs + BVLC_SC_VMAC_SIZE + BVLC_SC_UUID_SIZE + 2 * sizeof(uint16_t))) {
|
|
return 0;
|
|
}
|
|
|
|
memcpy(&pdu[offs], local_vmac, BVLC_SC_VMAC_SIZE);
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
memcpy(&pdu[offs], local_uuid, BVLC_SC_UUID_SIZE);
|
|
offs += BVLC_SC_UUID_SIZE;
|
|
memcpy(&pdu[offs], &max_bvlc_len, sizeof(max_bvlc_len));
|
|
offs += sizeof(max_bvlc_len);
|
|
memcpy(&pdu[offs], &max_npdu_size, sizeof(max_npdu_size));
|
|
offs += sizeof(max_npdu_size);
|
|
return (unsigned int)offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function decodes the Connect-Request message according
|
|
* BACNet standard AB.2.10.1 Connect-Request Format.
|
|
* @param payload - pointer to the decoded data
|
|
* @param packed_payload - pointer to the packed data
|
|
* @param packed_payload_len - size of the packed data
|
|
* @param error_code - pointer to the error code
|
|
* @param error_class - pointer to the error class
|
|
* @param err_desc - pointer to the error description string
|
|
* @return true if the data is decoded successfully, false otherwise
|
|
*/
|
|
static bool bvlc_sc_decode_connect_request(
|
|
BVLC_SC_DECODED_DATA *payload,
|
|
uint8_t *packed_payload,
|
|
size_t packed_payload_len,
|
|
uint16_t *error_code,
|
|
uint16_t *error_class,
|
|
const char **err_desc)
|
|
{
|
|
if (packed_payload_len < 26) {
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_connect_request_incomplete;
|
|
return false;
|
|
} else if (packed_payload_len > 26) {
|
|
*error_code = ERROR_CODE_UNEXPECTED_DATA;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_connect_request_unexpected;
|
|
return false;
|
|
}
|
|
payload->connect_request.vmac = (BACNET_SC_VMAC_ADDRESS *)packed_payload;
|
|
packed_payload += BVLC_SC_VMAC_SIZE;
|
|
payload->connect_request.uuid = (BACNET_SC_UUID *)packed_payload;
|
|
packed_payload += BVLC_SC_UUID_SIZE;
|
|
memcpy(
|
|
&payload->connect_request.max_bvlc_len, packed_payload,
|
|
sizeof(payload->connect_request.max_bvlc_len));
|
|
packed_payload += sizeof(payload->connect_request.max_bvlc_len);
|
|
memcpy(
|
|
&payload->connect_request.max_npdu_len, packed_payload,
|
|
sizeof(payload->connect_request.max_npdu_len));
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Connect-Accept message according
|
|
* BACNet standard AB.2.11.1 Connect-Accept Format.
|
|
*
|
|
* BVLC Function 1-octet (X'07') Advertisement
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 Absent, is always for
|
|
* connection peer node.
|
|
* Destination Virtual Address 0 If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
*
|
|
* Payload
|
|
* VMAC Address 6-octets The VMAC address of the
|
|
* requesting node.
|
|
* Device UUID 16-octet The device UUID of the
|
|
* requesting node.
|
|
* Maximum BVLC Length 2-octet The maximum BVLC message size
|
|
* that can be received and
|
|
* processed by the node, in number
|
|
* of octets.
|
|
* Maximum NPDU Length 2-octet The maximum NPDU message size
|
|
* that can be handled by the node's
|
|
* network entity, in number of
|
|
* octets.
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @param local_vmac - VMAC address
|
|
* @param local_uuid - The device UUID
|
|
* @param max_bvlc_len - the maximum BVLC message size
|
|
* @param max_npdu_size - the maximum NPDU message size
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_connect_accept(
|
|
uint8_t *pdu,
|
|
size_t pdu_len,
|
|
uint16_t message_id,
|
|
BACNET_SC_VMAC_ADDRESS *local_vmac,
|
|
BACNET_SC_UUID *local_uuid,
|
|
uint16_t max_bvlc_len,
|
|
uint16_t max_npdu_len)
|
|
{
|
|
size_t offs;
|
|
|
|
if (!local_vmac || !local_uuid) {
|
|
return 0;
|
|
}
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_CONNECT_ACCEPT, message_id, NULL, NULL);
|
|
if (!offs) {
|
|
return 0;
|
|
}
|
|
|
|
if (pdu_len <
|
|
(offs + BVLC_SC_VMAC_SIZE + BVLC_SC_UUID_SIZE + 2 * sizeof(uint16_t))) {
|
|
return 0;
|
|
}
|
|
|
|
memcpy(&pdu[offs], local_vmac, BVLC_SC_VMAC_SIZE);
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
memcpy(&pdu[offs], local_uuid, BVLC_SC_UUID_SIZE);
|
|
offs += BVLC_SC_UUID_SIZE;
|
|
memcpy(&pdu[offs], &max_bvlc_len, sizeof(max_bvlc_len));
|
|
offs += sizeof(max_bvlc_len);
|
|
memcpy(&pdu[offs], &max_npdu_len, sizeof(max_npdu_len));
|
|
offs += sizeof(max_npdu_len);
|
|
return (unsigned int)offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function decodes the Connect-Accept message according
|
|
* BACNet standard AB.2.11.1 Connect-Accept Format.
|
|
* @param payload - pointer to the decoded data
|
|
* @param packed_payload - pointer to the packed data
|
|
* @param packed_payload_len - size of the packed data
|
|
* @param error_code - pointer to the error code
|
|
* @param error_class - pointer to the error class
|
|
* @param err_desc - pointer to the error description string
|
|
* @return true if the data is decoded successfully, false otherwise
|
|
*/
|
|
static bool bvlc_sc_decode_connect_accept(
|
|
BVLC_SC_DECODED_DATA *payload,
|
|
uint8_t *packed_payload,
|
|
size_t packed_payload_len,
|
|
uint16_t *error_code,
|
|
uint16_t *error_class,
|
|
const char **err_desc)
|
|
{
|
|
if (packed_payload_len < 26) {
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_connect_accept_incomplete;
|
|
return false;
|
|
} else if (packed_payload_len > 26) {
|
|
*error_code = ERROR_CODE_UNEXPECTED_DATA;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_connect_accept_unexpected;
|
|
return false;
|
|
}
|
|
|
|
payload->connect_accept.vmac = (BACNET_SC_VMAC_ADDRESS *)packed_payload;
|
|
packed_payload += BVLC_SC_VMAC_SIZE;
|
|
payload->connect_accept.uuid = (BACNET_SC_UUID *)packed_payload;
|
|
packed_payload += BVLC_SC_UUID_SIZE;
|
|
memcpy(
|
|
&payload->connect_accept.max_bvlc_len, packed_payload,
|
|
sizeof(payload->connect_accept.max_bvlc_len));
|
|
packed_payload += sizeof(payload->connect_accept.max_bvlc_len);
|
|
memcpy(
|
|
&payload->connect_accept.max_npdu_len, packed_payload,
|
|
sizeof(payload->connect_accept.max_npdu_len));
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Disconnect-Request message according
|
|
* BACNet standard AB.2.12.1 Disconnect-Request Format.
|
|
*
|
|
* BVLC Function 1-octet (X'08') Advertisement
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 Absent, is always for
|
|
* connection peer node.
|
|
* Destination Virtual Address 0 If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
*
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_disconnect_request(
|
|
uint8_t *pdu, size_t pdu_len, uint16_t message_id)
|
|
{
|
|
size_t offs;
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_DISCONNECT_REQUEST, message_id, NULL, NULL);
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Disconnect-ACK message according
|
|
* BACNet standard AB.2.13.1 Disconnect-ACK Format.
|
|
*
|
|
* BVLC Function 1-octet (X'09') Advertisement
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 Absent, is always for
|
|
* connection peer node.
|
|
* Destination Virtual Address 0 If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
*
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t
|
|
bvlc_sc_encode_disconnect_ack(uint8_t *pdu, size_t pdu_len, uint16_t message_id)
|
|
{
|
|
size_t offs;
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_DISCONNECT_ACK, message_id, NULL, NULL);
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Heartbeat-Request message according
|
|
* BACNet standard AB.2.14.1 Heartbeat-Request Format.
|
|
*
|
|
* BVLC Function 1-octet (X'0A') Advertisement
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 Absent, is always for
|
|
* connection peer node.
|
|
* Destination Virtual Address 0 If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
*
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_heartbeat_request(
|
|
uint8_t *pdu, size_t pdu_len, uint16_t message_id)
|
|
{
|
|
size_t offs;
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_HEARTBEAT_REQUEST, message_id, NULL, NULL);
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Heartbeat-ACK message according
|
|
* BACNet standard AB.2.15.1 Heartbeat-ACK Format.
|
|
*
|
|
* BVLC Function 1-octet (X'0B') Advertisement
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 Absent, is always for
|
|
* connection peer node.
|
|
* Destination Virtual Address 0 If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
*
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t
|
|
bvlc_sc_encode_heartbeat_ack(uint8_t *pdu, size_t pdu_len, uint16_t message_id)
|
|
{
|
|
size_t offs;
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_HEARTBEAT_ACK, message_id, NULL, NULL);
|
|
return offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function encodes the Proprietary Message according BACNet standard
|
|
* AB.2.16.1 Proprietary Message Format.
|
|
*
|
|
* BVLC Function 1-octet (X'0C') Proprietary-Message.
|
|
* Control Flags 1-octet Control flags.
|
|
* Message ID 2-octets The message identifier of the
|
|
* message for which this message
|
|
* is the result.
|
|
* Originating Virtual Address 0 or 6-octets If absent, message is from
|
|
* connection peer node
|
|
* Destination Virtual Address 0 or 6-octets If absent, message is for
|
|
* connection peer node
|
|
* Destination Options Variable Optional, 0 to N header options
|
|
* Data Options 0-octets Shall be absent.
|
|
* Payload 3-N octets The payload shall consist of at
|
|
* least the vendor identifier and
|
|
* the proprietary function octet.
|
|
* Vendor identifier 2-octets Vendor Identifier, with most
|
|
* significant octet first, of the
|
|
* organization defining this
|
|
* message.
|
|
* Proprietary Function 1-octet The vendor-defined function code.
|
|
*
|
|
* Proprietary Data Variable Optional vendor-defined payload
|
|
* data.
|
|
* @param pdu - A buffer to store the encoded pdu.
|
|
* @param pdu_len - Size of the buffer to store the encoded pdu.
|
|
* @param message_id- The message identifier
|
|
* @param origin - Originating virtual address, can be NULL
|
|
* @param dest - Destination virtual address, can be NULL
|
|
* @param vendor_id - vendor identifier.
|
|
* @param proprietary_function - The vendor-defined function code
|
|
* @param proprietary_data - buffer which holds optional vendor-defined
|
|
* payload data.
|
|
* @param proprietary_data_len - length of proprietary_data buffer.
|
|
* @return number of bytes encoded, in a case of error returns 0.
|
|
*/
|
|
size_t bvlc_sc_encode_proprietary_message(
|
|
uint8_t *pdu,
|
|
size_t pdu_len,
|
|
uint16_t message_id,
|
|
BACNET_SC_VMAC_ADDRESS *origin,
|
|
BACNET_SC_VMAC_ADDRESS *dest,
|
|
uint16_t vendor_id,
|
|
uint8_t proprietary_function,
|
|
uint8_t *proprietary_data,
|
|
size_t proprietary_data_len)
|
|
{
|
|
size_t offs;
|
|
|
|
offs = bvlc_sc_encode_common(
|
|
pdu, pdu_len, BVLC_SC_PROPRIETARY_MESSAGE, message_id, origin, dest);
|
|
if (!offs) {
|
|
return 0;
|
|
}
|
|
|
|
if (pdu_len < (offs + sizeof(vendor_id) + sizeof(proprietary_function) +
|
|
proprietary_data_len)) {
|
|
return 0;
|
|
}
|
|
|
|
memcpy(&pdu[offs], &vendor_id, sizeof(vendor_id));
|
|
offs += sizeof(vendor_id);
|
|
memcpy(&pdu[offs], &proprietary_function, sizeof(proprietary_function));
|
|
offs += sizeof(proprietary_function);
|
|
memcpy(&pdu[offs], proprietary_data, proprietary_data_len);
|
|
offs += proprietary_data_len;
|
|
return (unsigned int)offs;
|
|
}
|
|
|
|
/**
|
|
* @brief Function decodes the Proprietary Message according BACNet standard
|
|
* AB.2.16.1 Proprietary Message Format.
|
|
* @param payload - pointer to the decoded data
|
|
* @param packed_payload - pointer to the packed data
|
|
* @param packed_payload_len - size of the packed data
|
|
* @param error_code - pointer to the error code
|
|
* @param error_class - pointer to the error class
|
|
* @param err_desc - pointer to the error description string
|
|
* @return true if the data is decoded successfully, false otherwise
|
|
*/
|
|
static bool bvlc_sc_decode_proprietary(
|
|
BVLC_SC_DECODED_DATA *payload,
|
|
uint8_t *packed_payload,
|
|
size_t packed_payload_len,
|
|
uint16_t *error_code,
|
|
uint16_t *error_class,
|
|
const char **err_desc)
|
|
{
|
|
if (packed_payload_len < 3) {
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_proprietary_incomplete;
|
|
return false;
|
|
}
|
|
|
|
memcpy(
|
|
&payload->proprietary.vendor_id, packed_payload,
|
|
sizeof(payload->proprietary.vendor_id));
|
|
packed_payload += sizeof(payload->proprietary.vendor_id);
|
|
payload->proprietary.function = packed_payload[0];
|
|
packed_payload++;
|
|
if (packed_payload_len - 3 > 0) {
|
|
payload->proprietary.data = packed_payload;
|
|
payload->proprietary.data_len = packed_payload_len - 3;
|
|
} else {
|
|
payload->proprietary.data = NULL;
|
|
payload->proprietary.data_len = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Decode the BVLC-SC header
|
|
* @param message - buffer with the message
|
|
* @param message_len - length of the message
|
|
* @param hdr - pointer to the decoded header
|
|
* @param error_code - pointer to the error code
|
|
* @param error_class - pointer to the error class
|
|
* @param err_desc - pointer to the error description string
|
|
* @return true if the data is decoded successfully, false otherwise
|
|
*/
|
|
static bool bvlc_sc_decode_hdr(
|
|
uint8_t *message,
|
|
size_t message_len,
|
|
BVLC_SC_DECODED_HDR *hdr,
|
|
uint16_t *error_code,
|
|
uint16_t *error_class,
|
|
const char **err_desc)
|
|
{
|
|
size_t offs = 4;
|
|
bool ret = false;
|
|
size_t hdr_opt_len = 0;
|
|
|
|
memset(hdr, 0, sizeof(*hdr));
|
|
|
|
if (message_len < 4) {
|
|
/* According EA-001-4 'Clarifying BVLC-Result in BACnet/SC ' */
|
|
/* If a BVLC message is received that has fewer than four octets, a */
|
|
/* BVLC-Result NAK shall not be returned. */
|
|
/* The message shall be discarded and not be processed. */
|
|
*error_code = ERROR_CODE_DISCARD;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = NULL;
|
|
return ret;
|
|
}
|
|
|
|
hdr->bvlc_function = message[0];
|
|
memcpy(&hdr->message_id, &message[2], sizeof(hdr->message_id));
|
|
|
|
if (message[1] & BVLC_SC_CONTROL_ORIG_VADDR) {
|
|
hdr->origin = (BACNET_SC_VMAC_ADDRESS *)&message[offs];
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
if (offs > message_len) {
|
|
hdr->origin = NULL;
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_hdr_incomplete1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (message[1] & BVLC_SC_CONTROL_DEST_VADDR) {
|
|
hdr->dest = (BACNET_SC_VMAC_ADDRESS *)&message[offs];
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
if (offs > message_len) {
|
|
hdr->dest = NULL;
|
|
*error_code = ERROR_CODE_MESSAGE_INCOMPLETE;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_hdr_incomplete2;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* in order to handle correctly item AB.3.1.5 Common Error Situations, */
|
|
/* in a case a BVLC message is received with unknown BVLC function, */
|
|
/* upper layer logic must understand if the message is unicast or not. */
|
|
/* That's why that error handling is put after filling of address fields. */
|
|
|
|
if (message[0] > BVLC_SC_PROPRIETARY_MESSAGE) {
|
|
*error_code = ERROR_CODE_BVLC_FUNCTION_UNKNOWN;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_unknown_bvlc_function;
|
|
return ret;
|
|
}
|
|
|
|
if (message[1] & BVLC_SC_CONTROL_DEST_OPTIONS) {
|
|
ret = bvlc_sc_validate_options_headers(
|
|
BACNET_PDU_DEST_OPTION_VALIDATION, &message[offs],
|
|
message_len - offs, &hdr_opt_len, &hdr->dest_options_num,
|
|
error_code, error_class, err_desc);
|
|
|
|
if (!ret) {
|
|
return false;
|
|
}
|
|
hdr->dest_options = &message[offs];
|
|
hdr->dest_options_len = hdr_opt_len;
|
|
offs += hdr_opt_len;
|
|
}
|
|
|
|
if (message[1] & BVLC_SC_CONTROL_DATA_OPTIONS) {
|
|
ret = bvlc_sc_validate_options_headers(
|
|
BACNET_PDU_DATA_OPTION_VALIDATION, &message[offs],
|
|
message_len - offs, &hdr_opt_len, &hdr->data_options_num,
|
|
error_code, error_class, err_desc);
|
|
if (!ret) {
|
|
return false;
|
|
}
|
|
hdr->data_options = &message[offs];
|
|
hdr->data_options_len = hdr_opt_len;
|
|
offs += hdr_opt_len;
|
|
}
|
|
|
|
if (offs >= message_len) {
|
|
/* no payload */
|
|
return true;
|
|
}
|
|
|
|
hdr->payload = &message[offs];
|
|
hdr->payload_len = message_len - offs;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Function decodes the BACnet SC header options.
|
|
* @param option_array - pointer to the array of decoded options
|
|
* @param options_list - pointer to the packed options list
|
|
* @return true if the data is decoded successfully, false otherwise
|
|
*/
|
|
static bool bvlc_sc_decode_header_options(
|
|
BVLC_SC_DECODED_HDR_OPTION *option_array, uint8_t *options_list)
|
|
{
|
|
uint8_t *next_option = options_list;
|
|
size_t i = 0;
|
|
|
|
while (next_option) {
|
|
bvlc_sc_decode_option_hdr(
|
|
options_list, &option_array[i].type,
|
|
&option_array[i].must_understand, &next_option);
|
|
|
|
option_array[i].packed_header_marker = options_list[0];
|
|
|
|
if (option_array[i].type == BVLC_SC_OPTION_TYPE_PROPRIETARY) {
|
|
bvlc_sc_decode_proprietary_option(
|
|
options_list, &option_array[i].specific.proprietary.vendor_id,
|
|
&option_array[i].specific.proprietary.option_type,
|
|
&option_array[i].specific.proprietary.data,
|
|
&option_array[i].specific.proprietary.data_len);
|
|
}
|
|
i++;
|
|
options_list = next_option;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Function decodes the BACnet SC header options if they exist.
|
|
* @param message - pointer to the decoded message
|
|
*/
|
|
static void
|
|
bvlc_sc_decode_dest_options_if_exists(BVLC_SC_DECODED_MESSAGE *message)
|
|
{
|
|
if (message->hdr.dest_options) {
|
|
bvlc_sc_decode_header_options(
|
|
message->dest_options, message->hdr.dest_options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Function decodes the BACnet SC header options if they exist.
|
|
* @param message - pointer to the decoded message
|
|
*/
|
|
static void
|
|
bvlc_sc_decode_data_options_if_exists(BVLC_SC_DECODED_MESSAGE *message)
|
|
{
|
|
if (message->hdr.data_options) {
|
|
bvlc_sc_decode_header_options(
|
|
message->data_options, message->hdr.data_options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Function decodes BACNet/SC message.
|
|
*
|
|
* @param buf - A buffer which holds BACNet/SC PDU.
|
|
* @param buf_len - length of a buffer which holds BACNet/SC PDU.
|
|
* @param message- pointer to structure for decoded data.
|
|
* @param error-code - the value of parameter is filled if function returns
|
|
* false. Check BACNET_ERROR_CLASS enum.
|
|
* @param error_class - the value of parameter is filled if function returns
|
|
* false. Check BACNET_ERROR_CODE enum.
|
|
* @return true if PDU was successfully decoded otherwise returns false,
|
|
* and error and class parameters are filled with corresponded codes.
|
|
*/
|
|
bool bvlc_sc_decode_message(
|
|
uint8_t *buf,
|
|
size_t buf_len,
|
|
BVLC_SC_DECODED_MESSAGE *message,
|
|
uint16_t *error_code,
|
|
uint16_t *error_class,
|
|
const char **err_desc)
|
|
{
|
|
if (!message || !buf_len || !error_code || !error_class || !buf ||
|
|
!err_desc) {
|
|
return false;
|
|
}
|
|
|
|
memset(message->data_options, 0, sizeof(message->data_options));
|
|
memset(message->dest_options, 0, sizeof(message->dest_options));
|
|
memset(&message->payload, 0, sizeof(message->payload));
|
|
|
|
if (!bvlc_sc_decode_hdr(
|
|
buf, buf_len, &message->hdr, error_code, error_class, err_desc)) {
|
|
return false;
|
|
}
|
|
|
|
if (message->hdr.dest_options) {
|
|
if (message->hdr.dest_options_num > BVLC_SC_HEADER_OPTION_MAX) {
|
|
/* dest header options list is too long */
|
|
*error_code = ERROR_CODE_OUT_OF_MEMORY;
|
|
*error_class = ERROR_CLASS_RESOURCES;
|
|
*err_desc = s_dest_options_list_too_long;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (message->hdr.data_options) {
|
|
if (message->hdr.data_options_num > BVLC_SC_HEADER_OPTION_MAX) {
|
|
/* data header options list is too long */
|
|
*error_code = ERROR_CODE_OUT_OF_MEMORY;
|
|
*error_class = ERROR_CLASS_RESOURCES;
|
|
*err_desc = s_data_options_list_too_long;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
switch (message->hdr.bvlc_function) {
|
|
case BVLC_SC_RESULT: {
|
|
if (message->hdr.data_options) {
|
|
/* The BVLC-Result message must not have data options */
|
|
*error_code = ERROR_CODE_INCONSISTENT_PARAMETERS;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_result_unexpected_data_options;
|
|
return false;
|
|
}
|
|
|
|
if (!message->hdr.payload || !message->hdr.payload_len) {
|
|
*error_code = ERROR_CODE_PAYLOAD_EXPECTED;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_result_payload_expected;
|
|
return false;
|
|
}
|
|
|
|
bvlc_sc_decode_dest_options_if_exists(message);
|
|
|
|
if (!bvlc_sc_decode_result(
|
|
&message->payload, message->hdr.payload,
|
|
message->hdr.payload_len, error_code, error_class,
|
|
err_desc)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case BVLC_SC_ENCAPSULATED_NPDU: {
|
|
if (!message->hdr.payload || !message->hdr.payload_len) {
|
|
*error_code = ERROR_CODE_PAYLOAD_EXPECTED;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_encapsulated_npdu_payload_expected;
|
|
return false;
|
|
}
|
|
|
|
bvlc_sc_decode_dest_options_if_exists(message);
|
|
bvlc_sc_decode_data_options_if_exists(message);
|
|
|
|
message->payload.encapsulated_npdu.npdu = message->hdr.payload;
|
|
message->payload.encapsulated_npdu.npdu_len =
|
|
message->hdr.payload_len;
|
|
break;
|
|
}
|
|
case BVLC_SC_ADDRESS_RESOLUTION: {
|
|
if (message->hdr.data_options) {
|
|
/* The Address-Resolution message must not have data options */
|
|
*error_code = ERROR_CODE_INCONSISTENT_PARAMETERS;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_address_resolution_data_options;
|
|
return false;
|
|
}
|
|
|
|
if (message->hdr.payload || message->hdr.payload_len) {
|
|
/* According EA-001-4 'Clarifying BVLC-Result in BACnet/SC */
|
|
/* If a BVLC message is received that is longer than expected,
|
|
*/
|
|
/* a BVLC-Result NAK shall be returned if it was a unicast */
|
|
/* message, indicating an 'Error Class' of COMMUNICATION and */
|
|
/* 'Error Code' of UNEXPECTED_DATA. The message shall be */
|
|
/* discarded and not be processed. */
|
|
|
|
*error_code = ERROR_CODE_UNEXPECTED_DATA;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_address_resolution_unexpected;
|
|
return false;
|
|
}
|
|
|
|
bvlc_sc_decode_dest_options_if_exists(message);
|
|
break;
|
|
}
|
|
case BVLC_SC_ADDRESS_RESOLUTION_ACK: {
|
|
if (message->hdr.data_options) {
|
|
/* The Address-Resolution Ack message must not have data options
|
|
*/
|
|
*error_code = ERROR_CODE_INCONSISTENT_PARAMETERS;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_address_resolution_ack_data_options;
|
|
return false;
|
|
}
|
|
|
|
bvlc_sc_decode_dest_options_if_exists(message);
|
|
|
|
if (message->hdr.payload_len) {
|
|
message->payload.address_resolution_ack
|
|
.utf8_websocket_uri_string = message->hdr.payload;
|
|
} else {
|
|
message->payload.address_resolution_ack
|
|
.utf8_websocket_uri_string = NULL;
|
|
}
|
|
message->payload.address_resolution_ack
|
|
.utf8_websocket_uri_string_len = message->hdr.payload_len;
|
|
break;
|
|
}
|
|
case BVLC_SC_ADVERTISIMENT: {
|
|
if (message->hdr.data_options) {
|
|
/* The advertisiment message must not have data options */
|
|
*error_code = ERROR_CODE_INCONSISTENT_PARAMETERS;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_advertisiment_data_options;
|
|
return false;
|
|
}
|
|
|
|
if (!message->hdr.payload || !message->hdr.payload_len) {
|
|
*error_code = ERROR_CODE_PAYLOAD_EXPECTED;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_advertisiment_payload_expected;
|
|
return false;
|
|
}
|
|
|
|
bvlc_sc_decode_dest_options_if_exists(message);
|
|
|
|
if (!bvlc_sc_decode_advertisiment(
|
|
&message->payload, message->hdr.payload,
|
|
message->hdr.payload_len, error_code, error_class,
|
|
err_desc)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case BVLC_SC_ADVERTISIMENT_SOLICITATION: {
|
|
if (message->hdr.data_options) {
|
|
/* The advertisiment solicitation message must not have data
|
|
* options */
|
|
*error_code = ERROR_CODE_INCONSISTENT_PARAMETERS;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_advertisiment_solicitation_data_options;
|
|
return false;
|
|
}
|
|
|
|
if (message->hdr.payload || message->hdr.payload_len) {
|
|
*error_code = ERROR_CODE_UNEXPECTED_DATA;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_advertisiment_solicitation_payload_expected;
|
|
return false;
|
|
}
|
|
|
|
bvlc_sc_decode_dest_options_if_exists(message);
|
|
break;
|
|
}
|
|
case BVLC_SC_CONNECT_REQUEST:
|
|
case BVLC_SC_CONNECT_ACCEPT:
|
|
case BVLC_SC_DISCONNECT_REQUEST:
|
|
case BVLC_SC_DISCONNECT_ACK:
|
|
case BVLC_SC_HEARTBEAT_REQUEST:
|
|
case BVLC_SC_HEARTBEAT_ACK: {
|
|
if (message->hdr.origin != 0) {
|
|
*error_code = ERROR_CODE_HEADER_ENCODING_ERROR;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_origin_unexpected;
|
|
return false;
|
|
}
|
|
|
|
if (message->hdr.dest != 0) {
|
|
*error_code = ERROR_CODE_HEADER_ENCODING_ERROR;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_dest_unexpected;
|
|
return false;
|
|
}
|
|
|
|
if (message->hdr.data_options) {
|
|
*error_code = ERROR_CODE_INCONSISTENT_PARAMETERS;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_data_option_unexpected;
|
|
return false;
|
|
}
|
|
|
|
if (message->hdr.bvlc_function == BVLC_SC_CONNECT_REQUEST ||
|
|
message->hdr.bvlc_function == BVLC_SC_CONNECT_ACCEPT) {
|
|
if (!message->hdr.payload || !message->hdr.payload_len) {
|
|
*error_code = ERROR_CODE_PAYLOAD_EXPECTED;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_absent_payload;
|
|
return false;
|
|
}
|
|
} else {
|
|
if (message->hdr.payload || message->hdr.payload_len) {
|
|
*error_code = ERROR_CODE_UNEXPECTED_DATA;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_message_too_long;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bvlc_sc_decode_dest_options_if_exists(message);
|
|
|
|
if (message->hdr.bvlc_function == BVLC_SC_CONNECT_REQUEST) {
|
|
if (!bvlc_sc_decode_connect_request(
|
|
&message->payload, message->hdr.payload,
|
|
message->hdr.payload_len, error_code, error_class,
|
|
err_desc)) {
|
|
return false;
|
|
}
|
|
} else if (message->hdr.bvlc_function == BVLC_SC_CONNECT_ACCEPT) {
|
|
if (!bvlc_sc_decode_connect_accept(
|
|
&message->payload, message->hdr.payload,
|
|
message->hdr.payload_len, error_code, error_class,
|
|
err_desc)) {
|
|
return false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case BVLC_SC_PROPRIETARY_MESSAGE: {
|
|
if (message->hdr.data_options) {
|
|
/* The proprietary message must not have data options */
|
|
*error_code = ERROR_CODE_INCONSISTENT_PARAMETERS;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_proprietary_data_options;
|
|
return false;
|
|
}
|
|
|
|
if (!message->hdr.payload || !message->hdr.payload_len) {
|
|
*error_code = ERROR_CODE_PAYLOAD_EXPECTED;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_proprietary_payload;
|
|
return false;
|
|
}
|
|
|
|
bvlc_sc_decode_dest_options_if_exists(message);
|
|
|
|
if (!bvlc_sc_decode_proprietary(
|
|
&message->payload, message->hdr.payload,
|
|
message->hdr.payload_len, error_code, error_class,
|
|
err_desc)) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
*error_code = ERROR_CODE_BVLC_FUNCTION_UNKNOWN;
|
|
*error_class = ERROR_CLASS_COMMUNICATION;
|
|
*err_desc = s_unknown_bvlc_function;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Function removes destination address of BACNet/SC message
|
|
* and sets originating address instead of it.
|
|
* It does it job only if message has destination address
|
|
* and does not have origination address, otherwise pdu
|
|
* stays unchanged.
|
|
* @param pdu - BACNet/SC PDU.
|
|
* @param pdu_len - length of a buffer which holds BACNet/SC PDU.
|
|
* @param orig- origination vmac.
|
|
*/
|
|
void bvlc_sc_remove_dest_set_orig(
|
|
uint8_t *pdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *orig)
|
|
{
|
|
size_t offs = 4;
|
|
if (pdu && pdu_len > 4) {
|
|
if (!(pdu[1] & BVLC_SC_CONTROL_ORIG_VADDR) &&
|
|
(pdu[1] & BVLC_SC_CONTROL_DEST_VADDR)) {
|
|
pdu[1] &= ~(BVLC_SC_CONTROL_DEST_VADDR);
|
|
pdu[1] |= BVLC_SC_CONTROL_ORIG_VADDR;
|
|
memcpy(&pdu[offs], orig, sizeof(*orig));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Function changes or adds originating address into BACNet/SC message.
|
|
* It is assumed that ppdu points to buffer with pdu that has
|
|
* BSC_PRE bytes behind.
|
|
* @param ppdu - pointer to buffer which holds BACNet/SC PDU.
|
|
* @param pdu_len - length of a buffer which holds BACNet/SC PDU.
|
|
* @param orig- origination vmac.
|
|
* @return new pdu length if function succeeded and ppdu points to beginning of
|
|
* changed pdu, otherwise returns old pdu_len and ppdu is not changed.
|
|
*/
|
|
size_t
|
|
bvlc_sc_set_orig(uint8_t **ppdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *orig)
|
|
{
|
|
uint8_t buf[BSC_PRE];
|
|
uint8_t *pdu = *ppdu;
|
|
size_t offs = 4;
|
|
if (pdu && pdu_len > 4) {
|
|
if (pdu[1] & BVLC_SC_CONTROL_ORIG_VADDR) {
|
|
memcpy(&pdu[offs], orig, sizeof(orig->address));
|
|
return pdu_len;
|
|
} else {
|
|
memcpy(buf, pdu, 4);
|
|
buf[1] |= BVLC_SC_CONTROL_ORIG_VADDR;
|
|
memcpy(&buf[4], orig, sizeof(orig->address));
|
|
memcpy(pdu - BVLC_SC_VMAC_SIZE, buf, BVLC_SC_VMAC_SIZE + 4);
|
|
*ppdu = pdu - BVLC_SC_VMAC_SIZE;
|
|
return pdu_len + BVLC_SC_VMAC_SIZE;
|
|
}
|
|
} else {
|
|
return pdu_len;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Function checks if vmac address is broadcast.
|
|
* @param vmac - pointer vmac address.
|
|
* @return true if vmac is broadcast. otherwise returns false.
|
|
*/
|
|
bool bvlc_sc_is_vmac_broadcast(BACNET_SC_VMAC_ADDRESS *vmac)
|
|
{
|
|
int i;
|
|
for (i = 0; i < BVLC_SC_VMAC_SIZE; i++) {
|
|
if (vmac->address[i] != 0xFF) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Function checks if it is needed to send BVLC result
|
|
* response message for given decoded BACNet/SC message.
|
|
* In a case of errors, standard requires to send such kind
|
|
* of responses for unicast messages of specific types.
|
|
* @param dm - pointer to decoded BACNet/SC message.
|
|
* @return true if vmac is broadcast, otherwise returns false.
|
|
*/
|
|
bool bvlc_sc_need_send_bvlc_result(BVLC_SC_DECODED_MESSAGE *dm)
|
|
{
|
|
if (dm->hdr.dest == NULL || !bvlc_sc_is_vmac_broadcast(dm->hdr.dest)) {
|
|
if (dm->hdr.bvlc_function == BVLC_SC_CONNECT_REQUEST ||
|
|
dm->hdr.bvlc_function == BVLC_SC_DISCONNECT_REQUEST ||
|
|
dm->hdr.bvlc_function == BVLC_SC_ENCAPSULATED_NPDU ||
|
|
dm->hdr.bvlc_function == BVLC_SC_ADDRESS_RESOLUTION ||
|
|
dm->hdr.bvlc_function == BVLC_SC_ADVERTISIMENT_SOLICITATION ||
|
|
dm->hdr.bvlc_function == BVLC_SC_HEARTBEAT_REQUEST ||
|
|
dm->hdr.bvlc_function > BVLC_SC_PROPRIETARY_MESSAGE) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Function checks if destination address of input BACNet/SC
|
|
* message is broadcast.
|
|
* @param pdu- buffer with BACNet/SC message.
|
|
* @param pdu_len- length of buffer of BACNet/SC message.
|
|
* @return true if destination address of input BACNet/SC
|
|
* is broadcast, otherwise returns false.
|
|
*/
|
|
bool bvlc_sc_pdu_has_dest_broadcast(uint8_t *pdu, size_t pdu_len)
|
|
{
|
|
size_t offs = 4;
|
|
|
|
if (pdu_len >= 4) {
|
|
if (pdu[1] & BVLC_SC_CONTROL_ORIG_VADDR) {
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
}
|
|
if (pdu[1] & BVLC_SC_CONTROL_DEST_VADDR &&
|
|
(offs + BVLC_SC_VMAC_SIZE) <= pdu_len) {
|
|
return bvlc_sc_is_vmac_broadcast(
|
|
(BACNET_SC_VMAC_ADDRESS *)&pdu[offs]);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Function checks if input BACNet/SC message has
|
|
* destination address field.
|
|
* @param pdu- buffer with BACNet/SC message.
|
|
* @param pdu_len- length of buffer of BACNet/SC message.
|
|
* @return true if destination address is presented in
|
|
* input BACNet/SC message, otherwise returns false.
|
|
*/
|
|
bool bvlc_sc_pdu_has_no_dest(uint8_t *pdu, size_t pdu_len)
|
|
{
|
|
if (pdu_len >= 4) {
|
|
if (pdu[1] & BVLC_SC_CONTROL_DEST_VADDR) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Function puts destination address of
|
|
* BACNet/SC message into vmac if message
|
|
* contains it.
|
|
* @param pdu- buffer with BACNet/SC message.
|
|
* @param pdu_len- length of buffer of BACNet/SC message.
|
|
* @return true if destination address is presented and was
|
|
* placed into vmac, otherwise returns false.
|
|
*/
|
|
bool bvlc_sc_pdu_get_dest(
|
|
uint8_t *pdu, size_t pdu_len, BACNET_SC_VMAC_ADDRESS *vmac)
|
|
{
|
|
size_t offs = 4;
|
|
|
|
if (pdu_len >= 4) {
|
|
if (pdu[1] & BVLC_SC_CONTROL_ORIG_VADDR) {
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
}
|
|
if (pdu[1] & BVLC_SC_CONTROL_DEST_VADDR &&
|
|
(offs + BVLC_SC_VMAC_SIZE) <= pdu_len) {
|
|
memcpy(&vmac->address[0], &pdu[offs], BVLC_SC_VMAC_SIZE);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Function removes originating and destination
|
|
* address fields from input BACNet/SC message.
|
|
* @param ppdu- pointer to buffer of BACNet/SC message.
|
|
* @param pdu_len- length of buffer of BACNet/SC message.
|
|
* @return new length of changed pdu if originating or destination
|
|
* addresses were removed or old pdu length if
|
|
* pdu was not changed. If pdu was changed, ppdu contains
|
|
* updated pointer to buffer to modified BACNet/SC message.
|
|
*/
|
|
size_t bvlc_sc_remove_orig_and_dest(uint8_t **ppdu, size_t pdu_len)
|
|
{
|
|
uint8_t *pdu = *ppdu;
|
|
size_t offs = 4;
|
|
|
|
if (pdu_len > 4) {
|
|
if (pdu[1] & BVLC_SC_CONTROL_ORIG_VADDR) {
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
}
|
|
if (pdu[1] & BVLC_SC_CONTROL_DEST_VADDR) {
|
|
offs += BVLC_SC_VMAC_SIZE;
|
|
}
|
|
pdu[1] &= ~(BVLC_SC_CONTROL_ORIG_VADDR);
|
|
pdu[1] &= ~(BVLC_SC_CONTROL_DEST_VADDR);
|
|
memmove(&pdu[offs - 4], pdu, 4);
|
|
*ppdu = &pdu[offs - 4];
|
|
return pdu_len - offs + 4;
|
|
}
|
|
return pdu_len;
|
|
}
|