From 541f4024fb0a398dca24b55e4eed8fe3bdf9723f Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 3 Aug 2021 09:55:01 -0500 Subject: [PATCH] Bugfix/network port object bdt required (#184) * Added BDT encoding to network port object. * Added BDT encoding rules from clause 21 * Added FDT encoding to network port object. * added more unit tests for BVLC * Fix network port and device object unit tests * fix router build for bvlc dependencies * fix router build for bvlc dependencies * fix network port MSTP compile Co-authored-by: Steve Karg Co-authored-by: Steve Karg --- apps/router/Makefile | 3 + src/bacnet/basic/bbmd/h_bbmd.c | 9 + src/bacnet/basic/bbmd/h_bbmd.h | 3 + src/bacnet/basic/object/netport.c | 139 +++++++- src/bacnet/basic/object/netport.h | 13 + src/bacnet/datalink/bvlc.c | 334 ++++++++++++++++++ src/bacnet/datalink/bvlc.h | 17 +- src/bacnet/datalink/dlenv.c | 7 + .../bacnet/basic/object/device/CMakeLists.txt | 1 + .../basic/object/netport/CMakeLists.txt | 1 + test/bacnet/basic/object/netport/src/main.c | 30 +- test/bacnet/datalink/bvlc/src/main.c | 57 +++ 12 files changed, 591 insertions(+), 23 deletions(-) diff --git a/apps/router/Makefile b/apps/router/Makefile index c52fcede..97173d58 100644 --- a/apps/router/Makefile +++ b/apps/router/Makefile @@ -35,7 +35,10 @@ SRCS = main.c \ ${BACNET_SOURCE_DIR}/indtext.c \ ${BACNET_SOURCE_DIR}/basic/sys/ringbuf.c \ ${BACNET_SOURCE_DIR}/datalink/crc.c \ + ${BACNET_SOURCE_DIR}/bacdcode.c \ ${BACNET_SOURCE_DIR}/bacint.c \ + ${BACNET_SOURCE_DIR}/bacreal.c \ + ${BACNET_SOURCE_DIR}/bacstr.c \ ${BACNET_SOURCE_DIR}/npdu.c \ ${BACNET_SOURCE_DIR}/bacaddr.c \ mstpmodule.c \ diff --git a/src/bacnet/basic/bbmd/h_bbmd.c b/src/bacnet/basic/bbmd/h_bbmd.c index 412fc2b9..90c32c0a 100644 --- a/src/bacnet/basic/bbmd/h_bbmd.c +++ b/src/bacnet/basic/bbmd/h_bbmd.c @@ -1257,6 +1257,15 @@ void bvlc_set_function_code(uint8_t function_code) } #if BBMD_ENABLED +/** + * @brief Get handle to foreign device table (FDT). + * @return pointer to first entry of foreign device table + */ +BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *bvlc_fdt_list(void) +{ + return &FD_Table[0]; +} + /** * @brief Get handle to broadcast distribution table (BDT). * @return pointer to first entry of broadcast distribution table diff --git a/src/bacnet/basic/bbmd/h_bbmd.h b/src/bacnet/basic/bbmd/h_bbmd.h index 16214e84..266bc39f 100644 --- a/src/bacnet/basic/bbmd/h_bbmd.h +++ b/src/bacnet/basic/bbmd/h_bbmd.h @@ -113,6 +113,9 @@ BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bvlc_bdt_list(void); BACNET_STACK_EXPORT void bvlc_bdt_list_clear(void); +/* Get foreign device table list */ +BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *bvlc_fdt_list(void); + /* Backup broadcast distribution table to a file. * Filename is the BBMD_BACKUP_FILE constant */ diff --git a/src/bacnet/basic/object/netport.c b/src/bacnet/basic/object/netport.c index a7dab6a9..fdad2808 100644 --- a/src/bacnet/basic/object/netport.c +++ b/src/bacnet/basic/object/netport.c @@ -44,10 +44,15 @@ #include "bacnet/bacdcode.h" #include "bacnet/npdu.h" #include "bacnet/apdu.h" +#include "bacnet/datalink/datalink.h" #include "bacnet/basic/object/device.h" /* me */ #include "bacnet/basic/object/netport.h" +#ifndef BBMD_ENABLED +#define BBMD_ENABLED 1 +#endif + #define BIP_DNS_MAX 3 struct bacnet_ipv4_port { uint8_t IP_Address[4]; @@ -63,6 +68,8 @@ struct bacnet_ipv4_port { bool IP_NAT_Traversal; uint32_t IP_Global_Address[4]; bool BBMD_Accept_FD_Registrations; + void *BBMD_BD_Table; + void *BBMD_FD_Table; }; #define IPV6_ADDR_SIZE 16 @@ -128,7 +135,7 @@ static const int MSTP_Port_Properties_Optional[] = { PROP_MAC_ADDRESS, static const int BIP_Port_Properties_Optional[] = { PROP_MAC_ADDRESS, PROP_BACNET_IP_MODE, PROP_IP_ADDRESS, PROP_BACNET_IP_UDP_PORT, PROP_IP_SUBNET_MASK, PROP_IP_DEFAULT_GATEWAY, PROP_IP_DNS_SERVER, -#if defined(BBMD_ENABLED) +#if defined(BACDL_BIP) && BBMD_ENABLED PROP_BBMD_ACCEPT_FD_REGISTRATIONS, PROP_BBMD_BROADCAST_DISTRIBUTION_TABLE, PROP_BBMD_FOREIGN_DEVICE_TABLE, #endif @@ -1281,6 +1288,113 @@ bool Network_Port_BBMD_Accept_FD_Registrations_Set( return status; } +/** + * For a given object instance-number, returns the BBMD-BD-Table head + * property value + * + * @param object_instance - object-instance number of the object + * + * @return BBMD-Accept-FD-Registrations property value + */ +void *Network_Port_BBMD_BD_Table(uint32_t object_instance) +{ + void *bdt_head = NULL; + unsigned index = 0; + struct bacnet_ipv4_port *ipv4 = NULL; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + ipv4 = &Object_List[index].Network.IPv4; + bdt_head = ipv4->BBMD_BD_Table; + } + + return bdt_head; +} + +/** + * For a given object instance-number, sets the BBMD-BD-Table head + * property value + * + * @param object_instance - object-instance number of the object + * @param bdt_head - Broadcast Distribution Table linked list head + * + * @return true if the Broadcast Distribution Table linked list head + * property value was set + */ +bool Network_Port_BBMD_BD_Table_Set( + uint32_t object_instance, + void *bdt_head) +{ + bool status = false; + unsigned index = 0; + struct bacnet_ipv4_port *ipv4 = NULL; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + ipv4 = &Object_List[index].Network.IPv4; + if (bdt_head != ipv4->BBMD_BD_Table) { + ipv4->BBMD_BD_Table = bdt_head; + Object_List[index].Changes_Pending = true; + } + status = true; + } + + return status; +} + +/** + * For a given object instance-number, returns the BBMD-FD-Table head + * property value + * + * @param object_instance - object-instance number of the object + * + * @return BBMD-Accept-FD-Registrations property value + */ +void *Network_Port_BBMD_FD_Table(uint32_t object_instance) +{ + void *fdt_head = NULL; + unsigned index = 0; + struct bacnet_ipv4_port *ipv4 = NULL; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + ipv4 = &Object_List[index].Network.IPv4; + fdt_head = ipv4->BBMD_FD_Table; + } + + return fdt_head; +} + +/** + * For a given object instance-number, sets the BBMD-FD-Table head + * property value + * + * @param object_instance - object-instance number of the object + * @param fdt_head - Foreign Device Table linked list head + * + * @return true if the BBMD-Accept-FD-Registrations property value was set + */ +bool Network_Port_BBMD_FD_Table_Set( + uint32_t object_instance, + void *fdt_head) +{ + bool status = false; + unsigned index = 0; + struct bacnet_ipv4_port *ipv4 = NULL; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + ipv4 = &Object_List[index].Network.IPv4; + if (fdt_head != ipv4->BBMD_FD_Table) { + ipv4->BBMD_FD_Table = fdt_head; + Object_List[index].Changes_Pending = true; + } + status = true; + } + + return status; +} + /** * For a given object instance-number, gets the BACnet/IP UDP Port number * Note: depends on Network_Type being set to PORT_TYPE_BIP for this object @@ -2003,17 +2117,21 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = BACNET_STATUS_ERROR; } break; -#if defined(BBMD_ENABLED) +#if defined(BACDL_BIP) && BBMD_ENABLED case PROP_BBMD_ACCEPT_FD_REGISTRATIONS: apdu_len = encode_application_boolean(&apdu[0], Network_Port_BBMD_Accept_FD_Registrations( rpdata->object_instance)); break; case PROP_BBMD_BROADCAST_DISTRIBUTION_TABLE: + apdu_len = bvlc_broadcast_distribution_table_encode(&apdu[0], + rpdata->application_data_len, + Network_Port_BBMD_BD_Table(rpdata->object_instance)); + break; case PROP_BBMD_FOREIGN_DEVICE_TABLE: - rpdata->error_class = ERROR_CLASS_PROPERTY; - rpdata->error_code = ERROR_CODE_READ_ACCESS_DENIED; - apdu_len = BACNET_STATUS_ERROR; + apdu_len = bvlc_foreign_device_table_encode(&apdu[0], + rpdata->application_data_len, + Network_Port_BBMD_FD_Table(rpdata->object_instance)); break; #endif case PROP_BACNET_IPV6_MODE: @@ -2258,22 +2376,23 @@ bool Network_Port_Read_Range( #if defined(BACDL_MSTP) case PROP_MAX_MASTER: case PROP_MAX_INFO_FRAMES: -#elif defined(BACDL_BIP) +#endif +#if defined(BACDL_BIP) case PROP_BACNET_IP_MODE: case PROP_IP_ADDRESS: case PROP_BACNET_IP_UDP_PORT: case PROP_IP_SUBNET_MASK: case PROP_IP_DEFAULT_GATEWAY: case PROP_IP_DNS_SERVER: -#if defined(BBMD_ENABLED) - case PROP_BBMD_ACCEPT_FD_REGISTRATIONS: #endif +#if defined(BACDL_BIP) && BBMD_ENABLED + case PROP_BBMD_ACCEPT_FD_REGISTRATIONS: #endif pRequest->error_class = ERROR_CLASS_SERVICES; pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; break; case PROP_BBMD_BROADCAST_DISTRIBUTION_TABLE: -#if defined(BACDL_BIP) && defined(BBMD_ENABLED) +#if defined(BACDL_BIP) && BBMD_ENABLED pInfo->RequestTypes = RR_BY_POSITION; pInfo->Handler = Network_Port_Read_Range_BDT; status = true; @@ -2283,7 +2402,7 @@ bool Network_Port_Read_Range( #endif break; case PROP_BBMD_FOREIGN_DEVICE_TABLE: -#if defined(BACDL_BIP) && defined(BBMD_ENABLED) +#if defined(BACDL_BIP) && BBMD_ENABLED pInfo->RequestTypes = RR_BY_POSITION; pInfo->Handler = Network_Port_Read_Range_FDT; status = true; diff --git a/src/bacnet/basic/object/netport.h b/src/bacnet/basic/object/netport.h index 20d0abcd..45afa784 100644 --- a/src/bacnet/basic/object/netport.h +++ b/src/bacnet/basic/object/netport.h @@ -245,6 +245,19 @@ extern "C" { uint32_t object_instance, bool value); + BACNET_STACK_EXPORT + void *Network_Port_BBMD_BD_Table(uint32_t object_instance); + BACNET_STACK_EXPORT + bool Network_Port_BBMD_BD_Table_Set( + uint32_t object_instance, + void *bdt_head); + BACNET_STACK_EXPORT + void *Network_Port_BBMD_FD_Table(uint32_t object_instance); + BACNET_STACK_EXPORT + bool Network_Port_BBMD_FD_Table_Set( + uint32_t object_instance, + void *fdt_head); + BACNET_STACK_EXPORT BACNET_IP_MODE Network_Port_BIP6_Mode( uint32_t object_instance); diff --git a/src/bacnet/datalink/bvlc.c b/src/bacnet/datalink/bvlc.c index 7b8d48f5..792d5d5d 100644 --- a/src/bacnet/datalink/bvlc.c +++ b/src/bacnet/datalink/bvlc.c @@ -520,6 +520,217 @@ bool bvlc_broadcast_distribution_table_entry_forward_address( return status; } +/** + * @brief Encode the Broadcast-Distribution-Table for Network Port object + * + * BACnetLIST of BACnetBDTEntry + * + * BACnetBDTEntry ::= SEQUENCE { + * bbmd-address [0] BACnetHostNPort, + * BACnetHostNPort ::= SEQUENCE { + * host [0] BACnetHostAddress, + * BACnetHostAddress ::= CHOICE { + * ip-address [1] OCTET STRING, -- 4 octets for B/IP + * } + * port [1] Unsigned16 + * } + * broadcast-mask [1] OCTET STRING + * } + * + * @param apdu - the APDU buffer + * @param apdu_size - the APDU buffer size + * @param bdt_head - head of the BDT linked list + * @return length of the APDU buffer + */ +int bvlc_broadcast_distribution_table_encode(uint8_t *apdu, + uint16_t apdu_size, + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_head) +{ + int len = 0; + int apdu_len = 0; + int entry_size = 0; + BACNET_OCTET_STRING octet_string; + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry; + + bdt_entry = bdt_head; + while (bdt_entry) { + if (bdt_entry->valid) { + /* bbmd-address [0] BACnetHostNPort - opening */ + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + /* host [0] BACnetHostAddress - opening */ + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + /* CHOICE - ip-address [1] OCTET STRING */ + octetstring_init(&octet_string, + &bdt_entry->dest_address.address[0], IP_ADDRESS_MAX); + len = encode_context_octet_string(&apdu[apdu_len], 1, + &octet_string); + apdu_len += len; + /* host [0] BACnetHostAddress - closing */ + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + /* port [1] Unsigned16 */ + len = encode_context_unsigned( + &apdu[apdu_len], 1, bdt_entry->dest_address.port); + apdu_len += len; + /* bbmd-address [0] BACnetHostNPort - closing */ + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + /* broadcast-mask [1] OCTET STRING */ + octetstring_init(&octet_string, + &bdt_entry->broadcast_mask.address[0], IP_ADDRESS_MAX); + len = encode_context_octet_string(&apdu[apdu_len], 1, + &octet_string); + apdu_len += len; + } + if (!entry_size) { + entry_size = apdu_len; + } + /* next entry */ + bdt_entry = bdt_entry->next; + if ((apdu_len + entry_size) > apdu_size) { + /* check for available space */ + break; + } + } + + return apdu_len; +} + +/** + * @brief Decode the Broadcast-Distribution-Table for Network Port object + * @param apdu - the APDU buffer + * @param apdu_len - the APDU buffer length + * @param bdt_head - head of a BDT linked list + * @return length of the APDU buffer decoded, or ERROR, REJECT, or ABORT + */ +int bvlc_broadcast_distribution_table_decode(uint8_t *apdu, + uint16_t apdu_len, + BACNET_ERROR_CODE *error_code, + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_head) +{ + int len = 0; + BACNET_OCTET_STRING octet_string = { 0 }; + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry = NULL; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + BACNET_UNSIGNED_INTEGER unsigned_value = 0; + + /* default reject code */ + if (error_code) { + *error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; + } + /* check for value pointers */ + if ((apdu_len == 0) || (!apdu)) { + return BACNET_STATUS_REJECT; + } + bdt_entry = bdt_head; + while (bdt_entry) { + /* bbmd-address [0] BACnetHostNPort - opening */ + if (!decode_is_opening_tag_number(&apdu[len++], 0)) { + if (error_code) { + *error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + if (len > apdu_len) { + return BACNET_STATUS_REJECT; + } + /* host [0] BACnetHostAddress - opening */ + if (!decode_is_opening_tag_number(&apdu[len++], 0)) { + if (error_code) { + *error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + if (len > apdu_len) { + return BACNET_STATUS_REJECT; + } + /* CHOICE - ip-address [1] OCTET STRING */ + len += decode_tag_number_and_value( + &apdu[len], &tag_number, &len_value_type); + if (tag_number != 1) { + if (error_code) { + *error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + len += decode_octet_string(&apdu[len], len_value_type, + &octet_string); + if (len > apdu_len) { + return BACNET_STATUS_REJECT; + } + octetstring_copy_value(&bdt_entry->dest_address.address[0], + IP_ADDRESS_MAX, &octet_string); + /* host [0] BACnetHostAddress - closing */ + if (!decode_is_closing_tag_number(&apdu[len++], 0)) { + if (error_code) { + *error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + if (len > apdu_len) { + return BACNET_STATUS_REJECT; + } + /* port [1] Unsigned16 */ + len += decode_tag_number_and_value( + &apdu[len], &tag_number, &len_value_type); + if (tag_number != 1) { + if (error_code) { + *error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value); + if (len > apdu_len) { + return BACNET_STATUS_REJECT; + } + if (unsigned_value <= UINT16_MAX) { + bdt_entry->dest_address.port = unsigned_value; + } else { + if (error_code) { + *error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE; + } + return BACNET_STATUS_REJECT; + } + /* bbmd-address [0] BACnetHostNPort - closing */ + if (!decode_is_closing_tag_number(&apdu[len++], 0)) { + if (error_code) { + *error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + if (len > apdu_len) { + return BACNET_STATUS_REJECT; + } + /* broadcast-mask [1] OCTET STRING */ + len += decode_tag_number_and_value( + &apdu[len], &tag_number, &len_value_type); + if (tag_number != 1) { + if (error_code) { + *error_code = ERROR_CODE_REJECT_INVALID_TAG; + } + return BACNET_STATUS_REJECT; + } + if (len > apdu_len) { + return BACNET_STATUS_REJECT; + } + len += decode_octet_string(&apdu[len], len_value_type, + &octet_string); + if (len > apdu_len) { + return BACNET_STATUS_REJECT; + } + octetstring_copy_value(&bdt_entry->broadcast_mask.address[0], + IP_ADDRESS_MAX, &octet_string); + bdt_entry->valid = true; + /* next entry */ + bdt_entry = bdt_entry->next; + } + + return apdu_len; +} + /** * @brief J.2.2 Write-Broadcast-Distribution-Table: encode * @@ -906,6 +1117,69 @@ int bvlc_decode_register_foreign_device( return bytes_consumed; } +/** + * @brief Encode the Foreign_Device-Table for Network Port object + * + * BACnetLIST of BACnetFDTEntry + * + * BACnetFDTEntry ::= SEQUENCE { + * bacnetip-address [0] OCTET STRING, -- 6-octet B/IP registrant address + * time-to-live [1] Unsigned16, -- time to live in seconds + * remaining-time-to-live [2] Unsigned16 -- remaining time in seconds + * } + * + * @param apdu - the APDU buffer + * @param apdu_size - the APDU buffer size + * @param fdt_head - head of the BDT linked list + * @return length of the APDU buffer + */ +int bvlc_foreign_device_table_encode(uint8_t *apdu, + uint16_t apdu_size, + BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_head) +{ + int len = 0; + int apdu_len = 0; + int entry_size = 0; + BACNET_OCTET_STRING octet_string = { 0 }; + BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_entry; + + fdt_entry = fdt_head; + while (fdt_entry) { + if (fdt_entry->valid) { + /* bacnetip-address [0] OCTET STRING */ + len = bvlc_encode_address( + octetstring_value(&octet_string), + octetstring_capacity(&octet_string), + &fdt_entry->dest_address); + octetstring_truncate(&octet_string, len); + len = encode_context_octet_string( + &apdu[apdu_len], 0, &octet_string); + apdu_len += len; + /* time-to-live [1] Unsigned16 */ + len = encode_context_unsigned( + &apdu[apdu_len], 1, fdt_entry->ttl_seconds); + apdu_len += len; + /* remaining-time-to-live [2] Unsigned16 */ + len = encode_context_unsigned( + &apdu[apdu_len], 2, fdt_entry->ttl_seconds_remaining); + apdu_len += len; + } + if (!entry_size) { + entry_size = apdu_len; + } + /* next entry */ + fdt_entry = fdt_entry->next; + if ((apdu_len + entry_size) > apdu_size) { + /* check for available space */ + break; + } + } + + return apdu_len; +} + + + /** * @brief J.2.7 Read-Foreign-Device-Table: encode * @@ -2806,6 +3080,63 @@ static void test_BVLC_Write_Broadcast_Distribution_Table_Message(Test *pTest, } } +static void test_BVLC_Broadcast_Distribution_Table_Encode(Test *pTest) +{ + uint8_t apdu[480] = { 0 }; + uint16_t apdu_len = 0; + uint16_t test_apdu_len = 0; + uint16_t i = 0; + uint16_t count = 0; + uint16_t test_count = 0; + bool status = false; + BACNET_ERROR_CODE error_code; + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY bdt_list[5] = { 0 }; + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY bdt_entry = { 0 }; + BACNET_IP_ADDRESS dest_address = { 0 }; + BACNET_IP_BROADCAST_DISTRIBUTION_MASK broadcast_mask = { 0 }; + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY test_bdt_list[5] = { 0 }; + + /* configure a BDT entry */ + count = sizeof(bdt_list) / sizeof(bdt_list[0]); + bvlc_broadcast_distribution_table_link_array(&bdt_list[0], count); + for (i = 0; i < count; i++) { + status = bvlc_address_port_from_ascii( + &dest_address, "192.168.0.255", "0xBAC0"); + ct_test(pTest, status); + dest_address.port += i; + broadcast_mask.address[0] = 255; + broadcast_mask.address[1] = 255; + broadcast_mask.address[2] = 255; + broadcast_mask.address[3] = 255; + status = bvlc_broadcast_distribution_table_entry_set( + &bdt_entry, &dest_address, &broadcast_mask); + ct_test(pTest, status); + status = bvlc_broadcast_distribution_table_entry_append( + &bdt_list[0], &bdt_entry); + ct_test(pTest, status); + } + test_count = bvlc_broadcast_distribution_table_count(&bdt_list[0]); + if (test_count != count) { + printf("size=%u count=%u\n", count, test_count); + } + ct_test(pTest, test_count == count); + /* test the encode/decode pair */ + apdu_len = bvlc_broadcast_distribution_table_encode(&apdu[0], + sizeof(apdu), &bdt_list[0]); + test_count = sizeof(test_bdt_list) / sizeof(test_bdt_list[0]); + bvlc_broadcast_distribution_table_link_array(&test_bdt_list[0], test_count); + test_apdu_len = bvlc_broadcast_distribution_table_decode(&apdu[0], + apdu_len, &error_code, &test_bdt_list[0]); + ct_test(pTest, test_apdu_len == apdu_len); + count = bvlc_broadcast_distribution_table_count(&test_bdt_list[0]); + ct_test(pTest, test_count == count); + for (i = 0; i < count; i++) { + status = bvlc_broadcast_distribution_table_entry_different( + &bdt_list[i], &test_bdt_list[i]); + ct_test(pTest, !status); + } +} + static void test_BVLC_Write_Broadcast_Distribution_Table(Test *pTest) { uint8_t npdu[480] = { 0 }; @@ -3183,6 +3514,9 @@ void test_BVLC(Test *pTest) /* individual tests */ rc = ct_addTestFunction(pTest, test_BVLC_Result); assert(rc); + rc = ct_addTestFunction(pTest, + test_BVLC_Broadcast_Distribution_Table_Encode); + assert(rc); rc = ct_addTestFunction(pTest, test_BVLC_Write_Broadcast_Distribution_Table); assert(rc); diff --git a/src/bacnet/datalink/bvlc.h b/src/bacnet/datalink/bvlc.h index 0cd7cb5d..38351ce4 100644 --- a/src/bacnet/datalink/bvlc.h +++ b/src/bacnet/datalink/bvlc.h @@ -294,7 +294,6 @@ extern "C" { BACNET_IP_ADDRESS *dst, const BACNET_IP_ADDRESS *src, const BACNET_IP_BROADCAST_DISTRIBUTION_MASK *mask); - BACNET_STACK_EXPORT bool bvlc_broadcast_distribution_mask_from_host( BACNET_IP_BROADCAST_DISTRIBUTION_MASK *mask, uint32_t broadcast_mask); @@ -319,6 +318,17 @@ extern "C" { uint8_t *addr2, uint8_t *addr3); + BACNET_STACK_EXPORT + int bvlc_broadcast_distribution_table_decode(uint8_t *apdu, + uint16_t apdu_len, + BACNET_ERROR_CODE *error_code, + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_head); + + BACNET_STACK_EXPORT + int bvlc_broadcast_distribution_table_encode(uint8_t *apdu, + uint16_t apdu_size, + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_head); + BACNET_STACK_EXPORT int bvlc_encode_write_broadcast_distribution_table(uint8_t *pdu, uint16_t pdu_size, @@ -399,6 +409,11 @@ extern "C" { uint16_t pdu_len, BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *fdt_entry); + BACNET_STACK_EXPORT + int bvlc_foreign_device_table_encode(uint8_t *apdu, + uint16_t apdu_size, + 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); diff --git a/src/bacnet/datalink/dlenv.c b/src/bacnet/datalink/dlenv.c index fb5fe74a..5a4e0a36 100644 --- a/src/bacnet/datalink/dlenv.c +++ b/src/bacnet/datalink/dlenv.c @@ -41,6 +41,9 @@ /** @file dlenv.c Initialize the DataLink configuration. */ #if defined(BACDL_BIP) +#ifndef BBMD_ENABLED +#define BBMD_ENABLED 1 +#endif /* timer used to renew Foreign Device Registration */ static uint16_t BBMD_Timer_Seconds; /* BBMD variables */ @@ -251,6 +254,10 @@ static void dlenv_network_port_init(void) Network_Port_MAC_Address_Set(instance, &addr.address[0], 6); Network_Port_IP_Subnet_Prefix_Set(instance, bip_get_subnet_prefix()); Network_Port_Link_Speed_Set(instance, 0.0); +#if BBMD_ENABLED + Network_Port_BBMD_BD_Table_Set(instance, bvlc_bdt_list()); + Network_Port_BBMD_FD_Table_Set(instance, bvlc_fdt_list()); +#endif /* common NP data */ Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED); Network_Port_Out_Of_Service_Set(instance, false); diff --git a/test/bacnet/basic/object/device/CMakeLists.txt b/test/bacnet/basic/object/device/CMakeLists.txt index a9d544af..d7a03434 100644 --- a/test/bacnet/basic/object/device/CMakeLists.txt +++ b/test/bacnet/basic/object/device/CMakeLists.txt @@ -73,6 +73,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/basic/service/h_wp.c ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/basic/tsm/tsm.c + ${SRC_DIR}/bacnet/datalink/bvlc.c ${SRC_DIR}/bacnet/cov.c ${SRC_DIR}/bacnet/datetime.c ${SRC_DIR}/bacnet/dcc.c diff --git a/test/bacnet/basic/object/netport/CMakeLists.txt b/test/bacnet/basic/object/netport/CMakeLists.txt index 6a1646b5..717af062 100644 --- a/test/bacnet/basic/object/netport/CMakeLists.txt +++ b/test/bacnet/basic/object/netport/CMakeLists.txt @@ -43,6 +43,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bactext.c ${SRC_DIR}/bacnet/basic/sys/bigend.c + ${SRC_DIR}/bacnet/datalink/bvlc.c ${SRC_DIR}/bacnet/datetime.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/lighting.c diff --git a/test/bacnet/basic/object/netport/src/main.c b/test/bacnet/basic/object/netport/src/main.c index 0401922a..856b6916 100644 --- a/test/bacnet/basic/object/netport/src/main.c +++ b/test/bacnet/basic/object/netport/src/main.c @@ -58,12 +58,15 @@ static void test_network_port(void) rpdata.object_property = *pRequired; rpdata.array_index = BACNET_ARRAY_ALL; len = Network_Port_Read_Property(&rpdata); - zassert_not_equal(len, 0, NULL); - test_len = bacapp_decode_application_data(rpdata.application_data, - (uint8_t)rpdata.application_data_len, &value); - zassert_true(test_len >= 0, NULL); - if (test_len < 0) { - printf("\n"); + zassert_not_equal(len, BACNET_STATUS_ERROR, NULL); + if (len > 0) { + test_len = bacapp_decode_application_data( + rpdata.application_data, + (uint8_t)rpdata.application_data_len, &value); + zassert_true(test_len >= 0, NULL); + if (test_len < 0) { + printf("\n"); + } } pRequired++; } @@ -71,12 +74,15 @@ static void test_network_port(void) rpdata.object_property = *pOptional; rpdata.array_index = BACNET_ARRAY_ALL; len = Network_Port_Read_Property(&rpdata); - zassert_not_equal(len, 0, NULL); - test_len = bacapp_decode_application_data(rpdata.application_data, - (uint8_t)rpdata.application_data_len, &value); - zassert_true(test_len >= 0, NULL); - if (test_len < 0) { - printf("\n"); + zassert_not_equal(len, BACNET_STATUS_ERROR, NULL); + if (len > 0) { + test_len = bacapp_decode_application_data( + rpdata.application_data, + (uint8_t)rpdata.application_data_len, &value); + zassert_true(test_len >= 0, NULL); + if (test_len < 0) { + printf("\n"); + } } pOptional++; } diff --git a/test/bacnet/datalink/bvlc/src/main.c b/test/bacnet/datalink/bvlc/src/main.c index 549b16e9..439b77bc 100644 --- a/test/bacnet/datalink/bvlc/src/main.c +++ b/test/bacnet/datalink/bvlc/src/main.c @@ -504,6 +504,63 @@ static void test_BVLC_Distribute_Broadcast_To_Network(void) #endif } +static void test_BVLC_Broadcast_Distribution_Table_Encode(void) +{ + uint8_t apdu[480] = { 0 }; + uint16_t apdu_len = 0; + uint16_t test_apdu_len = 0; + uint16_t i = 0; + uint16_t count = 0; + uint16_t test_count = 0; + bool status = false; + BACNET_ERROR_CODE error_code; + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY bdt_list[5] = { 0 }; + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY bdt_entry = { 0 }; + BACNET_IP_ADDRESS dest_address = { 0 }; + BACNET_IP_BROADCAST_DISTRIBUTION_MASK broadcast_mask = { 0 }; + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY test_bdt_list[5] = { 0 }; + + /* configure a BDT entry */ + count = sizeof(bdt_list) / sizeof(bdt_list[0]); + bvlc_broadcast_distribution_table_link_array(&bdt_list[0], count); + for (i = 0; i < count; i++) { + status = bvlc_address_port_from_ascii( + &dest_address, "192.168.0.255", "0xBAC0"); + zassert_true(status, NULL); + dest_address.port += i; + broadcast_mask.address[0] = 255; + broadcast_mask.address[1] = 255; + broadcast_mask.address[2] = 255; + broadcast_mask.address[3] = 255; + status = bvlc_broadcast_distribution_table_entry_set( + &bdt_entry, &dest_address, &broadcast_mask); + zassert_true(status, NULL); + status = bvlc_broadcast_distribution_table_entry_append( + &bdt_list[0], &bdt_entry); + zassert_true(status, NULL); + } + test_count = bvlc_broadcast_distribution_table_count(&bdt_list[0]); + if (test_count != count) { + printf("size=%u count=%u\n", count, test_count); + } + zassert_equal(test_count, count, NULL); + /* test the encode/decode pair */ + apdu_len = bvlc_broadcast_distribution_table_encode(&apdu[0], + sizeof(apdu), &bdt_list[0]); + test_count = sizeof(test_bdt_list) / sizeof(test_bdt_list[0]); + bvlc_broadcast_distribution_table_link_array(&test_bdt_list[0], test_count); + test_apdu_len = bvlc_broadcast_distribution_table_decode(&apdu[0], + apdu_len, &error_code, &test_bdt_list[0]); + zassert_equal(test_apdu_len, apdu_len, NULL); + 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); + } +} + #if 0 /*TODO: Expose test_BVLC_Header */ static void test_BVLC_Write_Broadcast_Distribution_Table_Message( uint8_t *npdu,