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
+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);
}
/**