diff --git a/src/bacnet/bacstr.c b/src/bacnet/bacstr.c index 4f279cf5..fed102ef 100644 --- a/src/bacnet/bacstr.c +++ b/src/bacnet/bacstr.c @@ -498,6 +498,35 @@ bool characterstring_ansi_copy( return false; } +/** + * @brief Copy a BACnetCharacterString into a buffer with null padding + * @param dest Pointer to the destination buffer. + * @param dest_max_len Size of the destination buffer. + * @param src Pointer to the source BACnetCharacterString. + * @return Length of the copied BACnetCharacterString, or 0 if the string + * exceeds the destination buffer size. + */ +size_t characterstring_copy_value( + char *dest, size_t dest_max_len, const BACNET_CHARACTER_STRING *src) +{ + size_t i = 0, length = 0; + + if (dest && src) { + if (src->length < dest_max_len) { + length = src->length; + for (i = 0; i < dest_max_len; i++) { + if (i < src->length) { + dest[i] = src->value[i]; + } else { + dest[i] = 0; + } + } + } + } + + return length; +} + /** * Returns true if the character encoding and string * contents are the same. diff --git a/src/bacnet/bacstr.h b/src/bacnet/bacstr.h index d4a325b9..30592205 100644 --- a/src/bacnet/bacstr.h +++ b/src/bacnet/bacstr.h @@ -89,6 +89,9 @@ BACNET_STACK_EXPORT bool characterstring_copy( BACNET_CHARACTER_STRING *dest, const BACNET_CHARACTER_STRING *src); BACNET_STACK_EXPORT +size_t characterstring_copy_value( + char *dest, size_t dest_max_len, const BACNET_CHARACTER_STRING *src); +BACNET_STACK_EXPORT bool characterstring_ansi_copy( char *dest, size_t dest_max_len, const BACNET_CHARACTER_STRING *src); /* returns true if the strings are the same length, encoding, value */ diff --git a/src/bacnet/basic/object/netport.c b/src/bacnet/basic/object/netport.c index a28b2417..fb520d1c 100644 --- a/src/bacnet/basic/object/netport.c +++ b/src/bacnet/basic/object/netport.c @@ -18,6 +18,7 @@ #include "bacnet/bacapp.h" #include "bacnet/bacint.h" #include "bacnet/bacdcode.h" +#include "bacnet/datetime.h" #include "bacnet/npdu.h" #include "bacnet/apdu.h" #include "bacnet/datalink/bvlc.h" @@ -33,26 +34,26 @@ #define BBMD_ENABLED 1 #endif +#define IPV4_ADDR_SIZE 4 #define BIP_DNS_MAX 3 struct bacnet_ipv4_port { - uint8_t IP_Address[4]; + uint8_t IP_Address[IPV4_ADDR_SIZE]; uint8_t IP_Subnet_Prefix; - uint8_t IP_Gateway[4]; - uint8_t IP_DNS_Server[BIP_DNS_MAX][4]; + uint8_t IP_Gateway[IPV4_ADDR_SIZE]; + uint8_t IP_DNS_Server[BIP_DNS_MAX][IPV4_ADDR_SIZE]; uint16_t Port; BACNET_IP_MODE Mode; bool IP_DHCP_Enable; uint32_t IP_DHCP_Lease_Seconds; - uint32_t IP_DHCP_Lease_Seconds_Remaining; - uint8_t IP_DHCP_Server[4]; + uint32_t IP_DHCP_Lease_Seconds_Start; + uint8_t IP_DHCP_Server[IPV4_ADDR_SIZE]; bool IP_NAT_Traversal; - uint32_t IP_Global_Address[4]; + uint32_t IP_Global_Address[IPV4_ADDR_SIZE]; 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; + BACNET_HOST_N_PORT_MINIMAL BBMD_Address; uint16_t BBMD_Lifetime; }; @@ -65,17 +66,18 @@ struct bacnet_ipv6_port { uint8_t IP_Gateway[IPV6_ADDR_SIZE]; uint8_t IP_DNS_Server[BIP_DNS_MAX][IPV6_ADDR_SIZE]; uint8_t IP_Multicast_Address[IPV6_ADDR_SIZE]; + bool IP_DHCP_Enable; uint8_t IP_DHCP_Server[IPV6_ADDR_SIZE]; + uint32_t IP_DHCP_Lease_Seconds; + uint32_t IP_DHCP_Lease_Seconds_Start; uint16_t Port; BACNET_IP_MODE Mode; - bool Auto_Addressing_Enable; char Zone_Index[ZONE_INDEX_SIZE]; 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[16]; - uint16_t BBMD_Port; + BACNET_HOST_N_PORT_MINIMAL BBMD_Address; uint16_t BBMD_Lifetime; }; @@ -184,6 +186,9 @@ static const int BIP_Port_Properties_Optional[] = { PROP_IP_DNS_SERVER, #if defined(BACDL_BIP) && (BACNET_NETWORK_PORT_IP_DHCP_ENABLED) PROP_IP_DHCP_ENABLE, + PROP_IP_DHCP_LEASE_TIME, + PROP_IP_DHCP_LEASE_TIME_REMAINING, + PROP_IP_DHCP_SERVER, #endif #if defined(BACDL_BIP) && (BBMD_ENABLED) PROP_BBMD_ACCEPT_FD_REGISTRATIONS, @@ -214,10 +219,12 @@ static const int BIP6_Port_Properties_Optional[] = { PROP_IPV6_DEFAULT_GATEWAY, PROP_BACNET_IPV6_MULTICAST_ADDRESS, PROP_IPV6_DNS_SERVER, +#if defined(BACDL_BIP6) && (BACNET_NETWORK_PORT_IP_DHCP_ENABLED) PROP_IPV6_AUTO_ADDRESSING_ENABLE, PROP_IPV6_DHCP_LEASE_TIME, PROP_IPV6_DHCP_LEASE_TIME_REMAINING, PROP_IPV6_DHCP_SERVER, +#endif PROP_IPV6_ZONE_INDEX, #if defined(BACDL_BIP6) && (BBMD_ENABLED) PROP_BBMD_ACCEPT_FD_REGISTRATIONS, @@ -1487,6 +1494,8 @@ bool Network_Port_IP_DHCP_Enable(uint32_t object_instance) if (index < BACNET_NETWORK_PORTS_MAX) { if (Object_List[index].Network_Type == PORT_TYPE_BIP) { dhcp_enable = Object_List[index].Network.IPv4.IP_DHCP_Enable; + } else if (Object_List[index].Network_Type == PORT_TYPE_BIP6) { + dhcp_enable = Object_List[index].Network.IPv6.IP_DHCP_Enable; } } @@ -1509,8 +1518,17 @@ bool Network_Port_IP_DHCP_Enable_Set(uint32_t object_instance, bool value) 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.IP_DHCP_Enable != value) { + Object_List[index].Changes_Pending = true; + } Object_List[index].Network.IPv4.IP_DHCP_Enable = value; status = true; + } else if (Object_List[index].Network_Type == PORT_TYPE_BIP6) { + if (Object_List[index].Network.IPv6.IP_DHCP_Enable != value) { + Object_List[index].Changes_Pending = true; + } + Object_List[index].Network.IPv6.IP_DHCP_Enable = value; + status = true; } } @@ -1602,11 +1620,19 @@ bool Network_Port_IP_DNS_Server_Set( { unsigned index = 0; /* offset from instance lookup */ bool status = false; + uint8_t *dns_server = NULL; index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { if (Object_List[index].Network_Type == PORT_TYPE_BIP) { if (dns_index < BIP_DNS_MAX) { + dns_server = &Object_List[index] + .Network.IPv4.IP_DNS_Server[dns_index][0]; + if ((dns_server[0] != a) || (dns_server[1] != b) || + (dns_server[2] != c) || (dns_server[3] != d)) { + /* octets are different, set changes pending */ + Object_List[index].Changes_Pending = true; + } Object_List[index].Network.IPv4.IP_DNS_Server[dns_index][0] = a; Object_List[index].Network.IPv4.IP_DNS_Server[dns_index][1] = b; Object_List[index].Network.IPv4.IP_DNS_Server[dns_index][2] = c; @@ -1954,6 +1980,72 @@ static int BBMD_Foreign_Device_Table_Encode( return apdu_len; } +/** + * @brief For a given object instance-number, gets the HostNPort + * @note depends on Network_Type being set for this object + * @param object_instance - object-instance number of the object + * @param bbmd_address - BACNET_HOST_N_PORT structure + * @return true if BBMD Address was copied + */ +bool Network_Port_Remote_BBMD_Address( + uint32_t object_instance, BACNET_HOST_N_PORT *bbmd_address) +{ + bool status = false; + unsigned index; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + if (Object_List[index].Network_Type == PORT_TYPE_BIP) { + status = host_n_port_from_minimal( + bbmd_address, &Object_List[index].Network.IPv4.BBMD_Address); + } else if (Object_List[index].Network_Type == PORT_TYPE_BIP6) { + status = host_n_port_from_minimal( + bbmd_address, &Object_List[index].Network.IPv6.BBMD_Address); + } + } + + return status; +} + +/** + * @brief For a given object instance-number, sets the FD BBMD Address: + * either as IP address or a hostname. + * @note depends on Network_Type being set for this object + * @param object_instance - object-instance number of the object + * @param bbmd_address - BACNET_HOST_N_PORT FD_BBMD_Address + * @return true if BBMD Address was set + */ +bool Network_Port_Remote_BBMD_Address_Set( + uint32_t object_instance, const BACNET_HOST_N_PORT *bbmd_address) +{ + bool status = false; + BACNET_HOST_N_PORT_MINIMAL bbmd_address_minimal = { 0 }; + BACNET_HOST_N_PORT_MINIMAL *dest_bbmd_address = NULL; + unsigned index; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + if (Object_List[index].Network_Type == PORT_TYPE_BIP) { + dest_bbmd_address = &Object_List[index].Network.IPv4.BBMD_Address; + } else if (Object_List[index].Network_Type == PORT_TYPE_BIP6) { + dest_bbmd_address = &Object_List[index].Network.IPv6.BBMD_Address; + } + if (dest_bbmd_address) { + status = + host_n_port_to_minimal(&bbmd_address_minimal, bbmd_address); + if (status) { + if (!host_n_port_minimal_same( + dest_bbmd_address, &bbmd_address_minimal)) { + host_n_port_to_minimal(dest_bbmd_address, bbmd_address); + Object_List[index].Changes_Pending = true; + } + } + } + } + + return status; +} + /** * For a given object instance-number, loads the ip-address into * an octet string. @@ -1972,23 +2064,27 @@ bool Network_Port_Remote_BBMD_IP_Address( { unsigned index = 0; /* offset from instance lookup */ bool status = false; + BACNET_HOST_N_PORT_MINIMAL *address; 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]; + address = &Object_List[index].Network.IPv4.BBMD_Address; + if (address->tag == BACNET_HOST_ADDRESS_TAG_IP_ADDRESS) { + if (a) { + *a = address->host.ip_address.address[0]; + } + if (b) { + *b = address->host.ip_address.address[1]; + } + if (c) { + *c = address->host.ip_address.address[2]; + } + if (d) { + *d = address->host.ip_address.address[3]; + } + status = true; } - 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]; - } - status = true; } } @@ -2012,21 +2108,25 @@ bool Network_Port_Remote_BBMD_IP_Address_Set( { unsigned index = 0; /* offset from instance lookup */ bool status = false; + BACNET_HOST_N_PORT_MINIMAL *address; 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_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)) { + address = &Object_List[index].Network.IPv4.BBMD_Address; + if ((address->host.ip_address.address[0] != a) || + (address->host.ip_address.address[1] != b) || + (address->host.ip_address.address[2] != c) || + (address->host.ip_address.address[3] != d) || + (address->tag != BACNET_HOST_ADDRESS_TAG_IP_ADDRESS)) { Object_List[index].Changes_Pending = true; } - 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; - + address->host.ip_address.address[0] = a; + address->host.ip_address.address[1] = b; + address->host.ip_address.address[2] = c; + address->host.ip_address.address[3] = d; + address->host.ip_address.length = 4; + address->tag = BACNET_HOST_ADDRESS_TAG_IP_ADDRESS; status = true; } } @@ -2050,7 +2150,7 @@ uint16_t Network_Port_Remote_BBMD_BIP_Port(uint32_t object_instance) 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; + value = Object_List[index].Network.IPv4.BBMD_Address.port; } } @@ -2075,10 +2175,10 @@ bool Network_Port_Remote_BBMD_BIP_Port_Set( 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) { + if (Object_List[index].Network.IPv4.BBMD_Address.port != value) { Object_List[index].Changes_Pending = true; } - Object_List[index].Network.IPv4.BBMD_Port = value; + Object_List[index].Network.IPv4.BBMD_Address.port = value; status = true; } } @@ -2139,9 +2239,9 @@ bool Network_Port_Remote_BBMD_BIP_Lifetime_Set( } /** - * @brief Get the foreign device subscription lifetime + * @brief Get the foreign device subscription lifetime seconds * @param object_instance [in] BACnet network port object instance number - * @return foreign device subscription lifetime + * @return foreign device subscription BBMD lifetime seconds */ static uint16_t Foreign_Device_Subscription_Lifetime(uint32_t object_instance) { @@ -2200,21 +2300,7 @@ bool Network_Port_BBMD_IP6_Accept_FD_Registrations(uint32_t object_instance) bool Network_Port_BBMD_IP6_Accept_FD_Registrations_Set( uint32_t object_instance, bool flag) { - bool status = false; - unsigned index = 0; - struct bacnet_ipv6_port *ipv6 = NULL; - - index = Network_Port_Instance_To_Index(object_instance); - if (index < BACNET_NETWORK_PORTS_MAX) { - ipv6 = &Object_List[index].Network.IPv6; - if (flag != ipv6->BBMD_Accept_FD_Registrations) { - ipv6->BBMD_Accept_FD_Registrations = flag; - Object_List[index].Changes_Pending = true; - } - status = true; - } - - return status; + return Network_Port_BBMD_Accept_FD_Registrations_Set(object_instance, flag); } /** @@ -2329,35 +2415,20 @@ bool Network_Port_BBMD_IP6_FD_Table_Set( * @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 + * @return number of bytes encoded, or BACNET_STATUS_ERROR if error */ static int Foreign_Device_BBMD_Address_Encode( uint32_t object_instance, uint8_t *apdu, size_t apdu_size) { - unsigned index = 0; /* offset from instance lookup */ - BACNET_IP_ADDRESS ip4_address; - BACNET_IP6_ADDRESS ip6_address; int apdu_len = 0; + BACNET_HOST_N_PORT bbmd_address = { 0 }; - 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( - &ip4_address, - 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]); - ip4_address.port = Object_List[index].Network.IPv4.BBMD_Port; - apdu_len = bvlc_foreign_device_bbmd_host_address_encode( - apdu, apdu_size, &ip4_address); - } else if (Object_List[index].Network_Type == PORT_TYPE_BIP6) { - bvlc6_address_n_port_set( - &ip6_address, Object_List[index].Network.IPv6.BBMD_IP_Address, - Object_List[index].Network.IPv6.BBMD_Port); - apdu_len = bvlc6_foreign_device_bbmd_host_address_encode( - apdu, apdu_size, &ip6_address); - } + Network_Port_Remote_BBMD_Address(object_instance, &bbmd_address); + apdu_len = host_n_port_encode(NULL, &bbmd_address); + if (apdu_len > apdu_size) { + apdu_len = BACNET_STATUS_ERROR; + } else { + apdu_len = host_n_port_encode(apdu, &bbmd_address); } return apdu_len; @@ -2378,14 +2449,19 @@ bool Network_Port_Remote_BBMD_IP6_Address( { unsigned index = 0; /* offset from instance lookup */ bool status = false; + BACNET_HOST_N_PORT_MINIMAL *address; + size_t i; index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { if (Object_List[index].Network_Type == PORT_TYPE_BIP6) { - if (addr) { - memcpy( - addr, Object_List[index].Network.IPv6.BBMD_IP_Address, - IP6_ADDRESS_MAX); + address = &Object_List[index].Network.IPv6.BBMD_Address; + if (address->tag == BACNET_HOST_ADDRESS_TAG_IP_ADDRESS) { + if (addr) { + for (i = 0; i < IP6_ADDRESS_MAX; i++) { + addr[i] = address->host.ip_address.address[i]; + } + } status = true; } } @@ -2408,19 +2484,22 @@ bool Network_Port_Remote_BBMD_IP6_Address_Set( { unsigned index = 0; /* offset from instance lookup */ bool status = false; + BACNET_HOST_N_PORT_MINIMAL bbmd_address = { 0 }; + uint16_t port; index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { if (Object_List[index].Network_Type == PORT_TYPE_BIP6) { - if (memcmp( - Object_List[index].Network.IPv6.BBMD_IP_Address, addr, - IP6_ADDRESS_MAX)) { - memcpy( - Object_List[index].Network.IPv6.BBMD_IP_Address, addr, - IP6_ADDRESS_MAX); + port = Object_List[index].Network.IPv6.BBMD_Address.port; + host_n_port_minimal_ip_init( + &bbmd_address, port, addr, IP6_ADDRESS_MAX); + if (!host_n_port_minimal_same( + &Object_List[index].Network.IPv6.BBMD_Address, + &bbmd_address)) { Object_List[index].Changes_Pending = true; } - status = true; + status = host_n_port_minimal_copy( + &Object_List[index].Network.IPv6.BBMD_Address, &bbmd_address); } } @@ -2443,7 +2522,7 @@ uint16_t Network_Port_Remote_BBMD_BIP6_Port(uint32_t object_instance) index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { if (Object_List[index].Network_Type == PORT_TYPE_BIP6) { - value = Object_List[index].Network.IPv6.BBMD_Port; + value = Object_List[index].Network.IPv6.BBMD_Address.port; } } @@ -2468,10 +2547,10 @@ bool Network_Port_Remote_BBMD_BIP6_Port_Set( index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { if (Object_List[index].Network_Type == PORT_TYPE_BIP6) { - if (Object_List[index].Network.IPv6.BBMD_Port != value) { + if (Object_List[index].Network.IPv6.BBMD_Address.port != value) { Object_List[index].Changes_Pending = true; } - Object_List[index].Network.IPv6.BBMD_Port = value; + Object_List[index].Network.IPv6.BBMD_Address.port = value; status = true; } } @@ -2967,6 +3046,226 @@ bool Network_Port_IPv6_DHCP_Server_Set( return status; } +/** + * @brief Get the current time from the Device object + * @return current time in epoch seconds + */ +static bacnet_time_t Network_Port_Epoch_Seconds_Now(void) +{ + BACNET_DATE_TIME bdatetime = { 0 }; + + datetime_local(&bdatetime.date, &bdatetime.time, NULL, NULL); + return datetime_seconds_since_epoch(&bdatetime); +} + +/** + * For a given object instance-number, sets the IPv4_DHCP_Lease_Time + * or IPv6_DHCP_Lease_Time property value in seconds. + * + * @note depends on Network_Type being set to + * PORT_TYPE_BIP or PORT_TYPE_BIP6 for this object + * + * @param object_instance - object-instance number of the object + * @param value - 32 bit IPv4 DHCP Lease Time in seconds + * + * @return IPv4_DHCP_Lease_Time + */ +bool Network_Port_IP_DHCP_Lease_Time_Set( + uint32_t object_instance, const uint32_t value) +{ + bool status = false; + unsigned index = 0; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + switch (Object_List[index].Network_Type) { + case PORT_TYPE_BIP: + if (Object_List[index].Network.IPv4.IP_DHCP_Lease_Seconds != + value) { + Object_List[index].Changes_Pending = true; + } + Object_List[index].Network.IPv4.IP_DHCP_Lease_Seconds = value; + Object_List[index].Network.IPv4.IP_DHCP_Lease_Seconds_Start = + Network_Port_Epoch_Seconds_Now(); + status = true; + break; + case PORT_TYPE_BIP6: + if (Object_List[index].Network.IPv6.IP_DHCP_Lease_Seconds != + value) { + Object_List[index].Changes_Pending = true; + } + Object_List[index].Network.IPv6.IP_DHCP_Lease_Seconds = value; + Object_List[index].Network.IPv6.IP_DHCP_Lease_Seconds_Start = + Network_Port_Epoch_Seconds_Now(); + status = true; + break; + default: + break; + } + } + + return status; +} + +/** + * @brief Get the DHCP lease time in seconds + * @param object_instance [in] BACnet network port object instance number + * @return DHCP lease time in seconds + */ +uint32_t Network_Port_IP_DHCP_Lease_Time(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) { + switch (Object_List[index].Network_Type) { + case PORT_TYPE_BIP: + value = Object_List[index].Network.IPv4.IP_DHCP_Lease_Seconds; + break; + case PORT_TYPE_BIP6: + value = Object_List[index].Network.IPv6.IP_DHCP_Lease_Seconds; + break; + default: + break; + } + } + + return value; +} + +/** + * @brief Get the DHCP lease time in seconds + * @param object_instance [in] BACnet network port object instance number + * @return DHCP lease time in seconds + */ +uint32_t Network_Port_IP_DHCP_Lease_Time_Remaining(uint32_t object_instance) +{ + uint32_t value = 0, elapsed_seconds = 0, seconds = 0, start_seconds = 0; + unsigned index = 0; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + switch (Object_List[index].Network_Type) { + case PORT_TYPE_BIP: + seconds = Object_List[index].Network.IPv4.IP_DHCP_Lease_Seconds; + if (seconds) { + start_seconds = + Object_List[index] + .Network.IPv4.IP_DHCP_Lease_Seconds_Start; + elapsed_seconds = + Network_Port_Epoch_Seconds_Now() - start_seconds; + if (elapsed_seconds < seconds) { + value = seconds - elapsed_seconds; + } + } + break; + case PORT_TYPE_BIP6: + seconds = Object_List[index].Network.IPv6.IP_DHCP_Lease_Seconds; + if (seconds) { + start_seconds = + Object_List[index] + .Network.IPv6.IP_DHCP_Lease_Seconds_Start; + elapsed_seconds = + Network_Port_Epoch_Seconds_Now() - start_seconds; + if (elapsed_seconds < seconds) { + value = seconds - elapsed_seconds; + } + } + break; + default: + break; + } + } + + return value; +} + +/** + * @brief Get the the address of the DHCP server from which the last DHCP + * lease was obtained for the port. If the address of the DHCP server + * cannot be determined, the value of this property shall be X'00000000'. + * @param object_instance [in] BACnet network port object instance number + * @param ip_address [out] pointer to the IP address + */ +void Network_Port_IP_DHCP_Server( + uint32_t object_instance, BACNET_OCTET_STRING *ip_address) +{ + unsigned index = 0; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + switch (Object_List[index].Network_Type) { + case PORT_TYPE_BIP: + octetstring_init( + ip_address, + &Object_List[index].Network.IPv4.IP_DHCP_Server[0], + IPV4_ADDR_SIZE); + break; + case PORT_TYPE_BIP6: + octetstring_init( + ip_address, + &Object_List[index].Network.IPv6.IP_DHCP_Server[0], + IPV6_ADDR_SIZE); + break; + default: + break; + } + } +} + +/** + * @brief For a given object instance-number, sets the DHCP server ip-address + * @note depends on Network_Type being set for this object + * @param object_instance - object-instance number of the object + * @param ip_address - octet string of the DHCP server address + * @return true if ip-address was set + */ +bool Network_Port_IP_DHCP_Server_Set( + uint32_t object_instance, BACNET_OCTET_STRING *ip_address) +{ + bool status = false; + unsigned index = 0; + BACNET_OCTET_STRING my_address = { 0 }; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + switch (Object_List[index].Network_Type) { + case PORT_TYPE_BIP: + /* check for changes */ + octetstring_init( + &my_address, + &Object_List[index].Network.IPv4.IP_DHCP_Server[0], + IPV4_ADDR_SIZE); + if (!octetstring_value_same(&my_address, ip_address)) { + Object_List[index].Changes_Pending = true; + } + octetstring_copy_value( + &Object_List[index].Network.IPv4.IP_DHCP_Server[0], + IPV4_ADDR_SIZE, ip_address); + status = true; + break; + case PORT_TYPE_BIP6: + octetstring_init( + &my_address, + &Object_List[index].Network.IPv6.IP_DHCP_Server[0], + IPV6_ADDR_SIZE); + if (!octetstring_value_same(&my_address, ip_address)) { + Object_List[index].Changes_Pending = true; + } + octetstring_copy_value( + &Object_List[index].Network.IPv6.IP_DHCP_Server[0], + IPV6_ADDR_SIZE, ip_address); + status = true; + break; + default: + break; + } + } + + 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 @@ -3085,7 +3384,7 @@ bool Network_Port_IPv6_Auto_Addressing_Enable(uint32_t object_instance) index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { ipv6 = &Object_List[index].Network.IPv6; - flag = ipv6->Auto_Addressing_Enable; + flag = ipv6->IP_DHCP_Enable; } return flag; @@ -3110,11 +3409,10 @@ bool Network_Port_IPv6_Auto_Addressing_Enable_Set( index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { if (Object_List[index].Network_Type == PORT_TYPE_BIP6) { - if (Object_List[index].Network.IPv6.Auto_Addressing_Enable != - value) { + if (Object_List[index].Network.IPv6.IP_DHCP_Enable != value) { Object_List[index].Changes_Pending = true; } - Object_List[index].Network.IPv6.Auto_Addressing_Enable = value; + Object_List[index].Network.IPv6.IP_DHCP_Enable = value; status = true; } } @@ -3182,16 +3480,13 @@ static bool Network_Port_FD_BBMD_Address_Write( *error_class = ERROR_CLASS_PROPERTY; *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; break; - } else if (value->host.ip_address.length == 4) { - status = Network_Port_Remote_BBMD_IP_Address_Set( - object_instance, value->host.ip_address.value[0], - value->host.ip_address.value[1], - value->host.ip_address.value[2], - value->host.ip_address.value[3]); - if (status) { - status = Network_Port_Remote_BBMD_BIP_Port_Set( - object_instance, value->port); - } + } else if ( + ((value->host_ip_address) && + (value->host.ip_address.length == 4)) || + ((value->host_name) && + (bacnet_is_valid_hostname(&value->host.name)))) { + status = Network_Port_Remote_BBMD_Address_Set( + object_instance, value); } if (!status) { *error_class = ERROR_CLASS_PROPERTY; @@ -3204,14 +3499,13 @@ static bool Network_Port_FD_BBMD_Address_Write( *error_class = ERROR_CLASS_PROPERTY; *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; break; - } else if (value->host.ip_address.length == 16) { - status = Network_Port_Remote_BBMD_IP6_Address_Set( - object_instance, &value->host.ip_address.value[0]); - - if (status) { - status = Network_Port_Remote_BBMD_BIP6_Port_Set( - object_instance, value->port); - } + } else if ( + ((value->host_ip_address) && + (value->host.ip_address.length == 16)) || + ((value->host_name) && + (bacnet_is_valid_hostname(&value->host.name)))) { + status = Network_Port_Remote_BBMD_Address_Set( + object_instance, value); } if (!status) { *error_class = ERROR_CLASS_PROPERTY; @@ -3489,6 +3783,21 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_boolean( &apdu[0], Network_Port_IP_DHCP_Enable(rpdata->object_instance)); break; + case PROP_IP_DHCP_LEASE_TIME: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_IP_DHCP_Lease_Time(rpdata->object_instance)); + break; + case PROP_IP_DHCP_LEASE_TIME_REMAINING: + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_IP_DHCP_Lease_Time_Remaining( + rpdata->object_instance)); + break; + case PROP_IP_DHCP_SERVER: + Network_Port_IP_DHCP_Server(rpdata->object_instance, &octet_string); + apdu_len = encode_application_octet_string(&apdu[0], &octet_string); + break; case PROP_IP_DNS_SERVER: apdu_len = bacnet_array_encode( rpdata->object_instance, rpdata->array_index, @@ -3575,10 +3884,15 @@ int Network_Port_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) rpdata->object_instance)); break; case PROP_IPV6_DHCP_LEASE_TIME: - apdu_len = encode_application_unsigned(&apdu[0], 0); + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_IP_DHCP_Lease_Time(rpdata->object_instance)); break; case PROP_IPV6_DHCP_LEASE_TIME_REMAINING: - apdu_len = encode_application_unsigned(&apdu[0], 0); + apdu_len = encode_application_unsigned( + &apdu[0], + Network_Port_IP_DHCP_Lease_Time_Remaining( + rpdata->object_instance)); break; case PROP_IPV6_DHCP_SERVER: Network_Port_IPv6_DHCP_Server( diff --git a/src/bacnet/basic/object/netport.h b/src/bacnet/basic/object/netport.h index f32c1d9f..64392c21 100644 --- a/src/bacnet/basic/object/netport.h +++ b/src/bacnet/basic/object/netport.h @@ -13,6 +13,7 @@ #include "bacnet/bacdef.h" /* BACnet Stack API */ #include "bacnet/apdu.h" +#include "bacnet/hostnport.h" #include "bacnet/readrange.h" #include "bacnet/rp.h" #include "bacnet/wp.h" @@ -160,6 +161,19 @@ BACNET_STACK_EXPORT bool Network_Port_IP_DHCP_Enable(uint32_t object_instance); BACNET_STACK_EXPORT bool Network_Port_IP_DHCP_Enable_Set(uint32_t object_instance, bool value); +BACNET_STACK_EXPORT +void Network_Port_IP_DHCP_Server( + uint32_t object_instance, BACNET_OCTET_STRING *ip_address); +BACNET_STACK_EXPORT +bool Network_Port_IP_DHCP_Server_Set( + uint32_t object_instance, BACNET_OCTET_STRING *ip_address); +BACNET_STACK_EXPORT +bool Network_Port_IP_DHCP_Lease_Time_Set( + uint32_t object_instance, const uint32_t value); +BACNET_STACK_EXPORT +uint32_t Network_Port_IP_DHCP_Lease_Time(uint32_t object_instance); +BACNET_STACK_EXPORT +uint32_t Network_Port_IP_DHCP_Lease_Time_Remaining(uint32_t object_instance); BACNET_STACK_EXPORT bool Network_Port_IP_DNS_Server( @@ -217,6 +231,13 @@ 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 +bool Network_Port_Remote_BBMD_Address( + uint32_t object_instance, BACNET_HOST_N_PORT *bbmd_address); +BACNET_STACK_EXPORT +bool Network_Port_Remote_BBMD_Address_Set( + uint32_t object_instance, const BACNET_HOST_N_PORT *bbmd_address); BACNET_STACK_EXPORT uint16_t Network_Port_Remote_BBMD_BIP_Lifetime(uint32_t object_instance); BACNET_STACK_EXPORT diff --git a/src/bacnet/hostnport.c b/src/bacnet/hostnport.c index c12ae14a..fd6f0058 100644 --- a/src/bacnet/hostnport.c +++ b/src/bacnet/hostnport.c @@ -374,6 +374,210 @@ bool host_n_port_copy(BACNET_HOST_N_PORT *dst, const BACNET_HOST_N_PORT *src) return status; } +/** + * @brief Initialize a BACnetHostNPort_Minimal structure for IP address + * @param host - BACnetHostNPort_Minimal structure + * @param port - port number + * @param address - BACnetHostAddress + */ +void host_n_port_minimal_ip_init( + BACNET_HOST_N_PORT_MINIMAL *host, + uint16_t port, + const uint8_t *address, + size_t address_len) +{ + unsigned i, imax; + + if (host) { + host->tag = BACNET_HOST_ADDRESS_TAG_IP_ADDRESS; + host->host.ip_address.length = address_len; + imax = min(address_len, sizeof(host->host.ip_address.address)); + if (address) { + for (i = 0; i < imax; i++) { + host->host.ip_address.address[i] = address[i]; + } + } else { + for (i = 0; i < imax; i++) { + host->host.ip_address.address[i] = 0; + } + } + host->port = port; + } +} + +/** + * @brief copy the BACnetHostNPort_Minimal complex data + * @param dest - destination structure + * @param src - source structure + * @return true if successfully copied + */ +bool host_n_port_minimal_copy( + BACNET_HOST_N_PORT_MINIMAL *dest, const BACNET_HOST_N_PORT_MINIMAL *src) +{ + bool status = false; + int i; + + if (dest && src) { + dest->tag = src->tag; + dest->port = src->port; + if (src->tag == BACNET_HOST_ADDRESS_TAG_IP_ADDRESS) { + status = true; + dest->host.ip_address.length = src->host.ip_address.length; + for (i = 0; i < src->host.ip_address.length; i++) { + if (i < sizeof(dest->host.ip_address.address)) { + dest->host.ip_address.address[i] = + src->host.ip_address.address[i]; + } + } + } else if (src->tag == BACNET_HOST_ADDRESS_TAG_NAME) { + status = true; + dest->host.name.length = src->host.name.length; + for (i = 0; i < src->host.name.length; i++) { + if (i < sizeof(dest->host.name.fqdn)) { + dest->host.name.fqdn[i] = src->host.name.fqdn[i]; + } + } + } else if (src->tag == BACNET_HOST_ADDRESS_TAG_NONE) { + status = true; + } + } + + return status; +} + +/** + * @brief Copy the BACnetHostNPort complex data from src to dst + * @param dst - destination structure + * @param src - source structure + * @return true if successfully copied + */ +bool host_n_port_from_minimal( + BACNET_HOST_N_PORT *dst, const BACNET_HOST_N_PORT_MINIMAL *src) +{ + bool status = false; + + if (dst && src) { + switch (src->tag) { + case BACNET_HOST_ADDRESS_TAG_NONE: + dst->host_ip_address = false; + dst->host_name = false; + status = true; + break; + case BACNET_HOST_ADDRESS_TAG_IP_ADDRESS: + dst->host_ip_address = true; + dst->host_name = false; + octetstring_init( + &dst->host.ip_address, src->host.ip_address.address, + src->host.ip_address.length); + status = true; + break; + case BACNET_HOST_ADDRESS_TAG_NAME: + dst->host_ip_address = false; + dst->host_name = true; + characterstring_init( + &dst->host.name, CHARACTER_ANSI_X34, src->host.name.fqdn, + src->host.name.length); + status = true; + break; + default: + return false; /* invalid tag number */ + } + if (status) { + dst->port = src->port; + } + } + + return status; +} + +/** + * @brief Copy the BACnetHostNPort complex data from src to dst + * @param dst - destination structure + * @param src - source structure + * @return true if successfully copied + */ +bool host_n_port_to_minimal( + BACNET_HOST_N_PORT_MINIMAL *dst, const BACNET_HOST_N_PORT *src) +{ + bool status = false; + + if (dst && src) { + if (src->host_ip_address) { + dst->tag = BACNET_HOST_ADDRESS_TAG_IP_ADDRESS; + dst->host.ip_address.length = octetstring_copy_value( + dst->host.ip_address.address, + sizeof(dst->host.ip_address.address), &src->host.ip_address); + if (dst->host.ip_address.length > 0) { + status = true; + } + } else if (src->host_name) { + dst->tag = BACNET_HOST_ADDRESS_TAG_NAME; + dst->host.name.length = characterstring_copy_value( + dst->host.name.fqdn, sizeof(dst->host.name.fqdn), + &src->host.name); + if (dst->host.name.length > 0) { + status = true; + } + } else { + dst->tag = BACNET_HOST_ADDRESS_TAG_NONE; + status = true; + } + dst->port = src->port; + } + + return status; +} + +bool host_n_port_minimal_same( + const BACNET_HOST_N_PORT_MINIMAL *dst, + const BACNET_HOST_N_PORT_MINIMAL *src) +{ + bool status = false; + int i; + + if (dst && src) { + if (dst->tag == src->tag) { + if (dst->tag == BACNET_HOST_ADDRESS_TAG_IP_ADDRESS) { + if (dst->host.ip_address.length == + src->host.ip_address.length) { + status = true; + for (i = 0; i < dst->host.ip_address.length; i++) { + if (i < sizeof(dst->host.ip_address.address)) { + if (dst->host.ip_address.address[i] != + src->host.ip_address.address[i]) { + status = false; + break; + } + } + } + } + } else if (dst->tag == BACNET_HOST_ADDRESS_TAG_NAME) { + if (dst->host.name.length == src->host.name.length) { + status = true; + for (i = 0; i < dst->host.name.length; i++) { + if (i < sizeof(dst->host.name.fqdn)) { + if (dst->host.name.fqdn[i] != + src->host.name.fqdn[i]) { + status = false; + break; + } + } + } + } + } else if (dst->tag == BACNET_HOST_ADDRESS_TAG_NONE) { + status = true; + } + if (status) { + if (dst->port != src->port) { + status = false; + } + } + } + } + + return status; +} + /** * @brief Compare the BACnetHostNPort complex data of src and dst * @param host1 - host 1 structure @@ -1159,3 +1363,102 @@ int bacnet_fdt_entry_to_ascii( return len; } + +/** + * @brief Checks conformance of a hostname with: + * RFC 1123: Requirements for Internet Hosts – application and support + * RFC 2181: Clarifications to the DNS specification + * + * Host software MUST handle host names of up to 63 characters and + * SHOULD handle host names of up to 255 characters. + * + * Whenever a user inputs the identity of an Internet host, it SHOULD + * be possible to enter either (1) a host domain name or (2) an IP + * address in dotted-decimal ("#.#.#.#") form. The host SHOULD check + * the string syntactically for a dotted-decimal number before + * looking it up in the Domain Name System. + * + * The DNS itself places only one restriction on the particular labels + * that can be used to identify resource records. That one restriction + * relates to the length of the label and the full name. The length of + * any one label is limited to between 1 and 63 octets. A full domain + * name is limited to 255 octets (including the separators). The zero + * length full name is defined as representing the root of the DNS tree, + * and is typically written and displayed as ".". Those restrictions + * aside, any binary string whatever can be used as the label of any + * resource record. Similarly, any binary string can serve as the value + * of any record that includes a domain name as some or all of its value + * (SOA, NS, MX, PTR, CNAME, and any others that may be added). + * + * @param hostname - hostname as BACNET_CHARACTER_STRING + * @return true if the host name conorms to RFC 1123, false otherwise + */ +bool bacnet_is_valid_hostname(const BACNET_CHARACTER_STRING *const hostname) +{ + int len; + const char *val; + int dot_count = 0; + int i = 0; + char c; + char fqdn_copy[255 + 1] = { 0 }; + char *label = NULL; + + len = characterstring_length(hostname); + /* Check length */ + if ((len == 0) || (len > sizeof(fqdn_copy) - 1)) { + /* Invalid length */ + return false; + } + /* Check if it looks like an IP address (basic check) */ + val = characterstring_value(hostname); + for (i = 0; i < len; i++) { + if (val[i] == '.') { + dot_count++; + } + } + /* Check if it's a numeric pattern (like an incomplete IP) */ + if (dot_count > 0 && strspn(val, "0123456789.") == len) { + /* Invalid: looks like an incomplete IP */ + return false; + } + /* Check each character */ + for (i = 0; i < len; i++) { + c = val[i]; + if (!isalnum(c) && c != '-' && c != '.') { + /* Invalid character */ + return false; + } + /* Check for starting and ending hyphens */ + if (i == 0 && c == '-') { + /* Cannot start with a hyphen */ + return false; + } + if (i == len - 1 && c == '-') { + /* Cannot end with a hyphen */ + return false; + } + /* Check for consecutive periods or hyphens */ + if (i > 0 && + ((val[i] == '-' && val[i - 1] == '-') || + (val[i] == '.' && val[i - 1] == '.'))) { + /* Invalid consecutive characters */ + return false; + } + } + /* Make a copy to manipulate when checking each label */ + strncpy(fqdn_copy, val, sizeof(fqdn_copy) - 1); + /* Split FQDN by '.' */ + label = strtok(fqdn_copy, "."); + while (label != NULL) { + /* check for each label length not exceeding 63 characters */ + if (strlen(label) > 63) { + /* Invalid label found */ + return false; + } + /* Move to the next label */ + label = strtok(NULL, "."); + } + + /* Valid hostname */ + return true; +} diff --git a/src/bacnet/hostnport.h b/src/bacnet/hostnport.h index 3a715397..8eba16f4 100644 --- a/src/bacnet/hostnport.h +++ b/src/bacnet/hostnport.h @@ -15,6 +15,7 @@ /* BACnet Stack API */ #include "bacnet/bacstr.h" #include "bacnet/datalink/bvlc.h" +#include "bacnet/datalink/bvlc6.h" /** * BACnetHostNPort ::= SEQUENCE { @@ -38,6 +39,25 @@ typedef struct BACnetHostNPort { uint16_t port; } BACNET_HOST_N_PORT; +#define BACNET_HOST_ADDRESS_TAG_NONE 0 +#define BACNET_HOST_ADDRESS_TAG_IP_ADDRESS 1 +#define BACNET_HOST_ADDRESS_TAG_NAME 2 +/* BACnetHostNPort with smaller RAM footprint using C datatypes */ +typedef struct BACnetHostNPort_Minimal { + uint8_t tag; + union BACnetHostAddress_Minimal { + struct BACnetHostOctetString { + uint8_t address[IP6_ADDRESS_MAX]; + uint8_t length; + } ip_address; + struct BACnetHostCharacterString { + char fqdn[256]; + uint8_t length; + } name; + } host; + uint16_t port; +} BACNET_HOST_N_PORT_MINIMAL; + /** * BACnetBDTEntry ::= SEQUENCE { * bbmd-address [0] BACnetHostNPort, @@ -99,6 +119,26 @@ int host_n_port_context_decode( BACNET_HOST_N_PORT *address); BACNET_STACK_EXPORT bool host_n_port_copy(BACNET_HOST_N_PORT *dst, const BACNET_HOST_N_PORT *src); + +BACNET_STACK_EXPORT +bool host_n_port_from_minimal( + BACNET_HOST_N_PORT *dst, const BACNET_HOST_N_PORT_MINIMAL *src); +BACNET_STACK_EXPORT +bool host_n_port_to_minimal( + BACNET_HOST_N_PORT_MINIMAL *dst, const BACNET_HOST_N_PORT *src); +BACNET_STACK_EXPORT +bool host_n_port_minimal_copy( + BACNET_HOST_N_PORT_MINIMAL *dst, const BACNET_HOST_N_PORT_MINIMAL *src); +BACNET_STACK_EXPORT +bool host_n_port_minimal_same( + const BACNET_HOST_N_PORT_MINIMAL *dst, + const BACNET_HOST_N_PORT_MINIMAL *src); +void host_n_port_minimal_ip_init( + BACNET_HOST_N_PORT_MINIMAL *host, + uint16_t port, + const uint8_t *address, + size_t address_len); + BACNET_STACK_EXPORT bool host_n_port_same( const BACNET_HOST_N_PORT *dst, const BACNET_HOST_N_PORT *src); @@ -162,6 +202,8 @@ bool bacnet_fdt_entry_from_ascii(BACNET_FDT_ENTRY *value, const char *argv); BACNET_STACK_EXPORT int bacnet_fdt_entry_to_ascii( char *str, size_t str_size, const BACNET_FDT_ENTRY *value); +BACNET_STACK_EXPORT +bool bacnet_is_valid_hostname(const BACNET_CHARACTER_STRING *const hostname); #ifdef __cplusplus } diff --git a/test/bacnet/basic/object/netport/CMakeLists.txt b/test/bacnet/basic/object/netport/CMakeLists.txt index 31443627..17fe577b 100644 --- a/test/bacnet/basic/object/netport/CMakeLists.txt +++ b/test/bacnet/basic/object/netport/CMakeLists.txt @@ -29,6 +29,7 @@ add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 BACDL_ALL=1 + BACNET_NETWORK_PORT_IP_DHCP_ENABLED=1 BACNET_SECURE_CONNECT_ROUTING_TABLE=1 BSC_CONF_HUB_FUNCTIONS_NUM=1 BSC_CONF_HUB_CONNECTORS_NUM=1 diff --git a/test/bacnet/basic/object/netport/src/main.c b/test/bacnet/basic/object/netport/src/main.c index 6759222a..2d24784e 100644 --- a/test/bacnet/basic/object/netport/src/main.c +++ b/test/bacnet/basic/object/netport/src/main.c @@ -37,32 +37,51 @@ static void test_network_port(void) }; uint8_t test_address[16] = { 0 }; uint8_t mac_len; + uint8_t max_master, max_info_frames; uint8_t ip_prefix; - uint32_t object_instance = 0; + uint32_t object_instance = 1234, test_object_instance = 0; + unsigned object_index = 0, test_object_index = 0; + BACNET_HOST_N_PORT host_n_port, test_host_n_port = { 0 }; + BACNET_OCTET_STRING ip_address; + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY bdt[2] = { + { .valid = true, + .dest_address = { .address = { 1, 2, 3, 4 }, .port = 47808 }, + .broadcast_mask = { .address = { 255, 255, 255, 255 } } }, + { .valid = true, + .dest_address = { .address = { 5, 6, 7, 8 }, .port = 47808 }, + .broadcast_mask = { .address = { 255, 255, 255, 0 } } } + }; + BACNET_IP_BROADCAST_DISTRIBUTION_TABLE_ENTRY *test_bdt; + BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY fdt[2] = { + { .valid = true, + .dest_address = { .address = { 1, 2, 3, 4 }, .port = 47808 }, + .ttl_seconds = 30, + .ttl_seconds_remaining = 30 }, + { .valid = true, + .dest_address = { .address = { 5, 6, 7, 8 }, .port = 47808 }, + .ttl_seconds = 60, + .ttl_seconds_remaining = 60 } + }; + BACNET_IP_FOREIGN_DEVICE_TABLE_ENTRY *test_fdt; uint8_t port_type[] = { PORT_TYPE_ETHERNET, PORT_TYPE_ARCNET, PORT_TYPE_MSTP, PORT_TYPE_PTP, PORT_TYPE_LONTALK, PORT_TYPE_BIP, PORT_TYPE_ZIGBEE, PORT_TYPE_VIRTUAL, PORT_TYPE_NON_BACNET, PORT_TYPE_BIP6, PORT_TYPE_BSC, PORT_TYPE_MAX }; - const int known_fail_property_list[] = { - PROP_IP_DNS_SERVER, - PROP_BBMD_BROADCAST_DISTRIBUTION_TABLE, - PROP_BBMD_FOREIGN_DEVICE_TABLE, - PROP_FD_BBMD_ADDRESS, - PROP_IPV6_DNS_SERVER, - PROP_ISSUER_CERTIFICATE_FILES, - PROP_SC_HUB_FUNCTION_CONNECTION_STATUS, - PROP_SC_DIRECT_CONNECT_CONNECTION_STATUS, - PROP_SC_FAILED_CONNECTION_REQUESTS, - -1 - }; + const int known_fail_property_list[] = { -1 }; while (port_type[port] != PORT_TYPE_MAX) { Network_Port_Init(); - object_instance = 1234; - status = Network_Port_Object_Instance_Number_Set(0, object_instance); + status = Network_Port_Object_Instance_Number_Set( + object_index, object_instance); zassert_true(status, NULL); + test_object_index = Network_Port_Instance_To_Index(object_instance); + zassert_equal(object_index, test_object_index, NULL); + test_object_instance = Network_Port_Index_To_Instance(object_index); + zassert_equal(object_instance, test_object_instance, NULL); + test_object_instance = Network_Port_Index_To_Instance(UINT_MAX); + zassert_equal(test_object_instance, BACNET_MAX_INSTANCE, NULL); status = Network_Port_Type_Set(object_instance, port_type[port]); zassert_true(status, NULL); count = Network_Port_Count(); @@ -118,8 +137,18 @@ static void test_network_port(void) zassert_equal(address[0], 127, NULL); status = Network_Port_MSTP_Max_Info_Frames_Set(object_instance, 1); zassert_true(status, NULL); - zassert_equal( - Network_Port_MSTP_Max_Info_Frames(object_instance), 1, NULL); + max_info_frames = + Network_Port_MSTP_Max_Info_Frames(object_instance); + zassert_equal(max_info_frames, 1, NULL); + Network_Port_MSTP_Max_Master_Set(object_instance, 127); + max_master = Network_Port_MSTP_Max_Master(object_instance); + zassert_equal(max_master, 127, NULL); + Network_Port_Changes_Pending_Set(object_instance, false); + status = Network_Port_MSTP_Max_Master_Set(object_instance, 63); + zassert_true(status, NULL); + max_master = Network_Port_MSTP_Max_Master(object_instance); + zassert_equal(max_master, 63, NULL); + zassert_true(Network_Port_Changes_Pending(object_instance), NULL); } if (port_type[port] == PORT_TYPE_BIP) { status = Network_Port_IP_Address_Set(object_instance, 1, 2, 3, 4); @@ -134,6 +163,16 @@ static void test_network_port(void) zassert_true(status, NULL); status = Network_Port_IP_DHCP_Enable(object_instance); zassert_true(status, NULL); + Network_Port_IP_DHCP_Lease_Time_Set(object_instance, 60); + zassert_equal( + Network_Port_IP_DHCP_Lease_Time(object_instance), 60, NULL); + zassert_equal( + Network_Port_IP_DHCP_Lease_Time_Remaining(object_instance), 60, + NULL); + octetstring_init_ascii_hex(&ip_address, "0x0A0B0C0D"); + status = + Network_Port_IP_DHCP_Server_Set(object_instance, &ip_address); + zassert_true(status, NULL); status = Network_Port_IP_DNS_Server_Set( object_instance, 0, 9, 10, 11, 12); zassert_true(status, NULL); @@ -151,21 +190,70 @@ static void test_network_port(void) status = Network_Port_BBMD_BD_Table_Set(object_instance, NULL); zassert_true(status, NULL); zassert_is_null(Network_Port_BBMD_BD_Table(object_instance), NULL); + bvlc_broadcast_distribution_table_link_array(bdt, ARRAY_SIZE(bdt)); + status = Network_Port_BBMD_BD_Table_Set(object_instance, bdt); + zassert_true(status, NULL); + test_bdt = Network_Port_BBMD_BD_Table(object_instance); + zassert_not_null(test_bdt, NULL); + zassert_equal( + test_bdt->dest_address.address[0], + bdt[0].dest_address.address[0], NULL); status = Network_Port_BBMD_FD_Table_Set(object_instance, NULL); zassert_true(status, NULL); zassert_is_null(Network_Port_BBMD_FD_Table(object_instance), NULL); + bvlc_foreign_device_table_link_array(fdt, ARRAY_SIZE(fdt)); + status = Network_Port_BBMD_FD_Table_Set(object_instance, &fdt[0]); + zassert_true(status, NULL); + test_fdt = Network_Port_BBMD_FD_Table(object_instance); + zassert_not_null(test_fdt, NULL); + zassert_equal( + test_fdt->dest_address.address[0], + fdt[0].dest_address.address[0], NULL); status = Network_Port_Remote_BBMD_IP_Address( object_instance, NULL, NULL, NULL, NULL); - zassert_true(status, NULL); + zassert_false(status, NULL); status = Network_Port_Remote_BBMD_IP_Address_Set( object_instance, 1, 2, 3, 4); zassert_true(status, NULL); + status = Network_Port_Remote_BBMD_IP_Address( + object_instance, &test_address[0], &test_address[1], + &test_address[2], &test_address[3]); + zassert_true(status, NULL); + status = Network_Port_Remote_BBMD_IP_Address( + object_instance, NULL, NULL, NULL, NULL); + zassert_true(status, NULL); + zassert_equal(test_address[0], 1, NULL); + zassert_equal(test_address[1], 2, NULL); + zassert_equal(test_address[2], 3, NULL); + zassert_equal(test_address[3], 4, NULL); + Network_Port_Changes_Pending_Set(object_instance, false); + status = Network_Port_Remote_BBMD_IP_Address_Set( + object_instance, 5, 6, 7, 8); + zassert_true(status, NULL); + zassert_true(Network_Port_Changes_Pending(object_instance), NULL); status = Network_Port_Remote_BBMD_BIP_Port_Set(object_instance, 47808); zassert_true(status, NULL); zassert_equal( Network_Port_Remote_BBMD_BIP_Port(object_instance), 47808, NULL); + Network_Port_Remote_BBMD_Address(object_instance, &host_n_port); + zassert_false(host_n_port.host_name, NULL); + zassert_true(host_n_port.host_ip_address, NULL); + zassert_equal(host_n_port.port, 47808, NULL); + test_host_n_port.host_ip_address = false; + test_host_n_port.host_name = true; + characterstring_init_ansi( + &test_host_n_port.host.name, "bbmd.example.com"); + test_host_n_port.port = 47808; + Network_Port_Remote_BBMD_Address_Set( + object_instance, &test_host_n_port); + Network_Port_Remote_BBMD_Address(object_instance, &host_n_port); + zassert_false(host_n_port.host_ip_address, NULL); + zassert_true(host_n_port.host_name, NULL); + zassert_equal(host_n_port.port, 47808, NULL); + characterstring_ansi_same( + &test_host_n_port.host.name, "bbmd.example.com"); status = Network_Port_Remote_BBMD_BIP_Lifetime_Set(object_instance, 60); zassert_true(status, NULL); @@ -192,16 +280,33 @@ static void test_network_port(void) Network_Port_BBMD_IP6_FD_Table(object_instance), NULL); status = Network_Port_Remote_BBMD_IP6_Address(object_instance, address); - zassert_true(status, NULL); + zassert_false(status, NULL); status = Network_Port_Remote_BBMD_IP6_Address_Set( object_instance, address); zassert_true(status, NULL); status = Network_Port_Remote_BBMD_BIP6_Port_Set(object_instance, 47808); zassert_true(status, NULL); + status = + Network_Port_Remote_BBMD_IP6_Address(object_instance, address); + zassert_true(status, NULL); zassert_equal( Network_Port_Remote_BBMD_BIP6_Port(object_instance), 47808, NULL); + Network_Port_Remote_BBMD_Address(object_instance, &host_n_port); + zassert_true(host_n_port.host_ip_address, NULL); + zassert_equal(host_n_port.port, 47808, NULL); + test_host_n_port.host_ip_address = false; + test_host_n_port.host_name = true; + characterstring_init_ansi( + &test_host_n_port.host.name, "bbmd.example.com"); + test_host_n_port.port = 47808; + Network_Port_Remote_BBMD_Address_Set( + object_instance, &test_host_n_port); + Network_Port_Remote_BBMD_Address(object_instance, &host_n_port); + zassert_false(host_n_port.host_ip_address, NULL); + zassert_true(host_n_port.host_name, NULL); + zassert_equal(host_n_port.port, 47808, NULL); status = Network_Port_Remote_BBMD_BIP6_Lifetime_Set(object_instance, 60); zassert_true(status, NULL); @@ -226,8 +331,21 @@ static void test_network_port(void) status = Network_Port_IPv6_Multicast_Address_Set( object_instance, address); zassert_true(status, NULL); + status = Network_Port_IP_DHCP_Enable_Set(object_instance, true); + zassert_true(status, NULL); + status = Network_Port_IP_DHCP_Enable(object_instance); + zassert_true(status, NULL); + Network_Port_IP_DHCP_Lease_Time_Set(object_instance, 60); + zassert_equal( + Network_Port_IP_DHCP_Lease_Time(object_instance), 60, NULL); + zassert_equal( + Network_Port_IP_DHCP_Lease_Time_Remaining(object_instance), 60, + NULL); status = Network_Port_IPv6_DHCP_Server_Set(object_instance, address); + octetstring_init(&ip_address, address, sizeof(address)); + status = + Network_Port_IP_DHCP_Server_Set(object_instance, &ip_address); zassert_true(status, NULL); status = Network_Port_BIP6_Port_Set(object_instance, 47808); zassert_true(status, NULL); @@ -257,7 +375,10 @@ static void test_network_port(void) Network_Port_Object_Name_ASCII); port++; Network_Port_Changes_Activate(); + zassert_false(Network_Port_Changes_Pending(object_instance), NULL); + Network_Port_Changes_Pending_Set(object_instance, true); Network_Port_Changes_Discard(); + zassert_false(Network_Port_Changes_Pending(object_instance), NULL); Network_Port_Cleanup(); } diff --git a/test/bacnet/hostnport/src/main.c b/test/bacnet/hostnport/src/main.c index ad3ebb6b..57c2589a 100644 --- a/test/bacnet/hostnport/src/main.c +++ b/test/bacnet/hostnport/src/main.c @@ -12,6 +12,43 @@ #include #include +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(create_object_tests, test_HostNPortMinimal_Init) +#else +static void test_HostNPortMinimal_Init(void) +#endif +{ + BACNET_HOST_N_PORT_MINIMAL test_data_1 = { 0 }, test_data_2 = { 0 }; + BACNET_HOST_N_PORT host_n_port = { 0 }; + uint8_t address[IP6_ADDRESS_MAX] = { 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16 }; + uint16_t port = 0xBAC0; + + /* test the init */ + host_n_port_minimal_ip_init(&test_data_1, port, address, sizeof(address)); + zassert_equal(test_data_1.tag, BACNET_HOST_ADDRESS_TAG_IP_ADDRESS, NULL); + zassert_equal(test_data_1.port, port, NULL); + zassert_equal(test_data_1.host.ip_address.length, sizeof(address), NULL); + zassert_mem_equal( + test_data_1.host.ip_address.address, address, sizeof(address), NULL); + host_n_port_from_minimal(&host_n_port, &test_data_1); + host_n_port_to_minimal(&test_data_2, &host_n_port); + zassert_true( + host_n_port_minimal_same(&test_data_1, &test_data_2), + "test_data_1 != test_data_2"); +} + +static void test_HostNPortMinimal_Copy(BACNET_HOST_N_PORT *data) +{ + BACNET_HOST_N_PORT_MINIMAL test_data_1 = { 0 }, test_data_2 = { 0 }; + + host_n_port_to_minimal(&test_data_1, data); + host_n_port_minimal_copy(&test_data_2, &test_data_1); + zassert_true( + host_n_port_minimal_same(&test_data_1, &test_data_2), + "test_data_1 != test_data_2"); +} + static void test_HostNPortCodec(BACNET_HOST_N_PORT *data) { uint8_t apdu[MAX_APDU] = { 0 }; @@ -72,12 +109,82 @@ static void test_HostNPort(void) data.host_name = false; data.port = 0xBAC0; test_HostNPortCodec(&data); + test_HostNPortMinimal_Copy(&data); /* Host Name */ characterstring_init_ansi(&data.host.name, "bacnet.org"); data.host_ip_address = false; data.host_name = true; data.port = 0xBAC0; test_HostNPortCodec(&data); + test_HostNPortMinimal_Copy(&data); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(create_object_tests, test_is_valid_hostname) +#else +static void test_is_valid_hostname(void) +#endif +{ + bool status = false; + BACNET_CHARACTER_STRING hostname; + + status = characterstring_init_ansi(&hostname, "valid.host.name"); + zassert_true(status, NULL); + status = bacnet_is_valid_hostname(&hostname); + zassert_true(status, NULL); + + status = characterstring_init_ansi(&hostname, "invalid..host.name"); + zassert_true(status, NULL); + status = bacnet_is_valid_hostname(&hostname); + zassert_false(status, NULL); + + status = characterstring_init_ansi(&hostname, "-invalid.host.name"); + zassert_true(status, NULL); + status = bacnet_is_valid_hostname(&hostname); + zassert_false(status, NULL); + + status = characterstring_init_ansi(&hostname, "valid.host-name"); + zassert_true(status, NULL); + status = bacnet_is_valid_hostname(&hostname); + zassert_true(status, NULL); + + status = characterstring_init_ansi(&hostname, "invalid.host--name"); + zassert_true(status, NULL); + status = bacnet_is_valid_hostname(&hostname); + zassert_false(status, NULL); + + status = characterstring_init_ansi(&hostname, "invalid.host name"); + zassert_true(status, NULL); + status = bacnet_is_valid_hostname(&hostname); + zassert_false(status, NULL); + + status = characterstring_init_ansi(&hostname, "valid.host.123.name"); + zassert_true(status, NULL); + status = bacnet_is_valid_hostname(&hostname); + zassert_true(status, NULL); + + { + char too_long_label[65] = { 0 }; + memset(too_long_label, 'l', sizeof(too_long_label) - 1); + status = characterstring_init_ansi(&hostname, too_long_label); + zassert_true(status, NULL); + status = bacnet_is_valid_hostname(&hostname); + zassert_false(status, NULL); + } + + { + char max_length_label[64] = { 0 }; + memset(max_length_label, 'l', sizeof(max_length_label) - 1); + status = characterstring_init_ansi(&hostname, max_length_label); + zassert_true(status, NULL); + status = bacnet_is_valid_hostname(&hostname); + zassert_true(status, NULL); + } + + status = characterstring_init_ansi(&hostname, "non-alpanumer!c.host name"); + zassert_true(status, NULL); + status = bacnet_is_valid_hostname(&hostname); + zassert_false(status, NULL); } #if defined(CONFIG_ZTEST_NEW_API) @@ -85,7 +192,10 @@ ZTEST_SUITE(host_n_port_tests, NULL, NULL, NULL, NULL, NULL); #else void test_main(void) { - ztest_test_suite(host_n_port_tests, ztest_unit_test(test_HostNPort)); + ztest_test_suite( + host_n_port_tests, ztest_unit_test(test_HostNPort), + ztest_unit_test(test_HostNPortMinimal_Init), + ztest_unit_test(test_is_valid_hostname)); ztest_run_test_suite(host_n_port_tests); }