From e08680044e28721906a028ed10b7e4b06acd75dd Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 10 May 2022 17:06:51 -0500 Subject: [PATCH] Added FD BBMD Address encoding. (#269) * Added FD BBMD Address encoder and decoder * Added FD BBMD address to bacapp * Added FD BBMD address to network port object Co-authored-by: Steve Karg --- src/bacnet/bacapp.c | 75 +++++++++ src/bacnet/bacapp.h | 7 +- src/bacnet/bacenum.h | 4 +- src/bacnet/basic/bbmd/h_bbmd.c | 26 +++ src/bacnet/basic/bbmd/h_bbmd.h | 6 + src/bacnet/basic/object/netport.c | 232 +++++++++++++++++++++++++++ src/bacnet/basic/object/netport.h | 23 +++ src/bacnet/datalink/bvlc.c | 152 ++++++++++++++++++ src/bacnet/datalink/bvlc.h | 11 ++ src/bacnet/datalink/dlenv.c | 7 + test/Makefile | 6 + test/bacnet/bacapp/CMakeLists.txt | 2 + test/bacnet/bacapp/src/main.c | 3 + test/bacnet/datalink/bvlc/src/main.c | 30 +++- 14 files changed, 581 insertions(+), 3 deletions(-) diff --git a/src/bacnet/bacapp.c b/src/bacnet/bacapp.c index f6e837e0..5afdb752 100644 --- a/src/bacnet/bacapp.c +++ b/src/bacnet/bacapp.c @@ -51,6 +51,9 @@ #include "bacnet/datetime.h" #include "bacnet/bacstr.h" #include "bacnet/lighting.h" +#if defined(BACAPP_HOST_N_PORT) +#include "bacnet/datalink/datalink.h" +#endif /** @file bacapp.c Utilities for the BACnet_Application_Data_Value */ @@ -150,6 +153,13 @@ int bacapp_encode_application_data( &apdu[0], &value->type.Lighting_Command); break; #endif +#if defined(BACAPP_HOST_N_PORT) + case BACNET_APPLICATION_TAG_HOST_N_PORT: + apdu_len = bvlc_foreign_device_bbmd_host_address_encode( + &apdu[0], MAX_APDU, &value->type.IP_Address); + + break; +#endif #if defined(BACAPP_DEVICE_OBJECT_PROP_REF) case BACNET_APPLICATION_TAG_DEVICE_OBJECT_PROPERTY_REFERENCE: /* BACnetDeviceObjectPropertyReference */ @@ -271,6 +281,14 @@ int bacapp_decode_data(uint8_t *apdu, len = lighting_command_decode( &apdu[0], len_value_type, &value->type.Lighting_Command); break; +#endif +#if defined(BACAPP_HOST_N_PORT) + case BACNET_APPLICATION_TAG_HOST_N_PORT: { + BACNET_ERROR_CODE error_code; + len = bvlc_foreign_device_bbmd_host_address_decode( + &apdu[0], len_value_type, &error_code, + &value->type.IP_Address); + } break; #endif default: break; @@ -566,6 +584,16 @@ int bacapp_encode_context_data_value(uint8_t *apdu, apdu_len = lighting_command_encode_context(&apdu[0], context_tag_number, &value->type.Lighting_Command); break; +#endif +#if defined(BACAPP_HOST_N_PORT) + case BACNET_APPLICATION_TAG_HOST_N_PORT: + apdu_len = encode_opening_tag(&apdu[0], context_tag_number); + apdu_len += bvlc_foreign_device_bbmd_host_address_encode( + &apdu[apdu_len], MAX_APDU-apdu_len, + &value->type.IP_Address); + apdu_len += encode_closing_tag(&apdu[apdu_len], + context_tag_number); + break; #endif default: break; @@ -918,6 +946,13 @@ bool bacapp_copy(BACNET_APPLICATION_DATA_VALUE *dest_value, lighting_command_copy(&dest_value->type.Lighting_Command, &src_value->type.Lighting_Command); break; +#endif +#if defined(BACAPP_HOST_N_PORT) + case BACNET_APPLICATION_TAG_HOST_N_PORT: + status = + bvlc_address_copy(&dest_value->type.IP_Address, + &src_value->type.IP_Address); + break; #endif default: status = false; @@ -1436,6 +1471,16 @@ int bacapp_snprintf_value( /* bytes were written. */ ret_val = str_len - rem_str_len; break; +#endif +#if defined(BACAPP_HOST_N_PORT) + case BACNET_APPLICATION_TAG_HOST_N_PORT: + ret_val = snprintf(str, str_len, "%u.%u.%u.%u:%u", + (unsigned)value->type.IP_Address.address[0], + (unsigned)value->type.IP_Address.address[1], + (unsigned)value->type.IP_Address.address[2], + (unsigned)value->type.IP_Address.address[3], + (unsigned)value->type.IP_Address.port); + break; #endif default: ret_val = 0; @@ -1503,6 +1548,9 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number, unsigned long unsigned_long_value = 0; double double_value = 0.0; int count = 0; +#if defined(BACAPP_HOST_N_PORT) + unsigned a[4] = { 0 }, p = 0; +#endif if (value && (tag_number < MAX_BACNET_APPLICATION_TAG)) { status = true; @@ -1620,6 +1668,26 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number, case BACNET_APPLICATION_TAG_LIGHTING_COMMAND: /* FIXME: add parsing for lighting command */ break; +#endif +#if defined(BACAPP_HOST_N_PORT) + case BACNET_APPLICATION_TAG_HOST_N_PORT: + count = sscanf(argv, "%3u.%3u.%3u.%3u:%5u", + &a[0], &a[1], &a[2], &a[3], &p); + if ((count == 4) || (count == 5)) { + value->type.IP_Address.address[0] = a[0]; + value->type.IP_Address.address[1] = a[1]; + value->type.IP_Address.address[2] = a[2]; + value->type.IP_Address.address[3] = a[3]; + if (count == 4) { + value->type.IP_Address.port = 0xBAC0U; + } else { + value->type.IP_Address.port = (uint16_t)p; + } + status = true; + } else { + status = false; + } + break; #endif default: break; @@ -1797,6 +1865,13 @@ bool bacapp_same_value(BACNET_APPLICATION_DATA_VALUE *value, status = lighting_command_same(&value->type.Lighting_Command, &test_value->type.Lighting_Command); break; +#endif +#if defined(BACAPP_HOST_N_PORT) + case BACNET_APPLICATION_TAG_HOST_N_PORT: + status = + !bvlc_address_different(&value->type.IP_Address, + &value->type.IP_Address); + break; #endif default: status = false; diff --git a/src/bacnet/bacapp.h b/src/bacnet/bacapp.h index 56282a62..026b6c12 100644 --- a/src/bacnet/bacapp.h +++ b/src/bacnet/bacapp.h @@ -38,7 +38,9 @@ #if defined (BACAPP_DEVICE_OBJECT_PROP_REF) #include "bacnet/bacdevobjpropref.h" #endif - +#if defined(BACAPP_HOST_N_PORT) +#include "bacnet/datalink/datalink.h" +#endif struct BACnet_Application_Data_Value; typedef struct BACnet_Application_Data_Value { @@ -86,6 +88,9 @@ typedef struct BACnet_Application_Data_Value { #if defined (BACAPP_LIGHTING_COMMAND) BACNET_LIGHTING_COMMAND Lighting_Command; #endif +#if defined(BACAPP_HOST_N_PORT) + BACNET_IP_ADDRESS IP_Address; +#endif #if defined (BACAPP_DEVICE_OBJECT_PROP_REF) BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE Device_Object_Property_Reference; diff --git a/src/bacnet/bacenum.h b/src/bacnet/bacenum.h index 69fafed1..17584fc7 100644 --- a/src/bacnet/bacenum.h +++ b/src/bacnet/bacenum.h @@ -1399,7 +1399,9 @@ typedef enum { /* BACnetReadAccessSpecification */ BACNET_APPLICATION_TAG_READ_ACCESS_SPECIFICATION, /* BACnetLightingCommand */ - BACNET_APPLICATION_TAG_LIGHTING_COMMAND + BACNET_APPLICATION_TAG_LIGHTING_COMMAND, + /* BACnetHostNPort */ + BACNET_APPLICATION_TAG_HOST_N_PORT } BACNET_APPLICATION_TAG; /* note: these are not the real values, */ diff --git a/src/bacnet/basic/bbmd/h_bbmd.c b/src/bacnet/basic/bbmd/h_bbmd.c index 34021deb..0a7a05c9 100644 --- a/src/bacnet/basic/bbmd/h_bbmd.c +++ b/src/bacnet/basic/bbmd/h_bbmd.c @@ -75,6 +75,8 @@ static BACNET_IP_ADDRESS BVLC_Global_Address; static bool BVLC_NAT_Handling = false; /** if we are a foreign device, store the remote BBMD address/port here */ 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; #if BBMD_ENABLED /* local buffer & length for sending */ static uint8_t BVLC_Buffer[BIP_MPDU_MAX]; @@ -1162,11 +1164,35 @@ int bvlc_register_with_bbmd(BACNET_IP_ADDRESS *bbmd_addr, uint16_t ttl_seconds) /* Store the BBMD address and port so that we won't broadcast locally. */ /* We are a foreign device! */ bvlc_address_copy(&Remote_BBMD, bbmd_addr); + Remote_BBMD_TTL_Seconds = ttl_seconds; BVLC_Buffer_Len = bvlc_encode_register_foreign_device( &BVLC_Buffer[0], sizeof(BVLC_Buffer), ttl_seconds); return bip_send_mpdu(bbmd_addr, &BVLC_Buffer[0], BVLC_Buffer_Len); } + +/** Get the remote BBMD address that was used to Register as a foreign device + * @param bbmd_addr - IPv4 address of BBMD with which to register + * @return Positive number (of bytes sent) on success, + * 0 if no registration request is sent, or + * -1 if registration fails. + */ +void bvlc_remote_bbmd_address( + BACNET_IP_ADDRESS *bbmd_addr) +{ + bvlc_address_copy(bbmd_addr, &Remote_BBMD); +} + +/** + * @brief Get the remote BBMD time-to-live seconds used to + * Register Foreign Device + * @return Lease time in seconds to use when registering. + */ +uint16_t bvlc_remote_bbmd_lifetime( + void) +{ + return Remote_BBMD_TTL_Seconds; +} #endif #if BBMD_CLIENT_ENABLED diff --git a/src/bacnet/basic/bbmd/h_bbmd.h b/src/bacnet/basic/bbmd/h_bbmd.h index 266bc39f..e7b5d44b 100644 --- a/src/bacnet/basic/bbmd/h_bbmd.h +++ b/src/bacnet/basic/bbmd/h_bbmd.h @@ -99,6 +99,12 @@ int bvlc_bbmd_read_fdt(BACNET_IP_ADDRESS *bbmd_addr); BACNET_STACK_EXPORT int bvlc_register_with_bbmd( BACNET_IP_ADDRESS *address, uint16_t time_to_live_seconds); +BACNET_STACK_EXPORT +void bvlc_remote_bbmd_address( + BACNET_IP_ADDRESS *address); +BACNET_STACK_EXPORT +uint16_t bvlc_remote_bbmd_lifetime( + void); /* Local interface to manage BBMD. * The interface user needs to handle mutual exclusion if needed i.e. diff --git a/src/bacnet/basic/object/netport.c b/src/bacnet/basic/object/netport.c index 88f45cb5..0ee8db01 100644 --- a/src/bacnet/basic/object/netport.c +++ b/src/bacnet/basic/object/netport.c @@ -70,6 +70,10 @@ struct bacnet_ipv4_port { bool BBMD_Accept_FD_Registrations; void *BBMD_BD_Table; void *BBMD_FD_Table; + /* used for foreign device registration to remote BBMD */ + uint8_t BBMD_IP_Address[4]; + uint16_t BBMD_Port; + uint16_t BBMD_Lifetime; }; #define IPV6_ADDR_SIZE 16 @@ -1395,6 +1399,217 @@ bool Network_Port_BBMD_FD_Table_Set( return status; } +#if defined(BACDL_BIP) && BBMD_ENABLED +/** + * For a given object instance-number, gets the ip-address and port + * Note: depends on Network_Type being set for this object + * + * @param object_instance - object-instance number of the object + * @param addr - holds the ip-address and port retrieved + * + * @return true if ip-address and port were retrieved + */ +static bool Network_Port_Remote_BBMD_IP_Address_And_Port( + uint32_t object_instance, BACNET_IP_ADDRESS *addr) +{ + unsigned index = 0; /* offset from instance lookup */ + bool status = false; + + if (addr) { + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + if (Object_List[index].Network_Type == PORT_TYPE_BIP) { + bvlc_address_set(addr, + Object_List[index].Network.IPv4.BBMD_IP_Address[0], + Object_List[index].Network.IPv4.BBMD_IP_Address[1], + Object_List[index].Network.IPv4.BBMD_IP_Address[2], + Object_List[index].Network.IPv4.BBMD_IP_Address[3]); + addr->port = Object_List[index].Network.IPv4.BBMD_Port; + status = true; + } + } + } + + return status; +} +#endif + +/** + * For a given object instance-number, loads the ip-address into + * an octet string. + * Note: depends on Network_Type being set for this object + * + * @param object_instance - object-instance number of the object + * @param a - ip-address first octet + * @param b - ip-address first octet + * @param c - ip-address first octet + * @param d - ip-address first octet + * + * @return true if ip-address was retrieved + */ +bool Network_Port_Remote_BBMD_IP_Address( + uint32_t object_instance, + uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d) +{ + unsigned index = 0; /* offset from instance lookup */ + bool status = false; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + if (Object_List[index].Network_Type == PORT_TYPE_BIP) { + if (a) { + *a = Object_List[index].Network.IPv4.BBMD_IP_Address[0]; + } + if (b) { + *b = Object_List[index].Network.IPv4.BBMD_IP_Address[1]; + } + if (c) { + *c = Object_List[index].Network.IPv4.BBMD_IP_Address[2]; + } + if (d) { + *d = Object_List[index].Network.IPv4.BBMD_IP_Address[3]; + } + } + } + + return status; +} + +/** + * For a given object instance-number, sets the ip-address + * Note: depends on Network_Type being set for this object + * + * @param object_instance - object-instance number of the object + * @param a - ip-address first octet + * @param b - ip-address first octet + * @param c - ip-address first octet + * @param d - ip-address first octet + * + * @return true if ip-address was set + */ +bool Network_Port_Remote_BBMD_IP_Address_Set( + uint32_t object_instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) +{ + unsigned index = 0; /* offset from instance lookup */ + bool status = false; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + if (Object_List[index].Network_Type == PORT_TYPE_BIP) { + Object_List[index].Network.IPv4.BBMD_IP_Address[0] = a; + Object_List[index].Network.IPv4.BBMD_IP_Address[1] = b; + Object_List[index].Network.IPv4.BBMD_IP_Address[2] = c; + Object_List[index].Network.IPv4.BBMD_IP_Address[3] = d; + } + } + + return status; +} + +/** + * For a given object instance-number, gets the BBMD UDP Port number + * Note: depends on Network_Type being set to PORT_TYPE_BIP for this object + * + * @param object_instance - object-instance number of the object + * + * @return BBMD UDP Port number + */ +uint16_t Network_Port_Remote_BBMD_BIP_Port(uint32_t object_instance) +{ + uint16_t value = 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) { + value = Object_List[index].Network.IPv4.BBMD_Port; + } + } + + return value; +} + +/** + * For a given object instance-number, sets the BBMD UDP Port number + * Note: depends on Network_Type being set to PORT_TYPE_BIP for this object + * + * @param object_instance - object-instance number of the object + * @param value - BBMD UDP Port number (default=0xBAC0) + * + * @return true if values are within range and property is set. + */ +bool Network_Port_Remote_BBMD_BIP_Port_Set(uint32_t object_instance, + uint16_t value) +{ + 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) { + if (Object_List[index].Network.IPv4.BBMD_Port != value) { + Object_List[index].Changes_Pending = true; + } + Object_List[index].Network.IPv4.BBMD_Port = value; + status = true; + } + } + + return status; +} + +/** + * For a given object instance-number, gets the BBMD lifetime seconds + * Note: depends on Network_Type being set to PORT_TYPE_BIP for this object + * + * @param object_instance - object-instance number of the object + * + * @return BBMD lifetime seconds + */ +uint16_t Network_Port_Remote_BBMD_BIP_Lifetime(uint32_t object_instance) +{ + uint16_t value = 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) { + value = Object_List[index].Network.IPv4.BBMD_Lifetime; + } + } + + return value; +} + +/** + * For a given object instance-number, sets the BBMD lifetime seconds + * Note: depends on Network_Type being set to PORT_TYPE_BIP for this object + * + * @param object_instance - object-instance number of the object + * @param value - BBMD lifetime seconds + * + * @return true if values are within range and property is set. + */ +bool Network_Port_Remote_BBMD_BIP_Lifetime_Set(uint32_t object_instance, + uint16_t value) +{ + 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) { + if (Object_List[index].Network.IPv4.BBMD_Lifetime != value) { + Object_List[index].Changes_Pending = true; + } + Object_List[index].Network.IPv4.BBMD_Lifetime = value; + 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 @@ -1962,9 +2177,13 @@ bool Network_Port_MSTP_Max_Info_Frames_Set( int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { int apdu_len = 0; + int apdu_size = 0; BACNET_BIT_STRING bit_string; BACNET_OCTET_STRING octet_string; BACNET_CHARACTER_STRING char_string; +#if defined(BACDL_BIP) && BBMD_ENABLED + BACNET_IP_ADDRESS ip_address; +#endif uint8_t *apdu = NULL; const int *pRequired = NULL; const int *pOptional = NULL; @@ -1984,6 +2203,7 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) return BACNET_STATUS_ERROR; } apdu = rpdata->application_data; + apdu_size = rpdata->application_data_len; switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( @@ -2133,6 +2353,17 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) rpdata->application_data_len, Network_Port_BBMD_FD_Table(rpdata->object_instance)); break; + case PROP_FD_BBMD_ADDRESS: + Network_Port_Remote_BBMD_IP_Address_And_Port( + rpdata->object_instance, &ip_address); + apdu_len = bvlc_foreign_device_bbmd_host_address_encode(&apdu[0], + apdu_size, &ip_address); + break; + case PROP_FD_SUBSCRIPTION_LIFETIME: + apdu_len = encode_application_unsigned( + &apdu[0], Network_Port_Remote_BBMD_BIP_Lifetime( + rpdata->object_instance)); + break; #endif case PROP_BACNET_IPV6_MODE: apdu_len = encode_application_enumerated( @@ -2214,6 +2445,7 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; apdu_len = BACNET_STATUS_ERROR; + (void)apdu_size; break; } diff --git a/src/bacnet/basic/object/netport.h b/src/bacnet/basic/object/netport.h index 45afa784..ce5dfd68 100644 --- a/src/bacnet/basic/object/netport.h +++ b/src/bacnet/basic/object/netport.h @@ -258,6 +258,29 @@ extern "C" { uint32_t object_instance, void *fdt_head); + BACNET_STACK_EXPORT + bool Network_Port_Remote_BBMD_IP_Address( + uint32_t object_instance, + uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d); + BACNET_STACK_EXPORT + bool Network_Port_Remote_BBMD_IP_Address_Set( + uint32_t object_instance, + uint8_t a, uint8_t b, uint8_t c, uint8_t d); + BACNET_STACK_EXPORT + uint16_t Network_Port_Remote_BBMD_BIP_Port( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Network_Port_Remote_BBMD_BIP_Port_Set( + uint32_t object_instance, + uint16_t value); + BACNET_STACK_EXPORT + uint16_t Network_Port_Remote_BBMD_BIP_Lifetime( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Network_Port_Remote_BBMD_BIP_Lifetime_Set( + uint32_t object_instance, + uint16_t value); + 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 b4eff1bc..ac1f1239 100644 --- a/src/bacnet/datalink/bvlc.c +++ b/src/bacnet/datalink/bvlc.c @@ -2620,3 +2620,155 @@ const char *bvlc_result_code_name(uint16_t result_code) return name; } + +/** + * @brief Encode a BBMD Address for Network Port object + * + * BACnetHostNPort ::= SEQUENCE { + * host [0] BACnetHostAddress, + * BACnetHostAddress ::= CHOICE { + * ip-address [1] OCTET STRING, -- 4 octets for B/IP + * } + * port [1] Unsigned16 + * } + * + * @param apdu - the APDU buffer + * @param apdu_size - the APDU buffer size + * @param ip_address - IP address and port number + * @return length of the APDU buffer + */ +int bvlc_foreign_device_bbmd_host_address_encode(uint8_t *apdu, + uint16_t apdu_size, + BACNET_IP_ADDRESS *ip_address) +{ + int len = 0; + int apdu_len = 0; + BACNET_OCTET_STRING octet_string; + + if (apdu && (apdu_size >= 9) && ip_address) { + /* 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, + &ip_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, ip_address->port); + apdu_len += len; + } + + return apdu_len; +} + +/** + * @brief Decode the Broadcast-Distribution-Table for Network Port object + * + * BACnetHostNPort ::= SEQUENCE { + * host [0] BACnetHostAddress, + * BACnetHostAddress ::= CHOICE { + * ip-address [1] OCTET STRING, -- 4 octets for B/IP + * } + * port [1] Unsigned16 + * } + * + * @param apdu - the APDU buffer + * @param apdu_len - the APDU buffer length + * @param ip_address - IP address and port number + * @return length of the APDU buffer decoded, or ERROR, REJECT, or ABORT + */ +int bvlc_foreign_device_bbmd_host_address_decode(uint8_t *apdu, + uint16_t apdu_len, + BACNET_ERROR_CODE *error_code, + BACNET_IP_ADDRESS *ip_address) +{ + int len = 0; + BACNET_OCTET_STRING octet_string = { 0 }; + 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; + } + /* 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; + } + (void)octetstring_copy_value(&ip_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) { + ip_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; + } + + return apdu_len; +} diff --git a/src/bacnet/datalink/bvlc.h b/src/bacnet/datalink/bvlc.h index ab5fd700..c3313362 100644 --- a/src/bacnet/datalink/bvlc.h +++ b/src/bacnet/datalink/bvlc.h @@ -511,6 +511,17 @@ extern "C" { BACNET_STACK_EXPORT const char *bvlc_result_code_name(uint16_t result_code); + BACNET_STACK_EXPORT + int bvlc_foreign_device_bbmd_host_address_encode(uint8_t *apdu, + uint16_t apdu_size, + BACNET_IP_ADDRESS *ip_address); + + BACNET_STACK_EXPORT + int bvlc_foreign_device_bbmd_host_address_decode(uint8_t *apdu, + uint16_t apdu_len, + BACNET_ERROR_CODE *error_code, + BACNET_IP_ADDRESS *ip_address); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/bacnet/datalink/dlenv.c b/src/bacnet/datalink/dlenv.c index e8e57bdb..54f2f609 100644 --- a/src/bacnet/datalink/dlenv.c +++ b/src/bacnet/datalink/dlenv.c @@ -254,6 +254,7 @@ static void dlenv_network_port_init(void) { const uint32_t instance = 1; BACNET_IP_ADDRESS addr = { 0 }; + uint8_t addr0, addr1, addr2, addr3; Network_Port_Object_Instance_Number_Set(0, instance); Network_Port_Name_Set(instance, "BACnet/IP Port"); @@ -266,6 +267,12 @@ static void dlenv_network_port_init(void) #if BBMD_ENABLED Network_Port_BBMD_BD_Table_Set(instance, bvlc_bdt_list()); Network_Port_BBMD_FD_Table_Set(instance, bvlc_fdt_list()); + /* foreign device registration */ + bvlc_address_get(&BBMD_Address, &addr0, &addr1, &addr2, &addr3); + Network_Port_Remote_BBMD_IP_Address_Set(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); #endif /* common NP data */ Network_Port_Reliability_Set(instance, RELIABILITY_NO_FAULT_DETECTED); diff --git a/test/Makefile b/test/Makefile index ae2eb19c..73d9715d 100644 --- a/test/Makefile +++ b/test/Makefile @@ -22,6 +22,12 @@ test: [ -d $(BUILD_DIR) ] && cd $(BUILD_DIR) && ctest $(CTEST_OPTIONS) [ -d $(BUILD_DIR) ] && $(MAKE) -C $(BUILD_DIR) lcov +.PHONY: retest +retest: + [ -d $(BUILD_DIR) ] && cd $(BUILD_DIR) && cmake --build . && cd .. + [ -d $(BUILD_DIR) ] && cd $(BUILD_DIR) && ctest $(CTEST_OPTIONS) + [ -d $(BUILD_DIR) ] && $(MAKE) -C $(BUILD_DIR) lcov + .PHONY: report report: [ -d $(BUILD_DIR) ] && cat $(BUILD_DIR)/Testing/Temporary/LastTest*.log diff --git a/test/bacnet/bacapp/CMakeLists.txt b/test/bacnet/bacapp/CMakeLists.txt index fcbecd22..0be45fd1 100644 --- a/test/bacnet/bacapp/CMakeLists.txt +++ b/test/bacnet/bacapp/CMakeLists.txt @@ -24,6 +24,7 @@ add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 PRINT_ENABLED=1 + BACAPP_HOST_N_PORT=1 ) include_directories( @@ -46,6 +47,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/basic/sys/days.c ${SRC_DIR}/bacnet/lighting.c ${SRC_DIR}/bacnet/indtext.c + ${SRC_DIR}/bacnet/datalink/bvlc.c # Test and test library files ./src/main.c ${ZTST_DIR}/ztest_mock.c diff --git a/test/bacnet/bacapp/src/main.c b/test/bacnet/bacapp/src/main.c index 6674c8a4..396f3308 100644 --- a/test/bacnet/bacapp/src/main.c +++ b/test/bacnet/bacapp/src/main.c @@ -148,6 +148,9 @@ static void test_bacapp_copy(void) #if defined(BACAPP_LIGHTING_COMMAND) BACNET_APPLICATION_TAG_LIGHTING_COMMAND, #endif + #if defined(BACAPP_HOST_N_PORT) + BACNET_APPLICATION_TAG_HOST_N_PORT, + #endif }; for (i = 0; i < sizeof(tags)/sizeof(tags[0]); ++i) { BACNET_APPLICATION_TAG tag = tags[i]; diff --git a/test/bacnet/datalink/bvlc/src/main.c b/test/bacnet/datalink/bvlc/src/main.c index eb8cb2c1..0d0706f4 100644 --- a/test/bacnet/datalink/bvlc/src/main.c +++ b/test/bacnet/datalink/bvlc/src/main.c @@ -816,6 +816,33 @@ static void test_BVLC_Address_Get_Set(void) zassert_equal(octet3, test_octet3, NULL); } +static void test_BVLC_BBMD_Address(void) +{ + uint8_t apdu[480] = { 0 }; + uint16_t apdu_len = 0; + uint16_t test_apdu_len = 0; + uint16_t i = 0; + BACNET_IP_ADDRESS bbmd_address; + BACNET_IP_ADDRESS test_bbmd_address; + BACNET_ERROR_CODE error_code = 0; + bool status = false; + + status = bvlc_address_port_from_ascii( + &bbmd_address, "192.168.0.255", "0xBAC0"); + zassert_true(status, NULL); + apdu_len = bvlc_foreign_device_bbmd_host_address_encode(apdu, sizeof(apdu), + &bbmd_address); + zassert_not_equal(apdu_len, 0, NULL); + test_apdu_len = bvlc_foreign_device_bbmd_host_address_decode(apdu, + apdu_len, &error_code, &test_bbmd_address); + zassert_not_equal(test_apdu_len, 0, NULL); + zassert_not_equal(test_apdu_len, BACNET_STATUS_ERROR, NULL); + zassert_not_equal(test_apdu_len, BACNET_STATUS_ABORT, NULL); + zassert_not_equal(test_apdu_len, BACNET_STATUS_REJECT, NULL); + status = bvlc_address_different(&bbmd_address, &test_bbmd_address); + zassert_false(status, NULL); +} + /** * @} */ @@ -837,7 +864,8 @@ void test_main(void) ztest_unit_test(test_BVLC_Original_Broadcast_NPDU), 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_Address_Get_Set), + ztest_unit_test(test_BVLC_BBMD_Address) ); ztest_run_test_suite(bvlc_tests);