From f616a8cc686c4307e8d56141152aa48f4a3286ff Mon Sep 17 00:00:00 2001 From: Leunar Kalludra Date: Fri, 24 Apr 2026 13:40:32 +0200 Subject: [PATCH] bip: decouple broadcast destination port from bind port (#1311) Co-authored-by: Leunar Kalludra --- ports/arduino_uno/bip-init.c | 1 + ports/arduino_uno/bip.c | 21 ++++++- ports/arduino_uno/bip.h | 2 + ports/bsd/bip-init.c | 30 ++++++++- ports/esp32/src/bip.c | 32 +++++++++- ports/esp32/src/bip.h | 12 ++++ ports/esp32/src/bip_init.c | 2 + ports/linux/bip-init.c | 30 ++++++++- ports/lwip/bip.c | 46 +++++++++++++- ports/pico/bip-init.c | 2 + ports/pico/bip.c | 24 ++++++- ports/pico/bip.h | 2 + ports/uip/bip.c | 24 ++++++- ports/win32/bip-init.c | 31 ++++++++- src/bacnet/datalink/bip.h | 6 ++ src/bacnet/datalink/dlenv.c | 6 ++ test/CMakeLists.txt | 3 + test/bacnet/basic/bbmd/src/main.c | 29 +++++++++ test/bacnet/datalink/mock/src/bip-mock.c | 10 +++ test/ports/bip_broadcast_port/CMakeLists.txt | 63 +++++++++++++++++++ .../ports/bip_broadcast_port/src/bvlc_stubs.c | 48 ++++++++++++++ test/ports/bip_broadcast_port/src/main.c | 62 ++++++++++++++++++ 22 files changed, 469 insertions(+), 17 deletions(-) create mode 100644 test/ports/bip_broadcast_port/CMakeLists.txt create mode 100644 test/ports/bip_broadcast_port/src/bvlc_stubs.c create mode 100644 test/ports/bip_broadcast_port/src/main.c diff --git a/ports/arduino_uno/bip-init.c b/ports/arduino_uno/bip-init.c index e08861f5..ce95a6a8 100644 --- a/ports/arduino_uno/bip-init.c +++ b/ports/arduino_uno/bip-init.c @@ -141,6 +141,7 @@ void bip_cleanup(void) close_func(sock_fd); } bip_set_socket(MAX_SOCK_NUM); + bip_set_broadcast_port(0); return; } diff --git a/ports/arduino_uno/bip.c b/ports/arduino_uno/bip.c index 5b72ee6a..68dc7ed8 100644 --- a/ports/arduino_uno/bip.c +++ b/ports/arduino_uno/bip.c @@ -24,6 +24,8 @@ static uint8_t BIP_Socket = MAX_SOCK_NUM; /* port to use - stored in network byte order */ static uint16_t BIP_Port = 0; /* this will force initialization in demos */ +/* broadcast destination port to use */ +static uint16_t BIP_Broadcast_Port; /* IP Address - stored in network byte order */ // static struct in_addr BIP_Address; static uint8_t BIP_Address[4] = { 0, 0, 0, 0 }; @@ -105,12 +107,26 @@ void bip_set_port(uint16_t port) BIP_Port = htons(port); } +void bip_set_broadcast_port(uint16_t port) +{ /* in network byte order */ + BIP_Broadcast_Port = htons(port); +} + /* returns network byte order */ uint16_t bip_get_port(void) { return ntohs(BIP_Port); } +uint16_t bip_get_broadcast_port(void) +{ + if (BIP_Broadcast_Port) { + return ntohs(BIP_Broadcast_Port); + } + + return ntohs(BIP_Port); +} + static int bip_decode_bip_address( const BACNET_ADDRESS *bac_addr, uint8_t *address, /* in network format */ @@ -166,7 +182,7 @@ int bip_send_pdu( for (uint8_t i = 0; i < 4; i++) { address[i] = BIP_Broadcast_Address[i]; } - port = BIP_Port; + port = htons(bip_get_broadcast_port()); mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; #ifdef DEBUG fprintf( @@ -386,11 +402,12 @@ void bip_get_my_address(BACNET_ADDRESS *my_address) void bip_get_broadcast_address(BACNET_ADDRESS *dest) { /* destination address */ int i = 0; /* counter */ + uint16_t port = htons(bip_get_broadcast_port()); if (dest) { dest->mac_len = 6; memcpy(&dest->mac[0], &BIP_Broadcast_Address, 4); - memcpy(&dest->mac[4], &BIP_Port, 2); + memcpy(&dest->mac[4], &port, 2); dest->net = BACNET_BROADCAST_NETWORK; dest->len = 0; /* no SLEN */ for (i = 0; i < MAX_MAC_LEN; i++) { diff --git a/ports/arduino_uno/bip.h b/ports/arduino_uno/bip.h index 431a1b12..fa12026a 100644 --- a/ports/arduino_uno/bip.h +++ b/ports/arduino_uno/bip.h @@ -65,8 +65,10 @@ uint16_t bip_receive( /* use host byte order for setting */ void bip_set_port(uint16_t port); +void bip_set_broadcast_port(uint16_t port); /* returns host byte order */ uint16_t bip_get_port(void); +uint16_t bip_get_broadcast_port(void); /* use network byte order for setting */ void bip_set_addr(const uint8_t *net_address); diff --git a/ports/bsd/bip-init.c b/ports/bsd/bip-init.c index 9a3a5661..6516b529 100644 --- a/ports/bsd/bip-init.c +++ b/ports/bsd/bip-init.c @@ -25,6 +25,8 @@ static int BIP_Broadcast_Socket = -1; /* port to use - stored here in network byte order */ /* Initialize to 0 - this will force initialization in demo apps */ static uint16_t BIP_Port; +/* broadcast destination port to use */ +static uint16_t BIP_Broadcast_Port; /* IP address - stored here in network byte order */ static struct in_addr BIP_Address; /* IP broadcast address - stored here in network byte order */ @@ -103,6 +105,15 @@ void bip_set_port(uint16_t port) BIP_Port = htons(port); } +/** + * @brief Set the BACnet IPv4 UDP broadcast destination port number + * @param port - IPv4 UDP port number - in host byte order + */ +void bip_set_broadcast_port(uint16_t port) +{ + BIP_Broadcast_Port = htons(port); +} + /** * @brief Get the BACnet IPv4 UDP port number * @return IPv4 UDP port number - in host byte order @@ -112,6 +123,19 @@ uint16_t bip_get_port(void) return ntohs(BIP_Port); } +/** + * @brief Get the BACnet IPv4 UDP broadcast destination port number + * @return IPv4 UDP port number - in host byte order + */ +uint16_t bip_get_broadcast_port(void) +{ + if (BIP_Broadcast_Port) { + return ntohs(BIP_Broadcast_Port); + } + + return ntohs(BIP_Port); +} + /** * @brief Get the IPv4 address for my interface. Used for sending src address. * @param addr - BACnet datalink address @@ -143,11 +167,12 @@ void bip_get_my_address(BACNET_ADDRESS *addr) void bip_get_broadcast_address(BACNET_ADDRESS *dest) { int i = 0; /* counter */ + uint16_t port = htons(bip_get_broadcast_port()); if (dest) { dest->mac_len = 6; memcpy(&dest->mac[0], &BIP_Broadcast_Addr.s_addr, 4); - memcpy(&dest->mac[4], &BIP_Port, 2); + memcpy(&dest->mac[4], &port, 2); dest->net = BACNET_BROADCAST_NETWORK; dest->len = 0; /* no SLEN */ for (i = 0; i < MAX_MAC_LEN; i++) { @@ -207,7 +232,7 @@ bool bip_get_broadcast_addr(BACNET_IP_ADDRESS *addr) { if (addr) { memcpy(&addr->address[0], &BIP_Broadcast_Addr.s_addr, 4); - addr->port = ntohs(BIP_Port); + addr->port = bip_get_broadcast_port(); } return true; @@ -814,6 +839,7 @@ void bip_cleanup(void) /* these were set non-zero during interface configuration */ BIP_Address.s_addr = 0; BIP_Broadcast_Addr.s_addr = 0; + BIP_Broadcast_Port = 0; return; } diff --git a/ports/esp32/src/bip.c b/ports/esp32/src/bip.c index cbe80a42..340de83a 100644 --- a/ports/esp32/src/bip.c +++ b/ports/esp32/src/bip.c @@ -23,6 +23,8 @@ static uint8_t BIP_Socket = MAX_SOCK_NUM; static uint16_t BIP_Port = 0; +/* broadcast destination port to use */ +static uint16_t BIP_Broadcast_Port = 0; static uint8_t BIP_Address[4] = { 0, 0, 0, 0 }; static uint8_t BIP_Broadcast_Address[4] = { 0, 0, 0, 0 }; @@ -130,6 +132,15 @@ void bip_set_port(uint16_t port) BIP_Port = port; } +/** + * @brief Store the UDP broadcast destination port used by BACnet/IP + * @param port UDP port number + */ +void bip_set_broadcast_port(uint16_t port) +{ + BIP_Broadcast_Port = port; +} + /** * @brief Get the UDP port used by BACnet/IP * @return UDP port number @@ -139,6 +150,19 @@ uint16_t bip_get_port(void) return BIP_Port; } +/** + * @brief Get the UDP broadcast destination port used by BACnet/IP + * @return UDP port number + */ +uint16_t bip_get_broadcast_port(void) +{ + if (BIP_Broadcast_Port) { + return BIP_Broadcast_Port; + } + + return BIP_Port; +} + /** * @brief Decode a BACnet/IP MAC address into IPv4 address and port parts * @param bac_addr source BACnet address @@ -193,7 +217,7 @@ int bip_send_pdu( for (i = 0; i < 4; i++) { address[i] = BIP_Broadcast_Address[i]; } - port = BIP_Port; + port = bip_get_broadcast_port(); mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; } else if (dest->mac_len == 6) { bip_decode_bip_address(dest, address, &port); @@ -330,7 +354,11 @@ void bip_get_broadcast_address(BACNET_ADDRESS *dest) if (dest) { dest->mac_len = 6; memcpy(&dest->mac[0], &BIP_Broadcast_Address[0], 4); - memcpy(&dest->mac[4], &BIP_Port, 2); + { + uint16_t port = bip_get_broadcast_port(); + + memcpy(&dest->mac[4], &port, 2); + } dest->net = BACNET_BROADCAST_NETWORK; dest->len = 0; } diff --git a/ports/esp32/src/bip.h b/ports/esp32/src/bip.h index 4b68b7f4..52eb6567 100644 --- a/ports/esp32/src/bip.h +++ b/ports/esp32/src/bip.h @@ -172,12 +172,24 @@ uint16_t bip_receive( */ void bip_set_port(uint16_t port); +/** + * @brief Store the UDP broadcast destination port used by BACnet/IP + * @param port UDP port number + */ +void bip_set_broadcast_port(uint16_t port); + /** * @brief Get the UDP port used by BACnet/IP * @return UDP port number */ uint16_t bip_get_port(void); +/** + * @brief Get the UDP broadcast destination port used by BACnet/IP + * @return UDP port number + */ +uint16_t bip_get_broadcast_port(void); + /** * @brief Store the local IPv4 address * @param net_address pointer to the 4-byte address diff --git a/ports/esp32/src/bip_init.c b/ports/esp32/src/bip_init.c index 14ebb9dd..4b8bcc31 100644 --- a/ports/esp32/src/bip_init.c +++ b/ports/esp32/src/bip_init.c @@ -93,4 +93,6 @@ void bip_cleanup(void) if (bip_valid()) { bip_socket_cleanup(); } + + bip_set_broadcast_port(0); } diff --git a/ports/linux/bip-init.c b/ports/linux/bip-init.c index 4c347464..d5ae1613 100644 --- a/ports/linux/bip-init.c +++ b/ports/linux/bip-init.c @@ -41,6 +41,8 @@ static int BIP_Broadcast_Socket = -1; /* port to use - stored here in network byte order */ /* Initialize to 0 - this will force initialization in demo apps */ static uint16_t BIP_Port; +/* broadcast destination port to use */ +static uint16_t BIP_Broadcast_Port; /* IP address - stored here in network byte order */ static struct in_addr BIP_Address; /* IP broadcast address - stored here in network byte order */ @@ -119,6 +121,15 @@ void bip_set_port(uint16_t port) BIP_Port = htons(port); } +/** + * @brief Set the BACnet IPv4 UDP broadcast destination port number + * @param port - IPv4 UDP port number - in host byte order + */ +void bip_set_broadcast_port(uint16_t port) +{ + BIP_Broadcast_Port = htons(port); +} + /** * @brief Get the BACnet IPv4 UDP port number * @return IPv4 UDP port number - in host byte order @@ -128,6 +139,19 @@ uint16_t bip_get_port(void) return ntohs(BIP_Port); } +/** + * @brief Get the BACnet IPv4 UDP broadcast destination port number + * @return IPv4 UDP port number - in host byte order + */ +uint16_t bip_get_broadcast_port(void) +{ + if (BIP_Broadcast_Port) { + return ntohs(BIP_Broadcast_Port); + } + + return ntohs(BIP_Port); +} + /** * @brief Get the IPv4 address for my interface. Used for sending src address. * @param addr - BACnet datalink address @@ -159,11 +183,12 @@ void bip_get_my_address(BACNET_ADDRESS *addr) void bip_get_broadcast_address(BACNET_ADDRESS *dest) { int i = 0; /* counter */ + uint16_t port = htons(bip_get_broadcast_port()); if (dest) { dest->mac_len = 6; memcpy(&dest->mac[0], &BIP_Broadcast_Addr.s_addr, 4); - memcpy(&dest->mac[4], &BIP_Port, 2); + memcpy(&dest->mac[4], &port, 2); dest->net = BACNET_BROADCAST_NETWORK; dest->len = 0; /* no SLEN */ for (i = 0; i < MAX_MAC_LEN; i++) { @@ -223,7 +248,7 @@ bool bip_get_broadcast_addr(BACNET_IP_ADDRESS *addr) { if (addr) { memcpy(&addr->address[0], &BIP_Broadcast_Addr.s_addr, 4); - addr->port = ntohs(BIP_Port); + addr->port = bip_get_broadcast_port(); } return true; @@ -1007,6 +1032,7 @@ void bip_cleanup(void) BIP_Address.s_addr = 0; BIP_Broadcast_Addr.s_addr = 0; BIP_Netmask.s_addr = 0; + BIP_Broadcast_Port = 0; return; } diff --git a/ports/lwip/bip.c b/ports/lwip/bip.c index be702943..87430c4c 100644 --- a/ports/lwip/bip.c +++ b/ports/lwip/bip.c @@ -24,6 +24,8 @@ static bool BIP_Port_Changed; static BACNET_IP_ADDRESS BIP_Address; /* Broadcast Address */ static BACNET_IP_ADDRESS BIP_Broadcast_Address; +/* broadcast destination port to use */ +static uint16_t BIP_Broadcast_Port; /* lwIP socket, of sorts */ static struct udp_pcb *Server_upcb; /* track packets for diagnostics */ @@ -89,7 +91,14 @@ bool bip_get_addr(BACNET_IP_ADDRESS *addr) */ bool bip_set_broadcast_addr(const BACNET_IP_ADDRESS *addr) { - return bvlc_address_copy(&BIP_Broadcast_Address, addr); + bool status = false; + + status = bvlc_address_copy(&BIP_Broadcast_Address, addr); + if (status) { + BIP_Broadcast_Port = htons(addr->port); + } + + return status; } /** @@ -99,7 +108,14 @@ bool bip_set_broadcast_addr(const BACNET_IP_ADDRESS *addr) */ bool bip_get_broadcast_addr(BACNET_IP_ADDRESS *addr) { - return bvlc_address_copy(addr, &BIP_Broadcast_Address); + bool status = false; + + status = bvlc_address_copy(addr, &BIP_Broadcast_Address); + if (status && addr) { + addr->port = ntohs(BIP_Broadcast_Port); + } + + return status; } /** @@ -114,6 +130,16 @@ void bip_set_port(uint16_t port) } } +/** + * @brief Set the BACnet IPv4 UDP broadcast destination port number + * @param port - IPv4 UDP port number - in host byte order + */ +void bip_set_broadcast_port(uint16_t port) +{ + BIP_Broadcast_Port = htons(port); + BIP_Broadcast_Address.port = BIP_Broadcast_Port; +} + /** * @brief Determine if the BACnet IPv4 UDP port number changed * @return true of the BACnet IPv4 UDP port number changed @@ -132,6 +158,19 @@ uint16_t bip_get_port(void) return ntohs(BIP_Address.port); } +/** + * @brief Get the BACnet IPv4 UDP broadcast destination port number + * @return IPv4 UDP port number - in host byte order + */ +uint16_t bip_get_broadcast_port(void) +{ + if (BIP_Broadcast_Port) { + return ntohs(BIP_Broadcast_Port); + } + + return ntohs(BIP_Address.port); +} + /** * @brief Convert the BACnet IPv4 address * @param address - IPv4 address from LwIP @@ -321,11 +360,12 @@ void bip_get_my_address(BACNET_ADDRESS *my_address) void bip_get_broadcast_address(BACNET_ADDRESS *dest) { int i = 0; /* counter */ + uint16_t port = htons(bip_get_broadcast_port()); if (dest) { dest->mac_len = 6; memcpy(&dest->mac[0], &BIP_Broadcast_Address.address, 4); - memcpy(&dest->mac[4], &BIP_Address.port, 2); + memcpy(&dest->mac[4], &port, 2); dest->net = BACNET_BROADCAST_NETWORK; dest->len = 0; /* no SLEN */ for (i = 0; i < MAX_MAC_LEN; i++) { diff --git a/ports/pico/bip-init.c b/ports/pico/bip-init.c index 0897b567..ae340df5 100644 --- a/ports/pico/bip-init.c +++ b/ports/pico/bip-init.c @@ -112,5 +112,7 @@ void bip_cleanup(void) bip_socket_cleanup(); } + bip_set_broadcast_port(0); + return; } diff --git a/ports/pico/bip.c b/ports/pico/bip.c index 5d5d8ac5..c65e2da7 100644 --- a/ports/pico/bip.c +++ b/ports/pico/bip.c @@ -26,6 +26,8 @@ static uint8_t BIP_Socket = MAX_SOCK_NUM; /* port to use - stored in network byte order */ static uint16_t BIP_Port = 0; +/* broadcast destination port to use */ +static uint16_t BIP_Broadcast_Port = 0; /* IP Address - stored in network byte order */ static uint8_t BIP_Address[4] = { 0, 0, 0, 0 }; /* Broadcast Address - stored in network byte order */ @@ -106,12 +108,26 @@ void bip_set_port(uint16_t port) BIP_Port = port; } +void bip_set_broadcast_port(uint16_t port) +{ + BIP_Broadcast_Port = port; +} + /* returns network byte order */ uint16_t bip_get_port(void) { return BIP_Port; } +uint16_t bip_get_broadcast_port(void) +{ + if (BIP_Broadcast_Port) { + return BIP_Broadcast_Port; + } + + return BIP_Port; +} + static int bip_decode_bip_address( const BACNET_ADDRESS *bac_addr, uint8_t *address, uint16_t *port) { @@ -161,7 +177,7 @@ int bip_send_pdu( for (i = 0; i < 4; i++) { address[i] = BIP_Broadcast_Address[i]; } - port = BIP_Port; + port = bip_get_broadcast_port(); mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; #ifdef DEBUG fprintf( @@ -349,7 +365,11 @@ void bip_get_broadcast_address(BACNET_ADDRESS *dest) if (dest) { dest->mac_len = 6; memcpy(&dest->mac[0], &BIP_Broadcast_Address, 4); - memcpy(&dest->mac[4], &BIP_Port, 2); + { + uint16_t port = bip_get_broadcast_port(); + + memcpy(&dest->mac[4], &port, 2); + } dest->net = BACNET_BROADCAST_NETWORK; dest->len = 0; /* no SLEN */ for (i = 0; i < MAX_MAC_LEN; i++) { diff --git a/ports/pico/bip.h b/ports/pico/bip.h index c5140776..59bfcee2 100644 --- a/ports/pico/bip.h +++ b/ports/pico/bip.h @@ -108,7 +108,9 @@ uint16_t bip_receive( /* use host byte order for setting */ void bip_set_port(uint16_t port); +void bip_set_broadcast_port(uint16_t port); uint16_t bip_get_port(void); +uint16_t bip_get_broadcast_port(void); /* use network byte order for setting */ void bip_set_addr(const uint8_t *net_address); diff --git a/ports/uip/bip.c b/ports/uip/bip.c index fe68812d..fdde213b 100644 --- a/ports/uip/bip.c +++ b/ports/uip/bip.c @@ -15,6 +15,8 @@ static int BIP_Socket = -1; /* port to use - stored in host byte order */ static uint16_t BIP_Port = 0xBAC0U; +/* broadcast destination port to use */ +static uint16_t BIP_Broadcast_Port; /* IP Address - stored in host byte order */ static struct in_addr BIP_Address; /* Broadcast Address - stored in host byte order */ @@ -40,6 +42,7 @@ void bip_cleanup(void) /* if (bip_valid()) */ /* close(BIP_Socket); */ BIP_Socket = -1; + BIP_Broadcast_Port = 0; return; } @@ -76,12 +79,28 @@ void bip_set_port(uint16_t port) BIP_Port = port; } +/* set using host byte order */ +void bip_set_broadcast_port(uint16_t port) +{ + BIP_Broadcast_Port = port; +} + /* returns host byte order */ uint16_t bip_get_port(void) { return BIP_Port; } +/* returns host byte order */ +uint16_t bip_get_broadcast_port(void) +{ + if (BIP_Broadcast_Port) { + return BIP_Broadcast_Port; + } + + return BIP_Port; +} + /* function to send a packet out the BACnet/IP socket (Annex J) */ /* returns number of bytes sent on success, negative number on failure */ int bip_send_pdu(BACNET_ADDRESS *dest, /* destination address */ @@ -116,7 +135,7 @@ int bip_send_pdu(BACNET_ADDRESS *dest, /* destination address */ /* broadcast */ else if (dest->mac_len == 0) { bip_dest.sin_addr.s_addr = BIP_Broadcast_Address.s_addr; - bip_dest.sin_port = htons(BIP_Port); + bip_dest.sin_port = htons(bip_get_broadcast_port()); memset(&(bip_dest.sin_zero), '\0', 8); mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU; } else @@ -197,7 +216,8 @@ void bip_get_broadcast_address(BACNET_ADDRESS *dest) dest->mac_len = 6; (void)encode_unsigned32( &dest->mac[0], htonl(BIP_Broadcast_Address.s_addr)); - (void)encode_unsigned16(&dest->mac[4], htons(BIP_Port)); + (void)encode_unsigned16( + &dest->mac[4], htons(bip_get_broadcast_port())); dest->net = BACNET_BROADCAST_NETWORK; dest->len = 0; /* no SLEN */ for (i = 0; i < MAX_MAC_LEN; i++) { diff --git a/ports/win32/bip-init.c b/ports/win32/bip-init.c index 7329cabb..1922fe46 100644 --- a/ports/win32/bip-init.c +++ b/ports/win32/bip-init.c @@ -29,6 +29,9 @@ static bool BIP_Initialized; /* port to use - stored here in network byte order */ /* Initialize to 0 - this will force initialization in demo apps */ static uint16_t BIP_Port; +/* broadcast destination port to use */ +/* Initialize to 0 - this will force initialization in demo apps */ +static uint16_t BIP_Broadcast_Port; /* IP address - stored here in network byte order */ static struct in_addr BIP_Address; /* IP broadcast address - stored here in network byte order */ @@ -259,6 +262,15 @@ void bip_set_port(uint16_t port) BIP_Port = htons(port); } +/** + * @brief Set the BACnet IPv4 UDP broadcast destination port number + * @param port - IPv4 UDP port number - in host byte order + */ +void bip_set_broadcast_port(uint16_t port) +{ + BIP_Broadcast_Port = htons(port); +} + /** * @brief Get the BACnet IPv4 UDP port number * @return IPv4 UDP port number - in host byte order @@ -268,6 +280,19 @@ uint16_t bip_get_port(void) return ntohs(BIP_Port); } +/** + * @brief Get the BACnet IPv4 UDP broadcast destination port number + * @return IPv4 UDP port number - in host byte order + */ +uint16_t bip_get_broadcast_port(void) +{ + if (BIP_Broadcast_Port) { + return ntohs(BIP_Broadcast_Port); + } + + return ntohs(BIP_Port); +} + /** * @brief Get the IPv4 address for my interface. Used for sending src address. * @param addr - BACnet datalink address @@ -299,11 +324,12 @@ void bip_get_my_address(BACNET_ADDRESS *addr) void bip_get_broadcast_address(BACNET_ADDRESS *dest) { int i = 0; /* counter */ + uint16_t port = htons(bip_get_broadcast_port()); if (dest) { dest->mac_len = 6; memcpy(&dest->mac[0], &BIP_Broadcast_Addr.s_addr, 4); - memcpy(&dest->mac[4], &BIP_Port, 2); + memcpy(&dest->mac[4], &port, 2); dest->net = BACNET_BROADCAST_NETWORK; dest->len = 0; /* no SLEN */ for (i = 0; i < MAX_MAC_LEN; i++) { @@ -363,7 +389,7 @@ bool bip_get_broadcast_addr(BACNET_IP_ADDRESS *addr) { if (addr) { memcpy(&addr->address[0], &BIP_Broadcast_Addr.s_addr, 4); - addr->port = ntohs(BIP_Port); + addr->port = bip_get_broadcast_port(); } return true; @@ -940,6 +966,7 @@ void bip_cleanup(void) /* these were set non-zero during interface configuration */ BIP_Address.s_addr = 0; BIP_Broadcast_Addr.s_addr = 0; + BIP_Broadcast_Port = 0; return; } diff --git a/src/bacnet/datalink/bip.h b/src/bacnet/datalink/bip.h index 0c454cde..94bbb4fe 100644 --- a/src/bacnet/datalink/bip.h +++ b/src/bacnet/datalink/bip.h @@ -71,6 +71,9 @@ uint16_t bip_receive( BACNET_STACK_EXPORT void bip_set_port(uint16_t port); +BACNET_STACK_EXPORT +void bip_set_broadcast_port(uint16_t port); + BACNET_STACK_EXPORT bool bip_port_changed(void); @@ -78,6 +81,9 @@ bool bip_port_changed(void); BACNET_STACK_EXPORT uint16_t bip_get_port(void); +BACNET_STACK_EXPORT +uint16_t bip_get_broadcast_port(void); + BACNET_STACK_EXPORT bool bip_set_addr(const BACNET_IP_ADDRESS *addr); diff --git a/src/bacnet/datalink/dlenv.c b/src/bacnet/datalink/dlenv.c index d1b95bda..2c916290 100644 --- a/src/bacnet/datalink/dlenv.c +++ b/src/bacnet/datalink/dlenv.c @@ -404,6 +404,10 @@ static void dlenv_network_port_bip_init(uint32_t instance) bip_set_port(0xBAC0); } } + pEnv = getenv("BACNET_IP_BROADCAST_PORT"); + if (pEnv) { + bip_set_broadcast_port((uint16_t)strtol(pEnv, NULL, 0)); + } pEnv = getenv("BACNET_IP_BROADCAST_BIND_ADDR"); if (pEnv) { bip_set_broadcast_binding(pEnv); @@ -1168,6 +1172,8 @@ bool dlenv_register_device(uint8_t port_type, bool wait_until_connected) * - BACDL_BIP: (BACnet/IP) * - BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP * communications. Default is 47808 (0xBAC0). + * - BACNET_IP_BROADCAST_PORT - UDP/IP destination port number (0..65534) + * used for BACnet/IP broadcasts. Default is BACNET_IP_PORT. * - BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign * Device Registration. Defaults to 47808 (0xBAC0). * - BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ae944b79..7f889d55 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -243,6 +243,7 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") list(APPEND testdirs ports/linux/bsc_event ports/linux/bip_subnet + ports/bip_broadcast_port ) elseif(WIN32) @@ -250,12 +251,14 @@ elseif(WIN32) list(APPEND testdirs ports/win32/bsc_event + ports/bip_broadcast_port ) elseif(APPLE) message(STATUS "Added ports specific tests for APPLE") list(APPEND testdirs ports/bsd/bsc_event + ports/bip_broadcast_port ) endif() diff --git a/test/bacnet/basic/bbmd/src/main.c b/test/bacnet/basic/bbmd/src/main.c index 27eada6b..fe3ec7a2 100644 --- a/test/bacnet/basic/bbmd/src/main.c +++ b/test/bacnet/basic/bbmd/src/main.c @@ -186,6 +186,34 @@ static void test_Initiate_Original_Broadcast_NPDU(void) test_cleanup(); } +static void test_Initiate_Original_Broadcast_NPDU_Uses_Broadcast_Port(void) +{ + uint8_t pdu[MAX_APDU] = { 0 }; + int npdu_len = 0; + int apdu_len = 0; + int pdu_len = 0; + BACNET_ADDRESS dest = { 0 }; + BACNET_NPDU_DATA npdu_data = { 0 }; + + test_setup(); + IUT.BIP_Addr.port = 0xBAC1U; + IUT.BIP_Broadcast_Addr.port = 0xBAC2U; + + dest.net = BACNET_BROADCAST_NETWORK; + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + npdu_len = npdu_encode_pdu(&pdu[0], &dest, &IUT.BACnet_Address, &npdu_data); + apdu_len = iam_encode_apdu( + &pdu[npdu_len], IUT.Device_ID, MAX_APDU, SEGMENTATION_NONE, + BACNET_VENDOR_ID); + pdu_len = npdu_len + apdu_len; + bvlc_send_pdu(&dest, &npdu_data, pdu, pdu_len); + + assert(Test_Sent_Message_Type == BVLC_ORIGINAL_BROADCAST_NPDU); + assert(Test_Sent_Message_Dest.port == IUT.BIP_Broadcast_Addr.port); + assert(Test_Sent_Message_Dest.port != IUT.BIP_Addr.port); + test_cleanup(); +} + static void test_BBMD_Result(void) { int result = 0; @@ -232,6 +260,7 @@ int main(void) /* individual tests */ test_BBMD_Result(); test_Initiate_Original_Broadcast_NPDU(); + test_Initiate_Original_Broadcast_NPDU_Uses_Broadcast_Port(); return 0; } diff --git a/test/bacnet/datalink/mock/src/bip-mock.c b/test/bacnet/datalink/mock/src/bip-mock.c index df5f9914..de0f5e27 100644 --- a/test/bacnet/datalink/mock/src/bip-mock.c +++ b/test/bacnet/datalink/mock/src/bip-mock.c @@ -80,6 +80,11 @@ void bip_set_port(uint16_t port) ztest_check_expected_value(port); } +void bip_set_broadcast_port(uint16_t port) +{ + ztest_check_expected_value(port); +} + bool bip_port_changed(void) { return ztest_get_return_value(); @@ -90,6 +95,11 @@ uint16_t bip_get_port(void) return ztest_get_return_value(); } +uint16_t bip_get_broadcast_port(void) +{ + return ztest_get_return_value(); +} + bool bip_set_addr(const BACNET_IP_ADDRESS *addr) { ztest_check_expected_data(addr, sizeof(BACNET_IP_ADDRESS)); diff --git a/test/ports/bip_broadcast_port/CMakeLists.txt b/test/ports/bip_broadcast_port/CMakeLists.txt new file mode 100644 index 00000000..9b8f682b --- /dev/null +++ b/test/ports/bip_broadcast_port/CMakeLists.txt @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) + +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + +string(REGEX REPLACE + "/test/ports/[a-zA-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/ports/[a-zA-Z_/-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/ports/[a-zA-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BACNET_BIG_ENDIAN=0 + CONFIG_ZTEST=1 + BACNET_STACK_DEPRECATED_DISABLE=1 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + set(PORT_BIP_SOURCE ${PORTS_DIR}/linux/bip-init.c) + find_package(Threads REQUIRED) +elseif(WIN32) + set(PORT_BIP_SOURCE ${PORTS_DIR}/win32/bip-init.c) + add_compile_definitions(BACNET_PORT=win32) +elseif(APPLE) + set(PORT_BIP_SOURCE ${PORTS_DIR}/bsd/bip-init.c) +else() + message(FATAL_ERROR "bip_broadcast_port test is only supported on Linux, macOS, and Windows") +endif() + +add_executable(${PROJECT_NAME} + ${PORT_BIP_SOURCE} + ${SRC_DIR}/bacnet/basic/sys/debug.c + ./src/bvlc_stubs.c + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + target_link_libraries(${PROJECT_NAME} Threads::Threads) +elseif(WIN32) + target_link_libraries(${PROJECT_NAME} ws2_32) +endif() diff --git a/test/ports/bip_broadcast_port/src/bvlc_stubs.c b/test/ports/bip_broadcast_port/src/bvlc_stubs.c new file mode 100644 index 00000000..005e3dbf --- /dev/null +++ b/test/ports/bip_broadcast_port/src/bvlc_stubs.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: MIT */ +#include "bacnet/basic/bbmd/h_bbmd.h" + +int bvlc_handler( + BACNET_IP_ADDRESS *addr, + BACNET_ADDRESS *src, + uint8_t *npdu, + uint16_t npdu_len) +{ + (void)addr; + (void)src; + (void)npdu; + (void)npdu_len; + + return 0; +} + +int bvlc_broadcast_handler( + BACNET_IP_ADDRESS *addr, + BACNET_ADDRESS *src, + uint8_t *npdu, + uint16_t npdu_len) +{ + (void)addr; + (void)src; + (void)npdu; + (void)npdu_len; + + return 0; +} + +int bvlc_send_pdu( + const BACNET_ADDRESS *dest, + const BACNET_NPDU_DATA *npdu_data, + const uint8_t *pdu, + unsigned pdu_len) +{ + (void)dest; + (void)npdu_data; + (void)pdu; + (void)pdu_len; + + return 0; +} + +void bvlc_init(void) +{ +} diff --git a/test/ports/bip_broadcast_port/src/main.c b/test/ports/bip_broadcast_port/src/main.c new file mode 100644 index 00000000..c9b9d1b8 --- /dev/null +++ b/test/ports/bip_broadcast_port/src/main.c @@ -0,0 +1,62 @@ +/** + * @file + * @brief Tests for separating BACnet/IP bind and broadcast destination ports + * @copyright SPDX-License-Identifier: MIT + */ +#include +#include + +static uint16_t bip_address_port(const BACNET_ADDRESS *address) +{ + return ((uint16_t)address->mac[4] << 8) | address->mac[5]; +} + +static void test_broadcast_port_defaults_to_bind_port(void) +{ + BACNET_ADDRESS broadcast = { 0 }; + BACNET_IP_ADDRESS broadcast_addr = { 0 }; + + bip_cleanup(); + bip_set_port(0xBAC1U); + + zassert_equal(bip_get_port(), 0xBAC1U, NULL); + zassert_equal(bip_get_broadcast_port(), 0xBAC1U, NULL); + + bip_get_broadcast_address(&broadcast); + zassert_equal(bip_address_port(&broadcast), 0xBAC1U, NULL); + + zassert_true(bip_get_broadcast_addr(&broadcast_addr), NULL); + zassert_equal(broadcast_addr.port, 0xBAC1U, NULL); +} + +static void test_broadcast_port_can_differ_from_bind_port(void) +{ + BACNET_ADDRESS local = { 0 }; + BACNET_ADDRESS broadcast = { 0 }; + BACNET_IP_ADDRESS broadcast_addr = { 0 }; + + bip_cleanup(); + bip_set_port(0xBAC1U); + bip_set_broadcast_port(0xBAC2U); + + zassert_equal(bip_get_port(), 0xBAC1U, NULL); + zassert_equal(bip_get_broadcast_port(), 0xBAC2U, NULL); + + bip_get_my_address(&local); + zassert_equal(bip_address_port(&local), 0xBAC1U, NULL); + + bip_get_broadcast_address(&broadcast); + zassert_equal(bip_address_port(&broadcast), 0xBAC2U, NULL); + + zassert_true(bip_get_broadcast_addr(&broadcast_addr), NULL); + zassert_equal(broadcast_addr.port, 0xBAC2U, NULL); +} + +void test_main(void) +{ + ztest_test_suite( + bip_broadcast_port_test, + ztest_unit_test(test_broadcast_port_defaults_to_bind_port), + ztest_unit_test(test_broadcast_port_can_differ_from_bind_port)); + ztest_run_test_suite(bip_broadcast_port_test); +}