From 144017f86140d7ebb65f9c67059301971900d782 Mon Sep 17 00:00:00 2001 From: Mikhail Antropov <103285038+Michail-Antropov@users.noreply.github.com> Date: Fri, 1 Jul 2022 17:48:41 +0300 Subject: [PATCH] BIP: add option - listen to broadcast on a separate socket (#293) * BIP: add socket to listen to broadcast packets for Linux/Windows/BSD/Zephyr * BIP: added BVLC broadcast handler to drop a packet when confirmed broadcast request is sent. * https://sourceforge.net/p/bacnet/bugs/65/ --- doc/README.msvc | 1 - ports/bsd/bip-init.c | 109 ++++++++++++++++-------- ports/linux/bacport.h | 1 - ports/linux/bip-init.c | 110 +++++++++++++++--------- ports/win32/bacport.h | 3 +- ports/win32/bip-init.c | 151 ++++++++++++++++++--------------- ports/zephyr/bip-init.c | 102 +++++++++++++--------- src/bacnet/basic/bbmd/h_bbmd.c | 26 ++++++ src/bacnet/basic/bbmd/h_bbmd.h | 6 ++ 9 files changed, 324 insertions(+), 185 deletions(-) diff --git a/doc/README.msvc b/doc/README.msvc index 15494eea..f7e654c4 100644 --- a/doc/README.msvc +++ b/doc/README.msvc @@ -13,7 +13,6 @@ BACDL_BIP=1 - chooses BACnet/IP for the datalink layer BACDL_ETHERNET=0 - chooses BACnet Ethernet for the datalink layer BACDL_ARCNET=0 - chooses BACnet ARCNET for the datalink layer BACDL_MSTP=0 - chooses BACnet MS/TP for the datalink layer -USE_INADDR=1 - uses INADDR_BROADCAST for broadcast rather than CLASSx TSM_ENABLED=1 - enables the Transaction State Machine for clients BIP_DEBUG=1 - enables print statements for debugging In Visual C++, add a Preprocessor Definition by: diff --git a/ports/bsd/bip-init.c b/ports/bsd/bip-init.c index 85782770..82a88a6f 100644 --- a/ports/bsd/bip-init.c +++ b/ports/bsd/bip-init.c @@ -47,8 +47,9 @@ * @brief Initializes BACnet/IP interface (BSD/MAC OS X). */ -/* unix socket */ +/* unix sockets */ static int BIP_Socket = -1; +static int BIP_Broadcast_Socket = -1; /* NOTE: we store address and port in network byte order since BACnet/IP uses network byte order for all address byte arrays @@ -315,6 +316,7 @@ uint16_t bip_receive( int received_bytes = 0; int offset = 0; uint16_t i = 0; + int socket; /* Make sure the socket is open */ if (BIP_Socket < 0) { @@ -333,10 +335,15 @@ uint16_t bip_receive( } FD_ZERO(&read_fds); FD_SET(BIP_Socket, &read_fds); - max = BIP_Socket; + FD_SET(BIP_Broadcast_Socket, &read_fds); + + max = BIP_Socket > BIP_Broadcast_Socket ? BIP_Socket : BIP_Broadcast_Socket; + /* see if there is a packet for us */ if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { - received_bytes = recvfrom(max, (char *)&npdu[0], max_npdu, 0, + socket = FD_ISSET(BIP_Socket, &read_fds) ? BIP_Socket : + BIP_Broadcast_Socket; + received_bytes = recvfrom(socket, (char *)&npdu[0], max_npdu, 0, (struct sockaddr *)&sin, &sin_len); } else { return 0; @@ -374,7 +381,9 @@ uint16_t bip_receive( debug_print_ipv4( "Received MPDU->", &sin.sin_addr, sin.sin_port, received_bytes); /* pass the packet into the BBMD handler */ - offset = bvlc_handler(&addr, src, npdu, received_bytes); + offset = socket == BIP_Socket ? + bvlc_handler(&addr, src, npdu, received_bytes) : + bvlc_broadcast_handler(&addr, src, npdu, received_bytes); if (offset > 0) { npdu_len = received_bytes - offset; debug_print_ipv4( @@ -556,6 +565,45 @@ void bip_set_interface(char *ifname) } } +static int createSocket(struct sockaddr_in *sin) +{ + int status = 0; /* return from socket lib calls */ + int sockopt = 0; + int sock_fd = -1; + + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock_fd < 0) { + return sock_fd; + } + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + sockopt = 1; + status = setsockopt( + sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + return status; + } + /* allow us to send a broadcast */ + status = setsockopt( + sock_fd, SOL_SOCKET, SO_BROADCAST, &sockopt, sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + return status; + } + + /* bind the socket to the local port number and IP address */ + status = + bind(sock_fd, (const struct sockaddr *)sin, sizeof(struct sockaddr)); + if (status < 0) { + close(sock_fd); + return status; + } + + return sock_fd; +} + /** Initialize the BACnet/IP services at the given interface. * @ingroup DLBIP * -# Gets the local IP address and local broadcast address from the system, @@ -575,9 +623,7 @@ void bip_set_interface(char *ifname) */ bool bip_init(char *ifname) { - int status = 0; /* return from socket lib calls */ struct sockaddr_in sin; - int sockopt = 0; int sock_fd = -1; if (ifname) { @@ -586,42 +632,28 @@ bool bip_init(char *ifname) } else { bip_set_interface("en0"); } - /* assumes that the driver has already been initialized */ - sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - BIP_Socket = sock_fd; - if (sock_fd < 0) - return false; - /* Allow us to use the same socket for sending and receiving */ - /* This makes sure that the src port is correct when sending */ - sockopt = 1; - status = setsockopt( - sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); - if (status < 0) { - close(sock_fd); - BIP_Socket = -1; - return status; - } - /* allow us to send a broadcast */ - status = setsockopt( - sock_fd, SOL_SOCKET, SO_BROADCAST, &sockopt, sizeof(sockopt)); - if (status < 0) { - close(sock_fd); - BIP_Socket = -1; - return false; - } - /* bind the socket to the local port number and IP address */ + sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = BIP_Port; memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); - status = - bind(sock_fd, (const struct sockaddr *)&sin, sizeof(struct sockaddr)); - if (status < 0) { - close(sock_fd); - BIP_Socket = -1; + + + sin.sin_addr.s_addr = BIP_Address.s_addr; + sock_fd = createSocket(&sin); + BIP_Socket = sock_fd; + if (sock_fd < 0) { return false; } + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sock_fd = createSocket(&sin); + BIP_Broadcast_Socket = sock_fd; + if (sock_fd < 0) { + return false; + } + + bvlc_init(); + return true; } @@ -646,5 +678,10 @@ void bip_cleanup(void) } BIP_Socket = -1; + if (BIP_Broadcast_Socket != -1) { + close(BIP_Broadcast_Socket); + } + BIP_Broadcast_Socket = -1; + return; } diff --git a/ports/linux/bacport.h b/ports/linux/bacport.h index 98812bbb..4fae2358 100644 --- a/ports/linux/bacport.h +++ b/ports/linux/bacport.h @@ -111,5 +111,4 @@ extern int bip_get_local_address_ioctl( char *ifname, struct in_addr *addr, int request); - #endif diff --git a/ports/linux/bip-init.c b/ports/linux/bip-init.c index 3603569d..38afb863 100644 --- a/ports/linux/bip-init.c +++ b/ports/linux/bip-init.c @@ -59,8 +59,9 @@ /** @file linux/bip-init.c Initializes BACnet/IP interface (Linux). */ -/* unix socket */ +/* unix sockets */ static int BIP_Socket = -1; +static int BIP_Broadcast_Socket = -1; /* NOTE: we store address and port in network byte order since BACnet/IP uses network byte order for all address byte arrays @@ -331,6 +332,7 @@ uint16_t bip_receive( int received_bytes = 0; int offset = 0; uint16_t i = 0; + int socket; /* Make sure the socket is open */ if (BIP_Socket < 0) { @@ -349,10 +351,15 @@ uint16_t bip_receive( } FD_ZERO(&read_fds); FD_SET(BIP_Socket, &read_fds); - max = BIP_Socket; + FD_SET(BIP_Broadcast_Socket, &read_fds); + + max = BIP_Socket > BIP_Broadcast_Socket ? BIP_Socket : BIP_Broadcast_Socket; + /* see if there is a packet for us */ if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { - received_bytes = recvfrom(max, (char *)&npdu[0], max_npdu, 0, + socket = FD_ISSET(BIP_Socket, &read_fds) ? BIP_Socket : + BIP_Broadcast_Socket; + received_bytes = recvfrom(socket, (char *)&npdu[0], max_npdu, 0, (struct sockaddr *)&sin, &sin_len); } else { return 0; @@ -390,7 +397,9 @@ uint16_t bip_receive( debug_print_ipv4( "Received MPDU->", &sin.sin_addr, sin.sin_port, received_bytes); /* pass the packet into the BBMD handler */ - offset = bvlc_handler(&addr, src, npdu, received_bytes); + offset = socket == BIP_Socket ? + bvlc_handler(&addr, src, npdu, received_bytes) : + bvlc_broadcast_handler(&addr, src, npdu, received_bytes); if (offset > 0) { npdu_len = received_bytes - offset; debug_print_ipv4( @@ -736,6 +745,7 @@ void bip_set_interface(char *ifname) { struct in_addr local_address; struct in_addr netmask; + short flags; int rv = 0; /* setup local address */ @@ -766,6 +776,48 @@ void bip_set_interface(char *ifname) } } +static int createSocket(struct sockaddr_in *sin) +{ + int status = 0; /* return from socket lib calls */ + int sockopt = 0; + int sock_fd = -1; + + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock_fd < 0) { + return sock_fd; + } + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + sockopt = 1; + status = setsockopt( + sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + return status; + } + /* allow us to send a broadcast */ + status = setsockopt( + sock_fd, SOL_SOCKET, SO_BROADCAST, &sockopt, sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + return status; + } + /* Bind to the proper interface to send without default gateway */ + setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, BIP_Interface_Name, + strlen(BIP_Interface_Name)); + + /* bind the socket to the local port number and IP address */ + status = + bind(sock_fd, (const struct sockaddr *)sin, sizeof(struct sockaddr)); + if (status < 0) { + close(sock_fd); + return status; + } + + return sock_fd; +} + /** Initialize the BACnet/IP services at the given interface. * @ingroup DLBIP * -# Gets the local IP address and local broadcast address from the system, @@ -785,9 +837,7 @@ void bip_set_interface(char *ifname) */ bool bip_init(char *ifname) { - int status = 0; /* return from socket lib calls */ struct sockaddr_in sin; - int sockopt = 0; int sock_fd = -1; if (ifname) { @@ -802,46 +852,25 @@ bool bip_init(char *ifname) fflush(stderr); return false; } - /* assumes that the driver has already been initialized */ - sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + sin.sin_family = AF_INET; + sin.sin_port = BIP_Port; + memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); + + sin.sin_addr.s_addr = BIP_Address.s_addr; + sock_fd = createSocket(&sin); BIP_Socket = sock_fd; if (sock_fd < 0) { return false; } - /* Allow us to use the same socket for sending and receiving */ - /* This makes sure that the src port is correct when sending */ - sockopt = 1; - status = setsockopt( - sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); - if (status < 0) { - close(sock_fd); - BIP_Socket = -1; - return status; - } - /* allow us to send a broadcast */ - status = setsockopt( - sock_fd, SOL_SOCKET, SO_BROADCAST, &sockopt, sizeof(sockopt)); - if (status < 0) { - close(sock_fd); - BIP_Socket = -1; - return false; - } - /* Bind to the proper interface to send without default gateway */ - setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, BIP_Interface_Name, - sizeof(BIP_Interface_Name)); - /* bind the socket to the local port number and IP address */ - sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_port = BIP_Port; - memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); - status = - bind(sock_fd, (const struct sockaddr *)&sin, sizeof(struct sockaddr)); - if (status < 0) { - close(sock_fd); - BIP_Socket = -1; + sock_fd = createSocket(&sin); + BIP_Broadcast_Socket = sock_fd; + if (sock_fd < 0) { return false; } + bvlc_init(); return true; @@ -866,5 +895,10 @@ void bip_cleanup(void) } BIP_Socket = -1; + if (BIP_Broadcast_Socket != -1) { + close(BIP_Broadcast_Socket); + } + BIP_Broadcast_Socket = -1; + return; } diff --git a/ports/win32/bacport.h b/ports/win32/bacport.h index 8557bf66..6cbb9a8a 100644 --- a/ports/win32/bacport.h +++ b/ports/win32/bacport.h @@ -39,8 +39,7 @@ #endif #include -#if (!defined(USE_INADDR) || (USE_INADDR == 0)) && \ - (!defined(USE_CLASSADDR) || (USE_CLASSADDR == 0)) +#if (!defined(USE_CLASSADDR) || (USE_CLASSADDR == 0)) #include #if defined(_MSC_VER) #pragma comment(lib, "IPHLPAPI.lib") diff --git a/ports/win32/bip-init.c b/ports/win32/bip-init.c index c60dd005..a8328a8b 100644 --- a/ports/win32/bip-init.c +++ b/ports/win32/bip-init.c @@ -45,16 +45,13 @@ #include "bacnet/basic/bbmd/h_bbmd.h" #include "bacport.h" -/* alternate methods of choosing broadcast address */ -#ifndef USE_INADDR -#define USE_INADDR 0 -#endif #ifndef USE_CLASSADDR #define USE_CLASSADDR 0 #endif -/* Windows socket */ +/* Windows sockets */ static SOCKET BIP_Socket = INVALID_SOCKET; +static SOCKET BIP_Broadcast_Socket = INVALID_SOCKET; /* NOTE: we store address and port in network byte order since BACnet/IP uses network byte order for all address byte arrays @@ -482,6 +479,7 @@ uint16_t bip_receive( int received_bytes = 0; int offset = 0; uint16_t i = 0; + SOCKET socket; /* Make sure the socket is open */ if (BIP_Socket == INVALID_SOCKET) { @@ -500,10 +498,15 @@ uint16_t bip_receive( } FD_ZERO(&read_fds); FD_SET(BIP_Socket, &read_fds); - max = BIP_Socket; + FD_SET(BIP_Broadcast_Socket, &read_fds); + + max = BIP_Socket > BIP_Broadcast_Socket ? BIP_Socket : BIP_Broadcast_Socket; + /* see if there is a packet for us */ if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { - received_bytes = recvfrom(max, (char *)&npdu[0], max_npdu, 0, + socket = FD_ISSET(BIP_Socket, &read_fds) ? BIP_Socket : + BIP_Broadcast_Socket; + received_bytes = recvfrom(socket, (char *)&npdu[0], max_npdu, 0, (struct sockaddr *)&sin, &sin_len); } else { return 0; @@ -541,7 +544,9 @@ uint16_t bip_receive( debug_print_ipv4( "Received MPDU->", &sin.sin_addr, sin.sin_port, received_bytes); /* pass the packet into the BBMD handler */ - offset = bvlc_handler(&addr, src, npdu, received_bytes); + offset = socket == BIP_Socket ? + bvlc_handler(&addr, src, npdu, received_bytes) : + bvlc_broadcast_handler(&addr, src, npdu, received_bytes); if (offset > 0) { npdu_len = received_bytes - offset; if (npdu_len <= max_npdu) { @@ -632,7 +637,7 @@ static long gethostaddr(void) return *(long *)host_ent->h_addr; } -#if ((USE_INADDR == 0) || (USE_CLASSADDR == 0)) +#if (USE_CLASSADDR == 0) /* returns the subnet mask in network byte order */ static uint32_t getIpMaskForIpAddress(uint32_t ipAddress) { @@ -679,12 +684,7 @@ static uint32_t getIpMaskForIpAddress(uint32_t ipAddress) static void set_broadcast_address(uint32_t net_address) { -#if USE_INADDR - /* Note: sometimes INADDR_BROADCAST does not let me get - any unicast messages. Not sure why... */ - (void)net_address; - BIP_Broadcast_Address.s_addr = INADDR_BROADCAST; -#elif USE_CLASSADDR +#if USE_CLASSADDR long broadcast_address = 0; if (IN_CLASSA(ntohl(net_address))) @@ -701,7 +701,7 @@ static void set_broadcast_address(uint32_t net_address) (ntohl(net_address) & ~IN_CLASSD_HOST) | IN_CLASSD_HOST; else broadcast_address = INADDR_BROADCAST; - BIP_Broadcast_Address.s_addr = htonl(broadcast_address)); + BIP_Broadcast_Address.s_addr = htonl(broadcast_address); #else /* these are network byte order variables */ long broadcast_address = 0; @@ -728,7 +728,6 @@ static void set_broadcast_address(uint32_t net_address) */ void bip_set_interface(char *ifname) { - bip_init_windows(); /* setup local address */ if (BIP_Address.s_addr == 0) { BIP_Address.s_addr = inet_addr(ifname); @@ -744,6 +743,45 @@ void bip_set_interface(char *ifname) } } +static int createSocket(struct sockaddr_in *sin) +{ + int rv = 0; /* return from socket lib calls */ + int value = 1; + SOCKET sock_fd = INVALID_SOCKET; + + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock_fd < 0) { + print_last_error("failed to allocate a socket"); + return sock_fd; + } + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + rv = setsockopt( + sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&value, sizeof(value)); + if (rv < 0) { + print_last_error("failed to set REUSEADDR socket option"); + closesocket(sock_fd); + return rv; + } + /* Enables transmission and receipt of broadcast messages on the socket. */ + rv = setsockopt( + sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&value, sizeof(value)); + if (rv < 0) { + print_last_error("failed to set BROADCAST socket option"); + closesocket(sock_fd); + return rv; + } + + rv = bind(sock_fd, (const struct sockaddr *)sin, sizeof(struct sockaddr)); + if (rv < 0) { + print_last_error("failed to bind"); + closesocket(sock_fd); + return rv; + } + return sock_fd; +} + /** Initialize the BACnet/IP services at the given interface. * @ingroup DLBIP * -# Gets the local IP address and local broadcast address from the system, @@ -763,9 +801,7 @@ void bip_set_interface(char *ifname) */ bool bip_init(char *ifname) { - int rv = 0; /* return from socket lib calls */ struct sockaddr_in sin = { -1 }; - int value = 1; SOCKET sock_fd = INVALID_SOCKET; bip_init_windows(); @@ -788,68 +824,38 @@ bool bip_init(char *ifname) ntohs(BIP_Port)); fflush(stderr); } - /* assumes that the driver has already been initialized */ - sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - BIP_Socket = sock_fd; - if (sock_fd < 0) { - print_last_error("failed to allocate a socket"); - return false; - } - /* Allow us to use the same socket for sending and receiving */ - /* This makes sure that the src port is correct when sending */ - rv = setsockopt( - sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&value, sizeof(value)); - if (rv < 0) { - print_last_error("failed to set REUSEADDR socket option"); - closesocket(sock_fd); - BIP_Socket = INVALID_SOCKET; - return false; - } - /* Enables transmission and receipt of broadcast messages on the socket. */ - rv = setsockopt( - sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&value, sizeof(value)); - if (rv < 0) { - print_last_error("failed to set BROADCAST socket option"); - closesocket(sock_fd); - BIP_Socket = INVALID_SOCKET; - return false; - } + /* bind the socket to the local port number and IP address */ sin.sin_family = AF_INET; -#if USE_INADDR - /* by setting sin.sin_addr.s_addr to INADDR_ANY, - I am telling the IP stack to automatically fill - in the IP address of the machine the process - is running on. - - Some server computers have multiple IP addresses. - A socket bound to one of these will not accept - connections to another address. Frequently you prefer - to allow any one of the computer's IP addresses - to be used for connections. Use INADDR_ANY (0L) to - allow clients to connect using any one of the host's - IP addresses. */ - sin.sin_addr.s_addr = htonl(INADDR_ANY); -#else - /* or we could use the specific adapter address - note: already in network byte order */ - sin.sin_addr.s_addr = BIP_Address.s_addr; -#endif sin.sin_port = BIP_Port; memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); + + sin.sin_addr.s_addr = BIP_Address.s_addr; if (BIP_Debug) { fprintf(stderr, "BIP: bind %s:%hu\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); fflush(stderr); } - rv = bind(sock_fd, (const struct sockaddr *)&sin, sizeof(struct sockaddr)); - if (rv < 0) { - print_last_error("failed to bind"); - closesocket(sock_fd); - BIP_Socket = INVALID_SOCKET; + sock_fd = createSocket(&sin); + BIP_Socket = sock_fd; + if (sock_fd < 0) { return false; } + sin.sin_addr.s_addr = htonl(INADDR_ANY); + if (BIP_Debug) { + fprintf(stderr, "BIP: broadcast bind %s:%hu\n", inet_ntoa(sin.sin_addr), + ntohs(sin.sin_port)); + fflush(stderr); + } + sock_fd = createSocket(&sin); + BIP_Broadcast_Socket = sock_fd; + if (sock_fd < 0) { + return false; + } + + bvlc_init(); + return true; } @@ -874,6 +880,13 @@ void bip_cleanup(void) closesocket(sock_fd); } BIP_Socket = INVALID_SOCKET; + + if (BIP_Broadcast_Socket != INVALID_SOCKET) { + sock_fd = BIP_Broadcast_Socket; + closesocket(sock_fd); + } + BIP_Broadcast_Socket = INVALID_SOCKET; + WSACleanup(); return; diff --git a/ports/zephyr/bip-init.c b/ports/zephyr/bip-init.c index 1c7e8aec..00a4d0ee 100644 --- a/ports/zephyr/bip-init.c +++ b/ports/zephyr/bip-init.c @@ -55,8 +55,9 @@ LOG_MODULE_DECLARE(bacnet, CONFIG_BACNETSTACK_LOG_LEVEL); #define THIS_FILE "bip-init.c" -/* zephyr socket */ +/* zephyr sockets */ static int BIP_Socket = -1; +static int BIP_Broadcast_Socket = -1; /* NOTE: we store address and port in network byte order since BACnet/IP uses network byte order for all address byte arrays @@ -320,6 +321,7 @@ uint16_t bip_receive( int received_bytes = 0; int offset = 0; uint16_t i = 0; + int socket; /* Make sure the socket is open */ if (BIP_Socket < 0) { @@ -339,11 +341,15 @@ uint16_t bip_receive( } ZSOCK_FD_ZERO(&read_fds); ZSOCK_FD_SET(BIP_Socket, &read_fds); - max = BIP_Socket; + FD_SET(BIP_Broadcast_Socket, &read_fds); + + max = BIP_Socket > BIP_Broadcast_Socket ? BIP_Socket : BIP_Broadcast_Socket; /* see if there is a packet for us */ if (zsock_select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { - received_bytes = zsock_recvfrom(BIP_Socket, (char *)&npdu[0], max_npdu, + socket = FD_ISSET(BIP_Socket, &read_fds) ? BIP_Socket : + BIP_Broadcast_Socket; + received_bytes = zsock_recvfrom(socket, (char *)&npdu[0], max_npdu, 0, (struct sockaddr *)&sin, &sin_len); } else @@ -378,7 +384,9 @@ uint16_t bip_receive( debug_print_ipv4("Received MPDU->", &sin.sin_addr, sin.sin_port, received_bytes); /* pass the packet into the BBMD handler */ - offset = bvlc_handler(&addr, src, npdu, received_bytes); + offset = socket == BIP_Socket ? + bvlc_handler(&addr, src, npdu, received_bytes) : + bvlc_broadcast_handler(&addr, src, npdu, received_bytes); if (offset > 0) { npdu_len = received_bytes - offset; debug_print_ipv4("Received NPDU->", &sin.sin_addr, sin.sin_port, @@ -504,6 +512,48 @@ void bip_set_interface(char *ifname) } } +static int createSocket(struct sockaddr_in *sin) +{ + int sock_fd = -1; + const int sockopt = 1; + int status = -1; + + /* assumes that the driver has already been initialized */ + sock_fd = zsock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock_fd < 0) { + LOG_ERR("%s:%d - Failed to create socket", THIS_FILE, __LINE__); + return sock_fd; + } + else + { + LOG_DBG("Socket created"); + } + + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + status = zsock_setsockopt( + sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); + if (status < 0) { + zsock_close(sock_fd); + return status; + } + + /* bind the socket to the local port number and IP address */ + status = + zsock_bind(sock_fd, (const struct sockaddr *)sin, sizeof(struct sockaddr)); + if (status < 0) { + zsock_close(sock_fd); + LOG_ERR("%s:%d - zsock_bind() failure", THIS_FILE, __LINE__); + return status; + } + else + { + LOG_DBG("Socket bound"); + } + + return sock_fd; +} + /** Initialize the BACnet/IP services at the given interface. * @ingroup DLBIP * -# Gets the local IP address and local broadcast address from the system, @@ -523,8 +573,6 @@ void bip_set_interface(char *ifname) */ bool bip_init(char *ifname) { - int sock_fd = -1; - const int sockopt = 1; int status = -1; struct sockaddr_in sin = { 0 }; @@ -535,44 +583,22 @@ bool bip_init(char *ifname) return false; } - /* assumes that the driver has already been initialized */ - sock_fd = zsock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - BIP_Socket = sock_fd; - if (sock_fd < 0) { - LOG_ERR("%s:%d - Failed to create socket", THIS_FILE, __LINE__); - return false; - } - else - { - LOG_DBG("Socket created"); - } - - /* Allow us to use the same socket for sending and receiving */ - /* This makes sure that the src port is correct when sending */ - status = zsock_setsockopt( - sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); - if (status < 0) { - zsock_close(sock_fd); - BIP_Socket = -1; - return false; - } - /* bind the socket to the local port number and IP address */ sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = BIP_Port; - status = - zsock_bind(sock_fd, (const struct sockaddr *)&sin, sizeof(struct sockaddr)); - if (status < 0) { - zsock_close(sock_fd); - BIP_Socket = -1; - LOG_ERR("%s:%d - zsock_bind() failure", THIS_FILE, __LINE__); + sin.sin_addr.s_addr = BIP_Address.s_addr; + sock_fd = createSocket(&sin); + BIP_Socket = sock_fd; + if (sock_fd < 0) { return false; } - else - { - LOG_DBG("Socket bound"); + + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sock_fd = createSocket(&sin); + BIP_Broadcast_Socket = sock_fd; + if (sock_fd < 0) { + return false; } bvlc_init(); diff --git a/src/bacnet/basic/bbmd/h_bbmd.c b/src/bacnet/basic/bbmd/h_bbmd.c index acebc158..a4b4c61b 100644 --- a/src/bacnet/basic/bbmd/h_bbmd.c +++ b/src/bacnet/basic/bbmd/h_bbmd.c @@ -1159,6 +1159,32 @@ int bvlc_handler(BACNET_IP_ADDRESS *addr, #endif } +int bvlc_broadcast_handler(BACNET_IP_ADDRESS *addr, + BACNET_ADDRESS *src, + uint8_t *npdu, + uint16_t npdu_len) +{ + int offset = 0; + uint8_t message_type = 0; + uint16_t message_length = 0; + int header_len = 0; + + header_len = + bvlc_decode_header(npdu, npdu_len, &message_type, &message_length); + if (header_len == 4) { + switch (message_type) { + case BVLC_ORIGINAL_UNICAST_NPDU: + /* drop unicast when sent as a broadcast */ + break; + default: + offset = bvlc_handler(addr, src, npdu, npdu_len); + break; + } + } + + return offset; +} + #if BBMD_CLIENT_ENABLED /** Register as a foreign device with the indicated BBMD. * @param bbmd_addr - IPv4 address of BBMD with which to register diff --git a/src/bacnet/basic/bbmd/h_bbmd.h b/src/bacnet/basic/bbmd/h_bbmd.h index b1662569..b0d20362 100644 --- a/src/bacnet/basic/bbmd/h_bbmd.h +++ b/src/bacnet/basic/bbmd/h_bbmd.h @@ -47,6 +47,12 @@ int bvlc_handler(BACNET_IP_ADDRESS *addr, uint8_t *npdu, uint16_t npdu_len); +BACNET_STACK_EXPORT +int bvlc_broadcast_handler(BACNET_IP_ADDRESS *addr, + BACNET_ADDRESS *src, + uint8_t *npdu, + uint16_t npdu_len); + BACNET_STACK_EXPORT int bvlc_bbmd_enabled_handler(BACNET_IP_ADDRESS *addr, BACNET_ADDRESS *src,