Feature/refactor bacnet ipv4 add unit tests (#64)

* refactor BACnet/IPv4 BVLC into encode-decode library with unit tests
Added Read-Broadcast-Distribution-Table encoding and unit test.
Added Read-Broadcast-Distribution-Table-Ack encoding and unit test.
Added Read-Foreign-Device-Table-Ack encoding and unit test.
Added some BDT/FDT diff copy functions with unit tests
Added some FDT add and delete entry functions with unit tests
Added some BDT set append and clear entry functions with unit tests
Added some BIPv4 address conversion functions with unit tests
Added non-BBMD handling unit test
Added basic unit test for BBMD handler
Added BBMD broadcast mask get set and unit tests
Added IPv6 maintenance timer.
Added ReadFDT app
Fixed ReadBDT app
Added Who-Is to Makefile for individual app build
Fixed debugging code blocks projects by swapping bip.c for h_bbmd.c module.
Ported BACnet/IPv4 to refactored BVLC for Linux, BSD, Windows
Fix datalink debug for DLENV module
Improve BIPv4 linux driver debug info
Added BDT mask functions
Reduce debug info clutter in Who-Is app by using environment option
Fix TTL seconds upper bounds addition
Fix CIDR prefix calculation on Linux BIPv4.
Convert BSD BIPv4 to BVLCv4
Fix CMake build for BIPv4 (Linux, BSD, Windows)
Added [U]nsigned to 0xBAC0 constants
Cleanup POSIX and Win32 API sockets
Remove unnecessary file scope variable initialization
Fix routed NPDU to depend on datalink; fix warning
Remove OS dependent network code from gateway
Enable BBMD client in library by default
Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
Co-authored-by: Steve Karg <steve.karg@legrand.us>
This commit is contained in:
Steve Karg
2020-04-04 11:31:54 -05:00
committed by GitHub
parent 0ce6368b43
commit eedfa58a55
51 changed files with 7178 additions and 2978 deletions
+594 -216
View File
@@ -38,167 +38,64 @@
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include "bacnet/bacdcode.h"
#include "bacnet/bacint.h"
#include "bacnet/config.h"
#include "bacnet/datalink/bip.h"
#include "bacnet/basic/sys/debug.h"
#include "bacnet/basic/bbmd/h_bbmd.h"
#include "bacport.h"
#if defined(_MSC_VER)
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "IPHLPAPI.lib")
/* alternate methods of choosing broadcast address */
#ifndef USE_INADDR
#define USE_INADDR 0
#endif
#ifndef USE_CLASSADDR
#define USE_CLASSADDR 0
#endif
bool BIP_Debug = false;
/* Windows socket */
static SOCKET BIP_Socket = INVALID_SOCKET;
/* gets an IP address by name, where name can be a
string that is an IP address in dotted form, or
a name that is a domain name
returns 0 if not found, or
an IP address in network byte order */
long bip_getaddrbyname(
const char *host_name)
/* NOTE: we store address and port in network byte order
since BACnet/IP uses network byte order for all address byte arrays
*/
/* port to use - stored here in network byte order */
/* Initialize to 0 - this will force initialization in demo apps */
static uint16_t BIP_Port;
/* IP address - stored here in network byte order */
static struct in_addr BIP_Address;
/* IP broadcast address - stored here in network byte order */
static struct in_addr BIP_Broadcast_Address;
/* enable debugging */
static bool BIP_Debug = false;
/**
* @brief Print the IPv4 address with debug info
* @param str - debug info string
* @param addr - IPv4 address
*/
static void debug_print_ipv4(const char *str, const struct in_addr *addr,
const unsigned int port, const unsigned int count)
{
struct hostent *host_ent;
if ((host_ent = gethostbyname(host_name)) == NULL)
return 0;
return *(long *) host_ent->h_addr;
}
/* To fill a need, we invent the gethostaddr() function. */
static long gethostaddr(
void)
{
struct hostent *host_ent;
char host_name[255];
if (gethostname(host_name, sizeof(host_name)) != 0)
return -1;
if ((host_ent = gethostbyname(host_name)) == NULL)
return -1;
if (BIP_Debug) {
printf("host: %s at %u.%u.%u.%u\n", host_name,
(unsigned) ((uint8_t *) host_ent->h_addr)[0],
(unsigned) ((uint8_t *) host_ent->h_addr)[1],
(unsigned) ((uint8_t *) host_ent->h_addr)[2],
(unsigned) ((uint8_t *) host_ent->h_addr)[3]);
fprintf(stderr, "BIP: %s %s:%hu (%u bytes)\n", str, inet_ntoa(*addr),
ntohs(port), count);
fflush(stderr);
}
/* note: network byte order */
return *(long *) host_ent->h_addr;
}
#if (!defined(USE_INADDR) || (USE_INADDR == 0)) && \
(!defined(USE_CLASSADDR) || (USE_CLASSADDR == 0))
/* returns the subnet mask in network byte order */
static uint32_t getIpMaskForIpAddress(
uint32_t ipAddress)
/**
* @brief Enabled debug printing of BACnet/IPv4
*/
void bip_debug_enable(void)
{
/* Allocate information for up to 16 NICs */
IP_ADAPTER_INFO AdapterInfo[16];
/* Save memory size of buffer */
DWORD dwBufLen = sizeof(AdapterInfo);
uint32_t ipMask = INADDR_BROADCAST;
bool found = false;
PIP_ADAPTER_INFO pAdapterInfo;
/* GetAdapterInfo:
[out] buffer to receive data
[in] size of receive data buffer */
DWORD dwStatus = GetAdaptersInfo(AdapterInfo,
&dwBufLen);
if (dwStatus == ERROR_SUCCESS) {
/* Verify return value is valid, no buffer overflow
Contains pointer to current adapter info */
pAdapterInfo = AdapterInfo;
do {
IP_ADDR_STRING *pIpAddressInfo = &pAdapterInfo->IpAddressList;
do {
unsigned long adapterAddress =
inet_addr(pIpAddressInfo->IpAddress.String);
unsigned long adapterMask =
inet_addr(pIpAddressInfo->IpMask.String);
if (adapterAddress == ipAddress) {
ipMask = adapterMask;
found = true;
}
pIpAddressInfo = pIpAddressInfo->Next;
} while (pIpAddressInfo && !found);
/* Progress through linked list */
pAdapterInfo = pAdapterInfo->Next;
/* Terminate on last adapter */
} while (pAdapterInfo && !found);
}
return ipMask;
}
#endif
static void set_broadcast_address(
uint32_t net_address)
{
#if defined(USE_INADDR) && USE_INADDR
/* Note: sometimes INADDR_BROADCAST does not let me get
any unicast messages. Not sure why... */
net_address = net_address;
bip_set_broadcast_addr(INADDR_BROADCAST);
#elif defined(USE_CLASSADDR) && USE_CLASSADDR
long broadcast_address = 0;
if (IN_CLASSA(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSA_HOST) | IN_CLASSA_HOST;
else if (IN_CLASSB(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSB_HOST) | IN_CLASSB_HOST;
else if (IN_CLASSC(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSC_HOST) | IN_CLASSC_HOST;
else if (IN_CLASSD(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSD_HOST) | IN_CLASSD_HOST;
else
broadcast_address = INADDR_BROADCAST;
bip_set_broadcast_addr(htonl(broadcast_address));
#else
/* these are network byte order variables */
long broadcast_address = 0;
long net_mask = 0;
net_mask = getIpMaskForIpAddress(net_address);
if (BIP_Debug) {
struct in_addr address;
address.s_addr = net_mask;
printf("IP Mask: %s\n", inet_ntoa(address));
}
broadcast_address = (net_address & net_mask) | (~net_mask);
bip_set_broadcast_addr(broadcast_address);
#endif
}
/* on Windows, ifname is the dotted ip address of the interface */
void bip_set_interface(
char *ifname)
{
struct in_addr address;
/* setup local address */
if (bip_get_addr() == 0) {
bip_set_addr(inet_addr(ifname));
}
if (BIP_Debug) {
address.s_addr = bip_get_addr();
fprintf(stderr, "Interface: %s\n", ifname);
}
/* setup local broadcast address */
if (bip_get_broadcast_addr() == 0) {
address.s_addr = bip_get_addr();
set_broadcast_address(address.s_addr);
}
BIP_Debug = true;
}
/**
* @brief Get the text string for Windows Error Codes
*/
static char *winsock_error_code_text(
int code)
{
@@ -316,6 +213,510 @@ static char *winsock_error_code_text(
}
}
/**
* @brief Print the text string for the last Windows Error Code
*/
static void print_last_error(const char *info)
{
int Code = WSAGetLastError();
fprintf(stderr, "BIP: %s [error code %i] %s\n",
info, Code, winsock_error_code_text(Code));
fflush(stderr);
}
/**
* @brief Initialize the Windows Socket Layer
*/
static void bip_init_windows(void)
{
static bool initialized = false;
int Result;
WSADATA wd;
if (!initialized) {
Result = WSAStartup((1 << 8) | 1, &wd);
/*Result = WSAStartup(MAKEWORD(2,2), &wd); */
if (Result != 0) {
print_last_error("TCP/IP stack initialization failed");
exit(1);
}
initialized = true;
atexit(bip_cleanup);
}
}
/**
* @brief Set the BACnet IPv4 UDP port number
* @param port - IPv4 UDP port number - in host byte order
*/
void bip_set_port(uint16_t port)
{
BIP_Port = htons(port);
}
/**
* @brief Get the BACnet IPv4 UDP port number
* @return IPv4 UDP port number - in host byte order
*/
uint16_t bip_get_port(void)
{
return ntohs(BIP_Port);
}
/**
* @brief Get the IPv4 address for my interface. Used for sending src address.
* @param addr - BACnet datalink address
*/
void bip_get_my_address(BACNET_ADDRESS *addr)
{
unsigned int i = 0;
if (addr) {
addr->mac_len = 6;
memcpy(&addr->mac[0], &BIP_Address.s_addr, 4);
memcpy(&addr->mac[4], &BIP_Port, 2);
/* local only, no routing */
addr->net = 0;
/* no SLEN */
addr->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
/* no SADR */
addr->adr[i] = 0;
}
}
}
/**
* Get the IPv4 broadcast address for my interface.
*
* @param addr - BACnet datalink address
*/
void bip_get_broadcast_address(BACNET_ADDRESS *dest)
{
int i = 0; /* counter */
if (dest) {
dest->mac_len = 6;
memcpy(&dest->mac[0], &BIP_Broadcast_Address.s_addr, 4);
memcpy(&dest->mac[4], &BIP_Port, 2);
dest->net = BACNET_BROADCAST_NETWORK;
dest->len = 0; /* no SLEN */
for (i = 0; i < MAX_MAC_LEN; i++) {
/* no SADR */
dest->adr[i] = 0;
}
}
return;
}
/**
* Set the BACnet/IP address
*
* @param addr - network IPv4 address
*/
bool bip_set_addr(BACNET_IP_ADDRESS *addr)
{
/* not something we do here within this application */
return false;
}
/**
* @brief Get the BACnet/IP address
* @param addr - network IPv4 address
* @return true if the address was retrieved
*/
bool bip_get_addr(BACNET_IP_ADDRESS *addr)
{
if (addr) {
memcpy(&addr->address[0], &BIP_Address.s_addr, 4);
addr->port = ntohs(BIP_Port);
}
return true;
}
/**
* @brief Set the BACnet/IP address
* @param addr - network IPv4 address
* @return true if the address was set
*/
bool bip_set_broadcast_addr(BACNET_IP_ADDRESS *addr)
{
/* not something we do within this application */
return false;
}
/**
* Get the BACnet/IP address
*
* @return BACnet/IP address
*/
bool bip_get_broadcast_addr(BACNET_IP_ADDRESS *addr)
{
if (addr) {
memcpy(&addr->address[0], &BIP_Broadcast_Address.s_addr, 4);
addr->port = ntohs(BIP_Port);
}
return true;
}
/**
* @brief Set the BACnet/IP subnet mask CIDR prefix
* @return true if the subnet mask CIDR prefix is set
*/
bool bip_set_subnet_prefix(uint8_t prefix)
{
/* not something we do within this application */
return false;
}
/**
* @brief Get the BACnet/IP subnet mask CIDR prefix
* @return subnet mask CIDR prefix
*/
uint8_t bip_get_subnet_prefix(void)
{
uint32_t address = 0;
uint32_t broadcast = 0;
uint32_t test_broadcast = 0;
uint32_t mask = 0xFFFFFFFE;
uint8_t prefix = 0;
address = BIP_Broadcast_Address.s_addr;
broadcast = BIP_Broadcast_Address.s_addr;
/* calculate the subnet prefix from the broadcast address */
for (prefix = 1; prefix <= 32; prefix++) {
test_broadcast = (address & mask) | (~mask);
if (test_broadcast == broadcast) {
break;
}
mask = mask<<1;
}
return prefix;
}
/**
* The send function for BACnet/IP driver layer
*
* @param dest - Points to a BACNET_IP_ADDRESS structure containing the
* destination address.
* @param mtu - the bytes of data to send
* @param mtu_len - the number of bytes of data to send
*
* @return Upon successful completion, returns the number of bytes sent.
* Otherwise, -1 shall be returned and errno set to indicate the error.
*/
int bip_send_mpdu(BACNET_IP_ADDRESS *dest, uint8_t *mtu, uint16_t mtu_len)
{
struct sockaddr_in bip_dest = { 0 };
int rv = 0;
/* assumes that the driver has already been initialized */
if (BIP_Socket == INVALID_SOCKET) {
if (BIP_Debug) {
fprintf(stderr, "BIP: driver not initialized!\n");
fflush(stderr);
}
return -1;
}
/* load destination IP address */
bip_dest.sin_family = AF_INET;
memcpy(&bip_dest.sin_addr.s_addr, &dest->address[0], 4);
bip_dest.sin_port = htons(dest->port);
/* Send the packet */
debug_print_ipv4("Sending MPDU->", &bip_dest.sin_addr, bip_dest.sin_port,
mtu_len);
rv = sendto(BIP_Socket, (char *)mtu, mtu_len, 0,
(struct sockaddr *)&bip_dest, sizeof(struct sockaddr));
if (rv == SOCKET_ERROR) {
print_last_error("sendto");
}
return rv;
}
/**
* BACnet/IP Datalink Receive handler.
*
* @param src - returns the source address
* @param npdu - returns the NPDU buffer
* @param max_npdu -maximum size of the NPDU buffer
* @param timeout - number of milliseconds to wait for a packet
*
* @return Number of bytes received, or 0 if none or timeout.
*/
uint16_t bip_receive(
BACNET_ADDRESS *src, uint8_t *npdu, uint16_t max_npdu, unsigned timeout)
{
uint16_t npdu_len = 0; /* return value */
fd_set read_fds;
int max = 0;
struct timeval select_timeout;
struct sockaddr_in sin = { 0 };
BACNET_IP_ADDRESS addr = { { 0 } };
socklen_t sin_len = sizeof(sin);
int received_bytes = 0;
int offset = 0;
uint16_t i = 0;
/* Make sure the socket is open */
if (BIP_Socket == INVALID_SOCKET) {
return 0;
}
/* we could just use a non-blocking socket, but that consumes all
the CPU time. We can use a timeout; it is only supported as
a select. */
if (timeout >= 1000) {
select_timeout.tv_sec = timeout / 1000;
select_timeout.tv_usec =
1000 * (timeout - select_timeout.tv_sec * 1000);
} else {
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 1000 * timeout;
}
FD_ZERO(&read_fds);
FD_SET(BIP_Socket, &read_fds);
max = BIP_Socket;
/* see if there is a packet for us */
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) {
received_bytes = recvfrom(BIP_Socket, (char *)&npdu[0], max_npdu, 0,
(struct sockaddr *)&sin, &sin_len);
} else {
return 0;
}
/* See if there is a problem */
if (received_bytes < 0) {
return 0;
}
/* no problem, just no bytes */
if (received_bytes == 0) {
return 0;
}
/* the signature of a BACnet/IPv packet */
if (npdu[0] != BVLL_TYPE_BACNET_IP) {
return 0;
}
/* Data link layer addressing between B/IPv4 nodes consists of a 32-bit
IPv4 address followed by a two-octet UDP port number (both of which
shall be transmitted with the most significant octet first). This
address shall be referred to as a B/IPv4 address.
*/
memcpy(&addr.address[0], &sin.sin_addr.s_addr, 4);
addr.port = ntohs(sin.sin_port);
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);
if (offset > 0) {
npdu_len = received_bytes - offset;
if (npdu_len <= max_npdu) {
/* shift the buffer to return a valid NPDU */
for (i = 0; i < npdu_len; i++) {
npdu[i] = npdu[offset + i];
}
} else {
npdu_len = 0;
}
}
return npdu_len;
}
/**
* The common send function for BACnet/IP application layer
*
* @param dest - Points to a #BACNET_ADDRESS structure containing the
* destination address.
* @param npdu_data - Points to a BACNET_NPDU_DATA structure containing the
* destination network layer control flags and data.
* @param mtu - the bytes of data to send
* @param mtu_len - the number of bytes of data to send
* @return Upon successful completion, returns the number of bytes sent.
* Otherwise, -1 shall be returned and errno set to indicate the error.
*/
int bip_send_pdu(BACNET_ADDRESS *dest,
BACNET_NPDU_DATA *npdu_data,
uint8_t *pdu,
unsigned pdu_len)
{
return bvlc_send_pdu(dest, npdu_data, pdu, pdu_len);
}
/**
* @brief gets an IP address by hostname (or string of numbers)
*
* gets an IP address by name, where name can be a string that is an
* IP address in dotted form, or a name that is a domain name
*
* @param host_name - the host name
* @return true if the address was retrieved
*/
bool bip_get_addr_by_name(const char *host_name, BACNET_IP_ADDRESS *addr)
{
struct hostent *host_ent;
bip_init_windows();
if ((host_ent = gethostbyname(host_name)) == NULL) {
return false;
}
if (addr) {
/* Host addresses in a struct hostent structure are always
given in network byte order */
/* h_addr: This is a synonym for h_addr_list[0];
in other words, it is the first host address.*/
memcpy(&addr->address[0], host_ent->h_addr, 4);
}
return true;
}
/* To fill a need, we invent the gethostaddr() function. */
static long gethostaddr(
void)
{
struct hostent *host_ent;
char host_name[255];
if (gethostname(host_name, sizeof(host_name)) != 0) {
print_last_error("gethostname");
exit(1);
}
if ((host_ent = gethostbyname(host_name)) == NULL) {
print_last_error("gethostbyname");
exit(1);
}
if (BIP_Debug) {
fprintf(stderr, "BIP: host %s at %u.%u.%u.%u\n", host_name,
(unsigned) ((uint8_t *) host_ent->h_addr)[0],
(unsigned) ((uint8_t *) host_ent->h_addr)[1],
(unsigned) ((uint8_t *) host_ent->h_addr)[2],
(unsigned) ((uint8_t *) host_ent->h_addr)[3]);
fflush(stderr);
}
/* note: network byte order */
return *(long *) host_ent->h_addr;
}
#if ((USE_INADDR == 0) || (USE_CLASSADDR == 0))
/* returns the subnet mask in network byte order */
static uint32_t getIpMaskForIpAddress(
uint32_t ipAddress)
{
/* Allocate information for up to 16 NICs */
IP_ADAPTER_INFO AdapterInfo[16];
/* Save memory size of buffer */
DWORD dwBufLen = sizeof(AdapterInfo);
uint32_t ipMask = INADDR_BROADCAST;
bool found = false;
PIP_ADAPTER_INFO pAdapterInfo;
/* GetAdapterInfo:
[out] buffer to receive data
[in] size of receive data buffer */
DWORD dwStatus = GetAdaptersInfo(AdapterInfo,
&dwBufLen);
if (dwStatus == ERROR_SUCCESS) {
/* Verify return value is valid, no buffer overflow
Contains pointer to current adapter info */
pAdapterInfo = AdapterInfo;
do {
IP_ADDR_STRING *pIpAddressInfo = &pAdapterInfo->IpAddressList;
do {
unsigned long adapterAddress =
inet_addr(pIpAddressInfo->IpAddress.String);
unsigned long adapterMask =
inet_addr(pIpAddressInfo->IpMask.String);
if (adapterAddress == ipAddress) {
ipMask = adapterMask;
found = true;
}
pIpAddressInfo = pIpAddressInfo->Next;
} while (pIpAddressInfo && !found);
/* Progress through linked list */
pAdapterInfo = pAdapterInfo->Next;
/* Terminate on last adapter */
} while (pAdapterInfo && !found);
}
return ipMask;
}
#endif
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
long broadcast_address = 0;
if (IN_CLASSA(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSA_HOST) | IN_CLASSA_HOST;
else if (IN_CLASSB(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSB_HOST) | IN_CLASSB_HOST;
else if (IN_CLASSC(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSC_HOST) | IN_CLASSC_HOST;
else if (IN_CLASSD(ntohl(net_address)))
broadcast_address =
(ntohl(net_address) & ~IN_CLASSD_HOST) | IN_CLASSD_HOST;
else
broadcast_address = INADDR_BROADCAST;
BIP_Broadcast_Address.s_addr = htonl(broadcast_address));
#else
/* these are network byte order variables */
long broadcast_address = 0;
long net_mask = 0;
net_mask = getIpMaskForIpAddress(net_address);
if (BIP_Debug) {
struct in_addr address;
address.s_addr = net_mask;
fprintf(stderr, "BIP: net mask: %s\n", inet_ntoa(address));
fflush(stderr);
}
broadcast_address = (net_address & net_mask) | (~net_mask);
BIP_Broadcast_Address.s_addr = broadcast_address;
#endif
}
/**
* @brief Gets the local IP address and local broadcast address from the
* system, and saves it into the BACnet/IP data structures.
*
* @param ifname [in] The named interface to use for the network layer.
* Eg, for Windows, ifname is the dotted ip address of the interface
*/
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);
}
if (BIP_Debug) {
fprintf(stderr, "BIP: Interface: %s\n", ifname);
fprintf(stderr, "BIP: Address: %s\n", inet_ntoa(BIP_Address));
fflush(stderr);
}
/* setup local broadcast address */
if (BIP_Broadcast_Address.s_addr == 0) {
set_broadcast_address(BIP_Address.s_addr);
}
}
/** Initialize the BACnet/IP services at the given interface.
* @ingroup DLBIP
* -# Gets the local IP address and local broadcast address from the system,
@@ -339,56 +740,33 @@ bool bip_init(
int rv = 0; /* return from socket lib calls */
struct sockaddr_in sin = { -1 };
int value = 1;
int sock_fd = -1;
int Result;
int Code;
WSADATA wd;
struct in_addr address;
struct in_addr broadcast_address;
SOCKET sock_fd = INVALID_SOCKET;
Result = WSAStartup((1 << 8) | 1, &wd);
/*Result = WSAStartup(MAKEWORD(2,2), &wd); */
if (Result != 0) {
Code = WSAGetLastError();
printf("TCP/IP stack initialization failed\n" " error code: %i %s\n",
Code, winsock_error_code_text(Code));
exit(1);
}
atexit(bip_cleanup);
if (ifname)
bip_init_windows();
if (ifname) {
bip_set_interface(ifname);
/* has address been set? */
address.s_addr = bip_get_addr();
if (address.s_addr == 0) {
address.s_addr = gethostaddr();
if (address.s_addr == (unsigned) -1) {
Code = WSAGetLastError();
printf("Get host address failed\n" " error code: %i %s\n", Code,
winsock_error_code_text(Code));
exit(1);
}
bip_set_addr(address.s_addr);
}
if (BIP_Debug) {
fprintf(stderr, "IP Address: %s\n", inet_ntoa(address));
/* has address been set? */
if (BIP_Address.s_addr == 0) {
BIP_Address.s_addr = gethostaddr();
}
/* has broadcast address been set? */
if (bip_get_broadcast_addr() == 0) {
set_broadcast_address(address.s_addr);
if (BIP_Broadcast_Address.s_addr == 0) {
set_broadcast_address(BIP_Address.s_addr);
}
if (BIP_Debug) {
broadcast_address.s_addr = bip_get_broadcast_addr();
fprintf(stderr, "IP Broadcast Address: %s\n",
inet_ntoa(broadcast_address));
fprintf(stderr, "UDP Port: 0x%04X [%hu]\n", ntohs(bip_get_port()),
ntohs(bip_get_port()));
fprintf(stderr, "BIP: Address: %s\n", inet_ntoa(BIP_Address));
fprintf(stderr, "BIP: Broadcast Address: %s\n",
inet_ntoa(BIP_Broadcast_Address));
fprintf(stderr, "BIP: UDP Port: 0x%04X [%hu]\n", ntohs(BIP_Port),
ntohs(BIP_Port));
fflush(stderr);
}
/* assumes that the driver has already been initialized */
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
bip_set_socket(sock_fd);
BIP_Socket = sock_fd;
if (sock_fd < 0) {
fprintf(stderr, "bip: failed to allocate a socket.\n");
print_last_error("failed to allocate a socket");
return false;
}
/* Allow us to use the same socket for sending and receiving */
@@ -396,36 +774,23 @@ bool bip_init(
rv = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value,
sizeof(value));
if (rv < 0) {
fprintf(stderr, "bip: failed to set REUSEADDR socket option.\n");
close(sock_fd);
bip_set_socket(-1);
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) {
fprintf(stderr, "bip: failed to set BROADCAST socket option.\n");
close(sock_fd);
bip_set_socket(-1);
print_last_error("failed to set BROADCAST socket option");
closesocket(sock_fd);
BIP_Socket = INVALID_SOCKET;
return false;
}
#if 0
/* probably only for Apple... */
/* rebind a port that is already in use.
Note: all users of the port must specify this flag */
rv = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, (char *) &value,
sizeof(value));
if (rv < 0) {
fprintf(stderr, "bip: failed to set REUSEPORT socket option.\n");
close(sock_fd);
bip_set_socket(-1);
return false;
}
#endif
/* bind the socket to the local port number and IP address */
sin.sin_family = AF_INET;
#if defined(USE_INADDR) && USE_INADDR
#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
@@ -442,36 +807,49 @@ bool bip_init(
#else
/* or we could use the specific adapter address
note: already in network byte order */
sin.sin_addr.s_addr = address.s_addr;
sin.sin_addr.s_addr = BIP_Address.s_addr;
#endif
sin.sin_port = bip_get_port();
sin.sin_port = BIP_Port;
memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero));
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) {
fprintf(stderr, "bip: failed to bind to %s port %hu\n",
inet_ntoa(sin.sin_addr), ntohs(bip_get_port()));
close(sock_fd);
bip_set_socket(-1);
print_last_error("failed to bind");
closesocket(sock_fd);
BIP_Socket = INVALID_SOCKET;
return false;
}
return true;
}
/**
* @brief Determine if this BACnet/IP datalink is valid
* @return true if the BACnet/IP datalink is valid
*/
bool bip_valid(void)
{
return (BIP_Socket != INVALID_SOCKET);
}
/** Cleanup and close out the BACnet/IP services by closing the socket.
* @ingroup DLBIP
*/
void bip_cleanup(
void)
{
int sock_fd = 0;
SOCKET sock_fd = 0;
if (bip_valid()) {
sock_fd = bip_socket();
close(sock_fd);
if (BIP_Socket != INVALID_SOCKET) {
sock_fd = BIP_Socket;
closesocket(sock_fd);
}
bip_set_socket(-1);
BIP_Socket = INVALID_SOCKET;
WSACleanup();
return;