Fixed BVLC Write Broadcast Distribution Table for protocol revision 17 and later. (#1005)

* Fixed BVLC Write Broadcast Distribution Table for protocol revision 17 and later.

* Added check for Network Port object bbmd-accept-fd-registrations property in BBMD handler.

* Added bvlc_foreign_device_table_decode() function and unit test.

* Added BDT and FTD write property to network port for IPv4
This commit is contained in:
Steve Karg
2025-06-10 09:48:10 -05:00
committed by GitHub
parent f0ec05d085
commit 5d45d43da5
7 changed files with 711 additions and 48 deletions
+45
View File
@@ -50,6 +50,8 @@ static bool BVLC_NAT_Handling = false;
static BACNET_IP_ADDRESS Remote_BBMD;
/** if we are a foreign device, store the Time-To-Live Seconds here */
static uint16_t Remote_BBMD_TTL_Seconds;
/** Dynamic enable or disable of accepting FD registrations */
static bool BBMD_Accept_FD_Registrations = 1;
#if BBMD_ENABLED || BBMD_CLIENT_ENABLED
/* local buffer & length for sending */
static uint8_t BVLC_Buffer[BIP_MPDU_MAX];
@@ -873,6 +875,22 @@ int bvlc_bbmd_enabled_handler(
}
break;
case BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE:
/* J.2.2.1 Write-Broadcast-Distribution-Table: Format
...
Prior to the introduction of the Network Port object
in Protocol_Revision 17, this message was the interoperable
means of updating BDTs. That function is now performed
by writes to the Network Port object
...
J.4.4.2 Use of the BVLL Write-Broadcast-Distribution-Table Message
Upon receipt of a BVLL Write-Broadcast-Distribution-Table message,
B/IP devices shall always return a BVLC-Result message to the
originating device with a result code of X'0010' indicating that
the Write-Broadcast-Distribution BVLL message is not supported.*/
#if (BACNET_PROTOCOL_REVISION >= 17)
result_code = BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK;
send_result = true;
#else
debug_print_bip("Received Write-BDT", addr);
function_len = bvlc_decode_write_broadcast_distribution_table(
pdu, pdu_len, &BBMD_Table[0]);
@@ -886,6 +904,7 @@ int bvlc_bbmd_enabled_handler(
BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK;
send_result = true;
}
#endif
/* not an NPDU */
offset = 0;
break;
@@ -971,6 +990,13 @@ int bvlc_bbmd_enabled_handler(
without the receipt of another BVLL Register-Foreign-Device
message from the same foreign device, the FDT entry for this
device shall be cleared. */
#if (BACNET_PROTOCOL_REVISION >= 17)
if (!BBMD_Accept_FD_Registrations) {
result_code = BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK;
send_result = true;
break;
}
#endif
function_len =
bvlc_decode_register_foreign_device(pdu, pdu_len, &ttl_seconds);
if (function_len) {
@@ -1203,6 +1229,25 @@ int bvlc_broadcast_handler(
return offset;
}
/**
* @brief Get the status of the BBMD_Accept_FD_Registrations flag.
* @return true if BBMD_Accept_FD_Registrations is enabled, false otherwise.
*/
bool bvlc_bbmd_accept_fd_registrations(void)
{
return BBMD_Accept_FD_Registrations;
}
/**
* @brief Set the status of the BBMD_Accept_FD_Registrations flag.
* @param flag - true to enable accepting foreign device registrations,
* false to disable.
*/
void bvlc_bbmd_accept_fd_registrations_set(bool flag)
{
BBMD_Accept_FD_Registrations = flag;
}
#if BBMD_CLIENT_ENABLED
/** Register as a foreign device with the indicated BBMD.
* @param bbmd_addr - IPv4 address of BBMD with which to register
+5
View File
@@ -98,6 +98,11 @@ void bvlc_remote_bbmd_address(BACNET_IP_ADDRESS *address);
BACNET_STACK_EXPORT
uint16_t bvlc_remote_bbmd_lifetime(void);
BACNET_STACK_EXPORT
bool bvlc_bbmd_accept_fd_registrations(void);
BACNET_STACK_EXPORT
void bvlc_bbmd_accept_fd_registrations_set(bool flag);
/* Local interface to manage BBMD.
* The interface user needs to handle mutual exclusion if needed i.e.
* BACnet packet is not being handled when the BBMD table is modified.
+245
View File
@@ -1869,6 +1869,110 @@ static int BBMD_Broadcast_Distribution_Table_Encode(
return apdu_len;
}
/**
* @brief Get the BACnetLIST capacity
* @param object_instance [in] BACnet network port object instance number
* @return capacity of the BACnetList (number of possible entries) or
* zero on error
*/
static size_t
BBMD_Broadcast_Distribution_Table_Capacity(uint32_t object_instance)
{
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_list;
size_t capacity = 0;
unsigned index = 0;
index = Network_Port_Instance_To_Index(object_instance);
if (index < BACNET_NETWORK_PORTS_MAX) {
if (Object_List[index].Network_Type == PORT_TYPE_BIP) {
bdt_list = Object_List[index].Network.IPv4.BBMD_BD_Table;
capacity = bvlc_broadcast_distribution_table_count(bdt_list);
}
}
return capacity;
}
/**
* @brief Decode a BACnetLIST property element to determine the element length
* @param object_instance [in] BACnet network port object instance number
* @param apdu [in] Buffer in which the APDU contents are extracted
* @param apdu_size [in] The size of the APDU buffer
* @return The length of the decoded apdu, or BACNET_STATUS_ERROR on error
*/
static int BBMD_Broadcast_Distribution_Table_Element_Length(
uint32_t object_instance, uint8_t *apdu, size_t apdu_size)
{
int len = 0;
unsigned index = 0;
index = Network_Port_Instance_To_Index(object_instance);
if (index < BACNET_NETWORK_PORTS_MAX) {
if (Object_List[index].Network_Type == PORT_TYPE_BIP) {
len = bvlc_decode_broadcast_distribution_table_entry(
apdu, apdu_size, NULL);
}
}
return len;
}
/**
* @brief Write a value to a BACnetLIST property element value
* using a BACnetARRAY write utility function
* @param object_instance [in] BACnet network port object instance number
* @param array_index [in] array index to write:
* 0=array size, 1 to N for individual array members
* @param application_data [in] encoded element value
* @param application_data_len [in] The size of the encoded element value
* @return BACNET_ERROR_CODE value
*/
static BACNET_ERROR_CODE BBMD_Broadcast_Distribution_Table_Element_Write(
uint32_t object_instance,
BACNET_ARRAY_INDEX array_index,
uint8_t *application_data,
size_t application_data_len)
{
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY bdt_entry = { 0 };
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_list;
uint16_t capacity = 0;
int len;
bool status = false;
unsigned index = 0;
index = Network_Port_Instance_To_Index(object_instance);
if (index < BACNET_NETWORK_PORTS_MAX) {
if (Object_List[index].Network_Type == PORT_TYPE_BIP) {
bdt_list = Object_List[index].Network.IPv4.BBMD_BD_Table;
capacity = bvlc_broadcast_distribution_table_count(bdt_list);
if (array_index == 0) {
error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
} else if (array_index <= capacity) {
len = bvlc_decode_broadcast_distribution_table_entry(
application_data, application_data_len, &bdt_entry);
if (len > 0) {
status = bvlc_broadcast_distribution_table_entry_insert(
bdt_list, &bdt_entry, array_index);
if (status) {
error_code = ERROR_CODE_SUCCESS;
} else {
error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
} else {
error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
} else {
error_code = ERROR_CODE_ABORT_OTHER;
}
}
return error_code;
}
/**
* For a given object instance-number, sets the BBMD-BD-Table head
* property value
@@ -1980,6 +2084,108 @@ static int BBMD_Foreign_Device_Table_Encode(
return apdu_len;
}
/**
* @brief Get the BACnetLIST capacity
* @param object_instance [in] BACnet network port object instance number
* @return capacity of the BACnetList (number of possible entries) or
* zero on error
*/
static size_t BBMD_Foreign_Device_Table_Capacity(uint32_t object_instance)
{
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_list;
size_t capacity = 0;
unsigned index = 0;
index = Network_Port_Instance_To_Index(object_instance);
if (index < BACNET_NETWORK_PORTS_MAX) {
if (Object_List[index].Network_Type == PORT_TYPE_BIP) {
fdt_list = Object_List[index].Network.IPv4.BBMD_FD_Table;
capacity = bvlc_foreign_device_table_count(fdt_list);
}
}
return capacity;
}
/**
* @brief Decode a BACnetLIST property element to determine the element length
* @param object_instance [in] BACnet network port object instance number
* @param apdu [in] Buffer in which the APDU contents are extracted
* @param apdu_size [in] The size of the APDU buffer
* @return The length of the decoded apdu, or BACNET_STATUS_ERROR on error
*/
static int BBMD_Foreign_Device_Table_Element_Length(
uint32_t object_instance, uint8_t *apdu, size_t apdu_size)
{
int len = 0;
unsigned index = 0;
index = Network_Port_Instance_To_Index(object_instance);
if (index < BACNET_NETWORK_PORTS_MAX) {
if (Object_List[index].Network_Type == PORT_TYPE_BIP) {
len = bvlc_decode_foreign_device_table_entry(apdu, apdu_size, NULL);
}
}
return len;
}
/**
* @brief Write a value to a BACnetLIST property element value
* using a BACnetARRAY write utility function
* @param object_instance [in] BACnet network port object instance number
* @param array_index [in] array index to write:
* 0=array size, 1 to N for individual array members
* @param application_data [in] encoded element value
* @param application_data_len [in] The size of the encoded element value
* @return BACNET_ERROR_CODE value
*/
static BACNET_ERROR_CODE BBMD_Foreign_Device_Table_Element_Write(
uint32_t object_instance,
BACNET_ARRAY_INDEX array_index,
uint8_t *application_data,
size_t application_data_len)
{
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY fdt_entry = { 0 };
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_list;
uint16_t capacity = 0;
int len;
bool status = false;
unsigned index = 0;
index = Network_Port_Instance_To_Index(object_instance);
if (index < BACNET_NETWORK_PORTS_MAX) {
if (Object_List[index].Network_Type == PORT_TYPE_BIP) {
fdt_list = Object_List[index].Network.IPv4.BBMD_FD_Table;
capacity = bvlc_foreign_device_table_count(fdt_list);
if (array_index == 0) {
error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
} else if (array_index <= capacity) {
len = bvlc_decode_foreign_device_table_entry(
application_data, application_data_len, &fdt_entry);
if (len > 0) {
status = bvlc_foreign_device_table_entry_insert(
fdt_list, &fdt_entry, array_index - 1);
if (status) {
error_code = ERROR_CODE_SUCCESS;
} else {
error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
} else {
error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
} else {
error_code = ERROR_CODE_ABORT_OTHER;
}
}
return error_code;
}
/**
* @brief For a given object instance-number, gets the HostNPort
* @note depends on Network_Type being set for this object
@@ -4100,6 +4306,7 @@ bool Network_Port_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
uint32_t capacity;
BACNET_APPLICATION_DATA_VALUE value = { 0 };
if (!Network_Port_Valid_Instance(wp_data->object_instance)) {
@@ -4181,6 +4388,44 @@ bool Network_Port_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
&wp_data->error_class, &wp_data->error_code);
}
break;
case PROP_BBMD_ACCEPT_FD_REGISTRATIONS:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN);
if (status) {
status = Network_Port_BBMD_Accept_FD_Registrations_Set(
wp_data->object_instance, value.type.Boolean);
if (!status) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_BBMD_BROADCAST_DISTRIBUTION_TABLE:
/* BACnetLIST */
capacity = BBMD_Broadcast_Distribution_Table_Capacity(
wp_data->object_instance);
wp_data->error_code = bacnet_array_write(
wp_data->object_instance, wp_data->array_index,
BBMD_Broadcast_Distribution_Table_Element_Length,
BBMD_Broadcast_Distribution_Table_Element_Write, capacity,
wp_data->application_data, wp_data->application_data_len);
if (wp_data->error_code == ERROR_CODE_SUCCESS) {
status = true;
}
break;
case PROP_BBMD_FOREIGN_DEVICE_TABLE:
/* BACnetLIST */
capacity =
BBMD_Foreign_Device_Table_Capacity(wp_data->object_instance);
wp_data->error_code = bacnet_array_write(
wp_data->object_instance, wp_data->array_index,
BBMD_Foreign_Device_Table_Element_Length,
BBMD_Foreign_Device_Table_Element_Write, capacity,
wp_data->application_data, wp_data->application_data_len);
if (wp_data->error_code == ERROR_CODE_SUCCESS) {
status = true;
}
break;
default:
if (Property_List_Member(
wp_data->object_instance, wp_data->object_property)) {
+216 -30
View File
@@ -356,6 +356,37 @@ bool bvlc_broadcast_distribution_table_entry_append(
return status;
}
/**
* @brief Append an entry to the Broadcast-Distribution-Table
* @param bdt_list - first entry in list of BDT entries
* @param bdt_entry - entry to insert into to list of BDT entries
* @param bdt_index - 0..N where N is count-1
* @return true if the Broadcast-Distribution-Table entry was inserted
*/
bool bvlc_broadcast_distribution_table_entry_insert(
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_list,
const BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry,
uint16_t bdt_index)
{
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_node = NULL;
uint16_t count = 0;
bool status = false;
bdt_node = bdt_list;
while (bdt_node) {
if (count == bdt_index) {
status = true;
bvlc_broadcast_distribution_table_entry_copy(bdt_node, bdt_entry);
bdt_node->valid = true;
break;
}
bdt_node = bdt_node->next;
count++;
}
return status;
}
/**
* @brief Set an entry to the Broadcast-Distribution-Table
* @param bdt_entry - first element in list of BDT entries
@@ -831,17 +862,15 @@ int bvlc_encode_write_broadcast_distribution_table(
* @param pdu_len - length of the buffer that needs decoding
* @param bdt_list - BDT Entry list
*
* @return number of bytes decoded
* @return number of bytes decoded, or 0 for none or error
*/
int bvlc_decode_write_broadcast_distribution_table(
const uint8_t *pdu,
uint16_t pdu_len,
uint16_t pdu_size,
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_list)
{
int bytes_consumed = 0;
int len = 0;
uint16_t offset = 0;
uint16_t pdu_bytes = 0;
uint16_t pdu_len = 0;
uint16_t bdt_entry_count = 0;
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry = NULL;
uint16_t list_len = 0;
@@ -850,26 +879,40 @@ int bvlc_decode_write_broadcast_distribution_table(
bdt_entry_count = bvlc_broadcast_distribution_table_count(bdt_list);
list_len = bdt_entry_count * BACNET_IP_BDT_ENTRY_SIZE;
/* will the entries fit */
if (pdu && (pdu_len <= list_len)) {
if (pdu && (pdu_size <= list_len)) {
/* check payload for valid entries */
while (pdu_len < pdu_size) {
len = bvlc_decode_broadcast_distribution_table_entry(
&pdu[pdu_len], pdu_size - pdu_len, NULL);
if (len > 0) {
pdu_len += len;
} else {
return 0;
}
}
pdu_len = 0;
bdt_entry = bdt_list;
while (bdt_entry) {
pdu_bytes = pdu_len - offset;
if (pdu_bytes >= BACNET_IP_BDT_ENTRY_SIZE) {
if (pdu_len < pdu_size) {
/* decode valid entries */
len = bvlc_decode_broadcast_distribution_table_entry(
&pdu[offset], pdu_bytes, bdt_entry);
&pdu[pdu_len], pdu_size - pdu_len, bdt_entry);
if (len > 0) {
pdu_len += len;
bdt_entry->valid = true;
} else {
/* set the available entries as invalid */
bdt_entry->valid = false;
}
offset += len;
} else {
/* set the available entries as invalid */
bdt_entry->valid = false;
}
bdt_entry = bdt_entry->next;
}
bytes_consumed = (int)offset;
}
return bytes_consumed;
return pdu_len;
}
/**
@@ -1274,6 +1317,92 @@ int bvlc_foreign_device_table_encode(
return len;
}
/**
* @brief Decode the Foreign_Device-Table for Network Port object
* @param apdu - the APDU buffer
* @param apdu_size - the APDU buffer length
* @param fdt_head - head of a FDT linked list
* @return length of the APDU buffer decoded, or ERROR, REJECT, or ABORT
*/
int bvlc_foreign_device_table_decode(
const uint8_t *apdu,
uint16_t apdu_size,
BACNET_ERROR_CODE *error_code,
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_head)
{
int len = 0, apdu_len = 0;
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_entry = NULL;
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
BACNET_OCTET_STRING octet_string = { 0 };
/* default reject code */
if (error_code) {
*error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
}
/* check for value pointers */
if ((apdu_size == 0) || (!apdu)) {
return BACNET_STATUS_REJECT;
}
fdt_entry = fdt_head;
while (fdt_entry) {
/* bacnetip-address [0] OCTET STRING */
len = bacnet_octet_string_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &octet_string);
if (len <= 0) {
if (error_code) {
*error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_REJECT;
}
bvlc_decode_address(
octetstring_value(&octet_string), octetstring_length(&octet_string),
&fdt_entry->dest_address);
apdu_len += len;
/* time-to-live [1] Unsigned16 */
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value);
if (len <= 0) {
if (error_code) {
*error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_REJECT;
}
apdu_len += len;
if (unsigned_value <= UINT16_MAX) {
fdt_entry->ttl_seconds = unsigned_value;
} else {
if (error_code) {
*error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
}
return BACNET_STATUS_REJECT;
}
/* remaining-time-to-live [2] Unsigned16 */
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 2, &unsigned_value);
if (len <= 0) {
if (error_code) {
*error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_REJECT;
}
apdu_len += len;
if (unsigned_value <= UINT16_MAX) {
fdt_entry->ttl_seconds_remaining = unsigned_value;
} else {
if (error_code) {
*error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
}
return BACNET_STATUS_REJECT;
}
/* mark as valid */
fdt_entry->valid = true;
/* next entry */
fdt_entry = fdt_entry->next;
}
return apdu_len;
}
/**
* @brief J.2.7 Read-Foreign-Device-Table: encode
*
@@ -1461,6 +1590,53 @@ bool bvlc_foreign_device_table_entry_add(
return status;
}
/**
* @brief Append an entry to the Foreign-Device-Table
* @param bdt_list - first entry in list of FDT entries
* @param bdt_entry - entry to insert into to list of FDT entries
* @param bdt_index - 0..N where N is count-1
* @return true if the entry was inserted
*/
bool bvlc_foreign_device_table_entry_insert(
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_list,
const BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_entry,
uint16_t array_index)
{
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_node = NULL;
uint16_t count = 0;
bool status = false;
fdt_node = fdt_list;
while (fdt_node) {
if (count == array_index) {
status = bvlc_foreign_device_table_entry_copy(fdt_node, fdt_entry);
fdt_node->valid = true;
break;
}
fdt_node = fdt_node->next;
count++;
}
return status;
}
/**
* @brief Clear all Write-Broadcast-Distribution-Table entries
* @param bdt_list - first element in array BDT entries
*/
void bvlc_foreign_device_table_valid_clear(
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_list)
{
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_entry;
/* clear the valid entries */
fdt_entry = fdt_list;
while (fdt_entry) {
fdt_entry->valid = false;
fdt_entry = fdt_entry->next;
}
}
/**
* @brief Count the number of valid Foreign-Device-Table entries
* @param fdt_list - first element in list of FDT entries
@@ -2143,6 +2319,7 @@ bool bvlc_address_mask(
dst->address[i] = src->address[i] | ~mask->address[i];
}
dst->port = src->port;
status = true;
}
return status;
@@ -2569,37 +2746,46 @@ int bvlc_encode_broadcast_distribution_table_entry(
* IP subnet served by the BBMD
*
* @param pdu - buffer from which to decode the message
* @param pdu_len - length of the buffer that needs decoding
* @param bdt_entry - BDT Entry
* @param pdu_size - length of the buffer that needs decoding
* @param bdt_entry - BDT Entry, or NULL for length only
*
* @return number of bytes decoded
* @return number of bytes decoded, or 0 if error
*/
int bvlc_decode_broadcast_distribution_table_entry(
const uint8_t *pdu,
uint16_t pdu_len,
uint16_t pdu_size,
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry)
{
int bytes_consumed = 0;
int pdu_len = 0;
int len = 0;
int offset = 0;
BACNET_IP_ADDRESS dest_address;
BACNET_IP_BROADCAST_DISTRIBUTION_MASK broadcast_mask;
if (pdu && (pdu_len >= BACNET_IP_BDT_ENTRY_SIZE)) {
if (bdt_entry) {
len = bvlc_decode_address(
&pdu[offset], pdu_len - offset, &bdt_entry->dest_address);
if (len > 0) {
offset += len;
len = bvlc_decode_broadcast_distribution_mask(
&pdu[offset], pdu_len - offset, &bdt_entry->broadcast_mask);
if (pdu) {
len = bvlc_decode_address(
&pdu[pdu_len], pdu_size - pdu_len, &dest_address);
if (len > 0) {
pdu_len += len;
if (bdt_entry) {
bvlc_address_copy(&bdt_entry->dest_address, &dest_address);
}
if (len > 0) {
offset += len;
bytes_consumed = offset;
} else {
return 0;
}
len = bvlc_decode_broadcast_distribution_mask(
&pdu[pdu_len], pdu_size - pdu_len, &broadcast_mask);
if (len > 0) {
pdu_len += len;
if (bdt_entry) {
bvlc_broadcast_distribution_mask_copy(
&bdt_entry->broadcast_mask, &broadcast_mask);
}
} else {
return 0;
}
}
return bytes_consumed;
return pdu_len;
}
/**
+20
View File
@@ -272,6 +272,12 @@ bool bvlc_broadcast_distribution_table_entry_append(
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_list,
const BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry);
BACNET_STACK_EXPORT
bool bvlc_broadcast_distribution_table_entry_insert(
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_list,
const BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry,
uint16_t array_index);
BACNET_STACK_EXPORT
bool bvlc_broadcast_distribution_table_entry_set(
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry,
@@ -380,6 +386,9 @@ void bvlc_foreign_device_table_maintenance_timer(
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_list, uint16_t seconds);
BACNET_STACK_EXPORT
void bvlc_foreign_device_table_valid_clear(
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_list);
BACNET_STACK_EXPORT
uint16_t bvlc_foreign_device_table_valid_count(
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_list);
@@ -412,6 +421,12 @@ bool bvlc_foreign_device_table_entry_add(
const BACNET_IP_ADDRESS *ip_address,
uint16_t ttl_seconds);
BACNET_STACK_EXPORT
bool bvlc_foreign_device_table_entry_insert(
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_list,
const BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_entry,
uint16_t array_index);
BACNET_STACK_EXPORT
int bvlc_encode_foreign_device_table_entry(
uint8_t *pdu,
@@ -435,6 +450,11 @@ int bvlc_foreign_device_table_encode(
uint8_t *apdu,
uint16_t apdu_size,
const BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_head);
int bvlc_foreign_device_table_decode(
const uint8_t *apdu,
uint16_t apdu_len,
BACNET_ERROR_CODE *error_code,
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_head);
BACNET_STACK_EXPORT
int bvlc_encode_read_foreign_device_table(uint8_t *pdu, uint16_t pdu_size);
+38
View File
@@ -321,6 +321,38 @@ static int bbmd6_register_as_foreign_device(void)
return retval;
}
/**
* @brief
*
* @param instance
*/
static void bip_network_port_activate_changes(uint32_t instance)
{
#if defined(BACDL_BIP)
bvlc_bbmd_accept_fd_registrations_set(
Network_Port_BBMD_Accept_FD_Registrations(instance));
#else
/* if we are not using BIP, then we don't have any changes to discard */
(void)instance;
#endif
}
/**
* @brief
*
* @param instance
*/
static void bip_network_port_discard_changes(uint32_t instance)
{
#if defined(BACDL_BIP)
Network_Port_BBMD_Accept_FD_Registrations_Set(
instance, bvlc_bbmd_accept_fd_registrations());
#else
/* if we are not using BIP, then we don't have any changes to discard */
(void)instance;
#endif
}
/**
* Datalink network port object settings
*/
@@ -401,6 +433,8 @@ static void dlenv_network_port_bip_init(uint32_t instance)
instance, addr0, addr1, addr2, addr3);
Network_Port_Remote_BBMD_BIP_Port_Set(instance, BBMD_Address.port);
Network_Port_Remote_BBMD_BIP_Lifetime_Set(instance, BBMD_TTL_Seconds);
Network_Port_BBMD_Accept_FD_Registrations_Set(
instance, bvlc_bbmd_accept_fd_registrations());
#endif
/* common NP data */
Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED);
@@ -411,6 +445,10 @@ static void dlenv_network_port_bip_init(uint32_t instance)
/* last thing - clear pending changes - we don't want to set these
since they are already set */
Network_Port_Changes_Pending_Set(instance, false);
Network_Port_Changes_Pending_Activate_Callback_Set(
instance, bip_network_port_activate_changes);
Network_Port_Changes_Pending_Discard_Callback_Set(
instance, bip_network_port_discard_changes);
}
/**
+142 -18
View File
@@ -97,13 +97,14 @@ static void test_BVLC_Result_Code(uint16_t result_code)
int len = 0, test_len = 0;
len = bvlc_encode_result(pdu, sizeof(pdu), result_code);
zassert_equal(len, 6, NULL);
zassert_equal(len, 6, "result-code=%s", bvlc_result_code_name(result_code));
test_len = test_BVLC_Header(pdu, len, &message_type, &length);
zassert_equal(test_len, 4, NULL);
zassert_equal(message_type, BVLC_RESULT, NULL);
zassert_equal(length, 6, NULL);
test_len += bvlc_decode_result(&pdu[4], length - 4, &test_result_code);
zassert_equal(len, test_len, NULL);
zassert_equal(
len, test_len, "result-code=%s", bvlc_result_code_name(result_code));
zassert_equal(result_code, test_result_code, NULL);
}
@@ -493,6 +494,9 @@ static void test_BVLC_Broadcast_Distribution_Table_Encode(void)
uint16_t i = 0;
uint16_t count = 0;
uint16_t test_count = 0;
uint8_t message_type = 0;
uint16_t message_length = 0;
int test_message_len = 0;
bool status = false;
BACNET_ERROR_CODE error_code;
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY bdt_list[5] = { 0 };
@@ -540,6 +544,25 @@ static void test_BVLC_Broadcast_Distribution_Table_Encode(void)
&bdt_list[i], &test_bdt_list[i]);
zassert_false(status, NULL);
}
apdu_len = bvlc_encode_read_broadcast_distribution_table_ack(
apdu, sizeof(apdu), &bdt_list[0]);
zassert_not_equal(apdu_len, 0, NULL);
test_message_len =
test_BVLC_Header(apdu, apdu_len, &message_type, &message_length);
zassert_equal(test_message_len, 4, NULL);
zassert_equal(message_type, BVLC_READ_BROADCAST_DIST_TABLE_ACK, NULL);
test_apdu_len = bvlc_decode_read_broadcast_distribution_table_ack(
&apdu[test_message_len], apdu_len, &test_bdt_list[0]);
zassert_equal(
test_message_len + test_apdu_len, apdu_len,
"apdu_len=%u test_apdu_len=%u", apdu_len, test_apdu_len);
count = bvlc_broadcast_distribution_table_count(&test_bdt_list[0]);
zassert_equal(test_count, count, NULL);
for (i = 0; i < count; i++) {
status = bvlc_broadcast_distribution_table_entry_different(
&bdt_list[i], &test_bdt_list[i]);
zassert_false(status, NULL);
}
}
static void test_BVLC_Write_Broadcast_Distribution_Table_Message(
@@ -552,6 +575,8 @@ static void test_BVLC_Write_Broadcast_Distribution_Table_Message(
BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *test_bdt_list = NULL;
uint16_t i = 0;
uint16_t count = 0;
BACNET_IP_ADDRESS dest_address = { { 1, 1, 1, 1 }, 47808 };
bool status = false;
count = bvlc_broadcast_distribution_table_valid_count(bdt_list);
test_bdt_list =
@@ -573,6 +598,15 @@ static void test_BVLC_Write_Broadcast_Distribution_Table_Message(
test_BVLC_Broadcast_Distribution_Table_Entry(
&bdt_list[i], &test_bdt_list[i]);
}
status = bvlc_broadcast_distribution_table_entry_forward_address(
&dest_address, bdt_list);
zassert_true(status, NULL);
status = bvlc_broadcast_distribution_table_entry_forward_address(
&dest_address, NULL);
zassert_false(status, NULL);
status =
bvlc_broadcast_distribution_table_entry_forward_address(NULL, NULL);
zassert_false(status, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
@@ -622,6 +656,10 @@ static void test_BVLC_Write_Broadcast_Distribution_Table(void)
zassert_true(status, NULL);
}
test_BVLC_Write_Broadcast_Distribution_Table_Message(&bdt_list[0]);
/* cleanup */
bvlc_broadcast_distribution_table_valid_clear(&bdt_list[0]);
test_count = bvlc_broadcast_distribution_table_valid_count(&bdt_list[0]);
zassert_equal(test_count, 0, NULL);
}
static void test_BVLC_Read_Foreign_Device_Table_Ack_Message(
@@ -654,33 +692,28 @@ static void test_BVLC_Read_Foreign_Device_Table_Ack_Message(
}
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bvlc_tests, test_BVLC_Read_Foreign_Device_Table_Ack)
#else
static void test_BVLC_Read_Foreign_Device_Table_Ack(void)
#endif
static int test_BVLC_Foreign_Device_Table_Setup(
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_list, int count)
{
uint16_t i = 0;
uint16_t count = 0;
uint16_t test_count = 0;
uint16_t test_port_start = 0xBAC1;
uint16_t ttl_seconds = 12345;
bool status = false;
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY fdt_list[5] = { 0 };
BACNET_IP_ADDRESS dest_address = { 0 };
status = bvlc_address_from_ascii(&dest_address, "192.168.0.1");
zassert_true(status, NULL);
/* configure a FDT entry */
count = sizeof(fdt_list) / sizeof(fdt_list[0]);
bvlc_foreign_device_table_link_array(fdt_list, count);
for (i = 0; i < count; i++) {
dest_address.port = test_port_start + i;
status = bvlc_foreign_device_table_entry_add(
&fdt_list[0], &dest_address, 12345);
fdt_list, &dest_address, ttl_seconds);
zassert_true(status, NULL);
/* add again should only update TTL */
status = bvlc_foreign_device_table_entry_add(
&fdt_list[0], &dest_address, 12345);
fdt_list, &dest_address, ttl_seconds);
zassert_true(status, NULL);
}
test_count = bvlc_foreign_device_table_count(fdt_list);
@@ -690,15 +723,100 @@ static void test_BVLC_Read_Foreign_Device_Table_Ack(void)
}
test_count = bvlc_foreign_device_table_valid_count(fdt_list);
zassert_equal(test_count, count, NULL);
return count;
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bvlc_tests, test_BVLC_Read_Foreign_Device_Table_Ack)
#else
static void test_BVLC_Read_Foreign_Device_Table_Ack(void)
#endif
{
uint16_t count = 0;
uint16_t test_count = 0;
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY fdt_list[5] = { 0 };
count = sizeof(fdt_list) / sizeof(fdt_list[0]);
test_count = test_BVLC_Foreign_Device_Table_Setup(fdt_list, count);
zassert_equal(test_count, count, NULL);
if (test_count != count) {
printf("size=%u count=%u\n", count, test_count);
}
test_count = bvlc_foreign_device_table_valid_count(fdt_list);
zassert_equal(test_count, count, NULL);
test_BVLC_Read_Foreign_Device_Table_Ack_Message(fdt_list);
/* cleanup */
for (i = 0; i < count; i++) {
dest_address.port = test_port_start + i;
status =
bvlc_foreign_device_table_entry_delete(&fdt_list[0], &dest_address);
zassert_true(status, NULL);
bvlc_foreign_device_table_valid_clear(&fdt_list[0]);
test_count = bvlc_foreign_device_table_valid_count(fdt_list);
zassert_equal(test_count, 0, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bvlc_tests, test_BVLC_Network_Port_Foreign_Device)
#else
static void test_BVLC_Network_Port_Foreign_Device(void)
#endif
{
uint8_t apdu[480] = { 0 };
int apdu_len = 0, test_apdu_len = 0;
BACNET_ERROR_CODE error_code;
uint16_t i = 0;
uint16_t count = 0;
uint16_t test_count = 0;
uint16_t test_ttl_seconds = 0;
int len;
bool status = false;
BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY fdt_list[5] = { 0 },
test_fdt_list[5] = { 0 },
test_entry = { 0 };
count = sizeof(fdt_list) / sizeof(fdt_list[0]);
test_count = test_BVLC_Foreign_Device_Table_Setup(fdt_list, count);
zassert_equal(test_count, count, NULL);
if (test_count != count) {
printf("size=%u count=%u\n", count, test_count);
}
test_count = bvlc_foreign_device_table_valid_count(fdt_list);
zassert_equal(test_count, count, NULL);
/* test the encode/decode pair */
apdu_len = bvlc_foreign_device_table_encode(apdu, sizeof(apdu), fdt_list);
zassert_not_equal(apdu_len, 0, NULL);
test_count = sizeof(test_fdt_list) / sizeof(test_fdt_list[0]);
bvlc_foreign_device_table_link_array(&test_fdt_list[0], test_count);
test_apdu_len = bvlc_foreign_device_table_decode(
&apdu[0], apdu_len, &error_code, &test_fdt_list[0]);
zassert_equal(test_apdu_len, apdu_len, NULL);
count = bvlc_foreign_device_table_count(&test_fdt_list[0]);
zassert_equal(test_count, count, NULL);
/* test timer */
test_ttl_seconds = fdt_list[0].ttl_seconds_remaining;
bvlc_foreign_device_table_maintenance_timer(fdt_list, 60);
test_ttl_seconds -= 60;
for (i = 0; i < count; i++) {
zassert_equal(
fdt_list[i].ttl_seconds_remaining, test_ttl_seconds,
"entry[%u].seconds_remaining=%u expected=%u", i,
fdt_list[i].ttl_seconds_remaining, test_ttl_seconds);
}
bvlc_foreign_device_table_maintenance_timer(fdt_list, 60);
test_ttl_seconds -= 60;
for (i = 0; i < count; i++) {
zassert_equal(
fdt_list[i].ttl_seconds_remaining, test_ttl_seconds,
"entry[%u].seconds_remaining=%u expected=%u", i,
fdt_list[i].ttl_seconds_remaining, test_ttl_seconds);
}
status = bvlc_foreign_device_table_entry_copy(&test_entry, &fdt_list[0]);
zassert_true(status, NULL);
status =
bvlc_foreign_device_table_entry_different(&fdt_list[0], &test_entry);
zassert_false(status, NULL);
len = bvlc_encode_read_foreign_device_table(NULL, 0);
zassert_equal(len, 0, NULL);
/* cleanup */
bvlc_foreign_device_table_valid_clear(&fdt_list[0]);
test_count = bvlc_foreign_device_table_valid_count(fdt_list);
zassert_equal(test_count, 0, NULL);
}
@@ -811,6 +929,9 @@ static void test_BVLC_Address_Get_Set(void)
(unsigned)test_octet2, (unsigned)test_octet3);
}
zassert_false(status, NULL);
bvlc_address_from_network(&dst, 0x7F000000);
status = bvlc_address_different(&dst, &src);
zassert_false(status, NULL);
/* BACnet to IPv4 address conversions */
status = bvlc_address_port_from_ascii(&src, "192.168.0.1", "0xBAC0");
zassert_true(status, NULL);
@@ -848,6 +969,8 @@ static void test_BVLC_Address_Get_Set(void)
zassert_equal(octet1, test_octet1, NULL);
zassert_equal(octet2, test_octet2, NULL);
zassert_equal(octet3, test_octet3, NULL);
status = bvlc_address_mask(&dst, &src, &mask);
zassert_true(status, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
@@ -907,7 +1030,8 @@ void test_main(void)
ztest_unit_test(test_BVLC_Secure_BVLL),
ztest_unit_test(test_BVLC_Address_Copy),
ztest_unit_test(test_BVLC_Address_Get_Set),
ztest_unit_test(test_BVLC_BBMD_Address));
ztest_unit_test(test_BVLC_BBMD_Address),
ztest_unit_test(test_BVLC_Network_Port_Foreign_Device));
ztest_run_test_suite(bvlc_tests);
}