Fixed network port object to accept host name option of host-n-port writes. (#997)

This commit is contained in:
Steve Karg
2025-05-20 15:44:47 -05:00
committed by GitHub
parent 524fd162f9
commit b3fd374c84
9 changed files with 1077 additions and 133 deletions
+29
View File
@@ -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.
+3
View File
@@ -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 */
+427 -113
View File
@@ -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(
+21
View File
@@ -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
+303
View File
@@ -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;
}
+42
View File
@@ -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
}