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:
@@ -43,7 +43,15 @@
|
||||
|
||||
/** @file linux/bip-init.c Initializes BACnet/IP interface (Linux). */
|
||||
|
||||
bool BIP_Debug = false;
|
||||
static bool BIP_Debug = false;
|
||||
|
||||
/**
|
||||
* @brief Enabled debug printing of BACnet/IPv4
|
||||
*/
|
||||
void bip_debug_enable(void)
|
||||
{
|
||||
BIP_Debug = true;
|
||||
}
|
||||
|
||||
/* gets an IP address by name, where name can be a
|
||||
string that is an IP address in dotted form, or
|
||||
|
||||
@@ -130,13 +130,13 @@ uint8_t *bip_get_broadcast_addr(void)
|
||||
|
||||
void bip_set_port(uint16_t port)
|
||||
{ /* in network byte order */
|
||||
BIP_Port = port;
|
||||
BIP_Port = htons(port);
|
||||
}
|
||||
|
||||
/* returns network byte order */
|
||||
uint16_t bip_get_port(void)
|
||||
{
|
||||
return BIP_Port;
|
||||
return ntohs(BIP_Port);
|
||||
}
|
||||
|
||||
static int bip_decode_bip_address(BACNET_ADDRESS * bac_addr,
|
||||
|
||||
@@ -36,8 +36,6 @@
|
||||
|
||||
#define BVLL_TYPE_BACNET_IP (0x81)
|
||||
|
||||
extern bool BIP_Debug;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
@@ -80,9 +78,9 @@ extern "C" {
|
||||
|
||||
unsigned timeout); /* milliseconds to wait for a packet */
|
||||
|
||||
/* use network byte order for setting */
|
||||
/* use host byte order for setting */
|
||||
void bip_set_port(uint16_t port);
|
||||
/* returns network byte order */
|
||||
/* returns host byte order */
|
||||
uint16_t bip_get_port(void);
|
||||
|
||||
/* use network byte order for setting */
|
||||
@@ -102,6 +100,7 @@ extern "C" {
|
||||
an IP address in network byte order */
|
||||
long bip_getaddrbyname(const char *host_name);
|
||||
|
||||
void bip_debug_enable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
/*=============== SEEPROM ================*/
|
||||
/* data version - use to check valid version */
|
||||
#define SEEPROM_ID 0xBAC0
|
||||
#define SEEPROM_ID 0xBAC0U
|
||||
#define SEEPROM_VERSION 0x0001
|
||||
|
||||
#define SEEPROM_BYTES_MAX (2*1024)
|
||||
|
||||
+424
-58
@@ -34,16 +34,387 @@
|
||||
|
||||
#include <stdint.h> /* for standard integer types uint8_t etc. */
|
||||
#include <stdbool.h> /* for the standard bool type. */
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/datalink/bip.h"
|
||||
#include "bacport.h"
|
||||
#include <ifaddrs.h>
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/datalink/bip.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/basic/bbmd/h_bbmd.h"
|
||||
#include "bacport.h"
|
||||
|
||||
/** @file linux/bip-init.c Initializes BACnet/IP interface (BSD/MAC OS X). */
|
||||
/**
|
||||
* @file
|
||||
* @brief Initializes BACnet/IP interface (BSD/MAC OS X).
|
||||
*/
|
||||
|
||||
bool BIP_Debug = true;
|
||||
/* unix socket */
|
||||
static int BIP_Socket = -1;
|
||||
|
||||
void *get_addr_ptr(
|
||||
/* 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_Addr;
|
||||
/* 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)
|
||||
{
|
||||
if (BIP_Debug) {
|
||||
fprintf(stderr, "BIP: %s %s:%hu (%u bytes)\n", str, inet_ntoa(*addr),
|
||||
ntohs(port), count);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enabled debug printing of BACnet/IPv4
|
||||
*/
|
||||
void bip_debug_enable(void)
|
||||
{
|
||||
BIP_Debug = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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_Addr.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 within this driver */
|
||||
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 driver */
|
||||
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_Addr.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 driver */
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the BACnet/IP subnet mask CIDR prefix
|
||||
* @return subnet mask CIDR prefix 1..32
|
||||
*/
|
||||
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_Address.s_addr;
|
||||
broadcast = BIP_Broadcast_Addr.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 };
|
||||
|
||||
/* assumes that the driver has already been initialized */
|
||||
if (BIP_Socket < 0) {
|
||||
if (BIP_Debug) {
|
||||
fprintf(stderr, "BIP: driver not initialized!\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
return BIP_Socket;
|
||||
}
|
||||
/* 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);
|
||||
return sendto(BIP_Socket, (char *)mtu, mtu_len, 0,
|
||||
(struct sockaddr *)&bip_dest, sizeof(struct sockaddr));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 < 0) {
|
||||
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;
|
||||
debug_print_ipv4("Received NPDU->", &sin.sin_addr, sin.sin_port,
|
||||
npdu_len);
|
||||
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 {
|
||||
if (BIP_Debug) {
|
||||
fprintf(stderr, "BIP: NPDU dropped!\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void *get_addr_ptr(
|
||||
struct sockaddr *sockaddr_ptr)
|
||||
{
|
||||
void *addr_ptr;
|
||||
@@ -55,22 +426,6 @@ void *get_addr_ptr(
|
||||
return addr_ptr;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct hostent *host_ent;
|
||||
|
||||
if ((host_ent = gethostbyname(host_name)) == NULL)
|
||||
return 0;
|
||||
|
||||
return *(long *) host_ent->h_addr;
|
||||
}
|
||||
|
||||
/** Gets the local IP address and local broadcast address from the system,
|
||||
* and saves it into the BACnet/IP data structures.
|
||||
*
|
||||
@@ -118,6 +473,24 @@ static int get_local_address(
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** Get the netmask of the BACnet/IP's interface via an getifaddrs() call.
|
||||
* @param netmask [out] The netmask, in host order.
|
||||
* @return 0 on success, else the error from the getifaddrs() call.
|
||||
*/
|
||||
int bip_get_local_netmask(
|
||||
struct in_addr *netmask)
|
||||
{
|
||||
int rv;
|
||||
char *ifname = getenv("BACNET_IFACE"); /* will probably be null */
|
||||
if (ifname == NULL)
|
||||
ifname = "en0";
|
||||
printf("ifname %s", ifname);
|
||||
char *request = "netmask";
|
||||
rv = get_local_address(ifname, netmask, request);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** Gets the local IP address and local broadcast address from the system,
|
||||
* and saves it into the BACnet/IP data structures.
|
||||
*
|
||||
@@ -137,23 +510,26 @@ void bip_set_interface(
|
||||
if (rv < 0) {
|
||||
local_address.s_addr = 0;
|
||||
}
|
||||
bip_set_addr(local_address.s_addr);
|
||||
BIP_Address.s_addr = local_address.s_addr;
|
||||
if (BIP_Debug) {
|
||||
fprintf(stderr, "Interface: %s\n", ifname);
|
||||
fprintf(stderr, "IP Address: %s\n", inet_ntoa(local_address));
|
||||
fprintf(stderr, "BIP: Interface: %s\n", ifname);
|
||||
fprintf(stderr, "BIP: Address: %s\n", inet_ntoa(local_address));
|
||||
fflush(stderr);
|
||||
}
|
||||
/* setup local broadcast address */
|
||||
request = "broadaddr";
|
||||
rv = get_local_address(ifname, &broadcast_address, request);
|
||||
if (rv < 0) {
|
||||
broadcast_address.s_addr = ~0;
|
||||
BIP_Broadcast_Addr.s_addr = ~0;
|
||||
} else {
|
||||
BIP_Broadcast_Addr.s_addr = broadcast_address.s_addr;
|
||||
}
|
||||
bip_set_broadcast_addr(broadcast_address.s_addr);
|
||||
if (BIP_Debug) {
|
||||
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: Broadcast Address: %s\n",
|
||||
inet_ntoa(BIP_Broadcast_Addr));
|
||||
fprintf(stderr, "BIP: UDP Port: 0x%04X [%hu]\n", ntohs(BIP_Port),
|
||||
ntohs(BIP_Port));
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +566,7 @@ bool bip_init(
|
||||
}
|
||||
/* 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)
|
||||
return false;
|
||||
/* Allow us to use the same socket for sending and receiving */
|
||||
@@ -201,7 +577,7 @@ bool bip_init(
|
||||
sizeof(sockopt));
|
||||
if (status < 0) {
|
||||
close(sock_fd);
|
||||
bip_set_socket(-1);
|
||||
BIP_Socket = -1;
|
||||
return status;
|
||||
}
|
||||
/* allow us to send a broadcast */
|
||||
@@ -210,25 +586,34 @@ bool bip_init(
|
||||
sizeof(sockopt));
|
||||
if (status < 0) {
|
||||
close(sock_fd);
|
||||
bip_set_socket(-1);
|
||||
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_get_port();
|
||||
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_set_socket(-1);
|
||||
BIP_Socket = -1;
|
||||
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 != -1);
|
||||
}
|
||||
|
||||
/** Cleanup and close out the BACnet/IP services by closing the socket.
|
||||
* @ingroup DLBIP
|
||||
*/
|
||||
@@ -237,29 +622,10 @@ void bip_cleanup(
|
||||
{
|
||||
int sock_fd = 0;
|
||||
|
||||
if (bip_valid()) {
|
||||
sock_fd = bip_socket();
|
||||
close(sock_fd);
|
||||
if (BIP_Socket != -1) {
|
||||
close(BIP_Socket);
|
||||
}
|
||||
bip_set_socket(-1);
|
||||
BIP_Socket = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** Get the netmask of the BACnet/IP's interface via an getifaddrs() call.
|
||||
* @param netmask [out] The netmask, in host order.
|
||||
* @return 0 on success, else the error from the getifaddrs() call.
|
||||
*/
|
||||
int bip_get_local_netmask(
|
||||
struct in_addr *netmask)
|
||||
{
|
||||
int rv;
|
||||
char *ifname = getenv("BACNET_IFACE"); /* will probably be null */
|
||||
if (ifname == NULL)
|
||||
ifname = "en0";
|
||||
printf("ifname %s", ifname);
|
||||
char *request = "netmask";
|
||||
rv = get_local_address(ifname, netmask, request);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#include "bacnet/datalink/bip.h"
|
||||
|
||||
long bip_getaddrbyname(
|
||||
long bip_get_addr_by_name(
|
||||
const char *host_name)
|
||||
{
|
||||
return 0;
|
||||
@@ -28,15 +28,15 @@ void bip_cleanup (void)
|
||||
|
||||
bool bip_init(char *ifname)
|
||||
{
|
||||
|
||||
|
||||
tcpip_adapter_ip_info_t ip_info = { 0 };
|
||||
|
||||
int value = 1;
|
||||
|
||||
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
|
||||
|
||||
bip_set_interface(ifname);
|
||||
bip_set_port(htons(0xBAC0));
|
||||
bip_set_interface(ifname);
|
||||
bip_set_port(0xBAC0U);
|
||||
bip_set_addr(ip_info.ip.addr);
|
||||
bip_set_broadcast_addr((ip_info.ip.addr&ip_info.netmask.addr)|(~ip_info.netmask.addr));
|
||||
|
||||
@@ -44,7 +44,7 @@ bool bip_init(char *ifname)
|
||||
struct sockaddr_in saddr = { 0 };
|
||||
|
||||
saddr.sin_family = PF_INET;
|
||||
saddr.sin_port = htons(0xBAC0);
|
||||
saddr.sin_port = htons(0xBAC0U);
|
||||
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
|
||||
|
||||
|
||||
@@ -101,12 +101,15 @@
|
||||
#include <netdb.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
|
||||
/** @file linux/net.h Includes Linux network headers. */
|
||||
/** @file linux/bacport.h Includes Linux network headers. */
|
||||
|
||||
/* Local helper functions for this port */
|
||||
BACNET_STACK_EXPORT
|
||||
extern int bip_get_local_netmask(
|
||||
struct in_addr *netmask);
|
||||
|
||||
extern int bip_get_local_address_ioctl(
|
||||
char *ifname,
|
||||
struct in_addr *addr,
|
||||
int request);
|
||||
|
||||
#endif
|
||||
|
||||
+457
-93
@@ -32,39 +32,395 @@
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
#include <stdint.h> /* for standard integer types uint8_t etc. */
|
||||
#include <stdbool.h> /* for the standard bool type. */
|
||||
#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/datalink/bip.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/basic/bbmd/h_bbmd.h"
|
||||
#include "bacport.h"
|
||||
|
||||
/** @file linux/bip-init.c Initializes BACnet/IP interface (Linux). */
|
||||
|
||||
bool BIP_Debug = false;
|
||||
/* unix socket */
|
||||
static int BIP_Socket = -1;
|
||||
|
||||
/* 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_Addr;
|
||||
/* 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)
|
||||
{
|
||||
if (BIP_Debug) {
|
||||
fprintf(stderr, "BIP: %s %s:%hu (%u bytes)\n", str, inet_ntoa(*addr),
|
||||
ntohs(port), count);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enabled debug printing of BACnet/IPv4
|
||||
*/
|
||||
void bip_debug_enable(void)
|
||||
{
|
||||
BIP_Debug = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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_Addr.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 within this driver */
|
||||
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 driver */
|
||||
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_Addr.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 driver */
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the BACnet/IP subnet mask CIDR prefix
|
||||
* @return subnet mask CIDR prefix 1..32
|
||||
*/
|
||||
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_Address.s_addr;
|
||||
broadcast = BIP_Broadcast_Addr.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 };
|
||||
|
||||
/* assumes that the driver has already been initialized */
|
||||
if (BIP_Socket < 0) {
|
||||
if (BIP_Debug) {
|
||||
fprintf(stderr, "BIP: driver not initialized!\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
return BIP_Socket;
|
||||
}
|
||||
/* 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);
|
||||
return sendto(BIP_Socket, (char *)mtu, mtu_len, 0,
|
||||
(struct sockaddr *)&bip_dest, sizeof(struct sockaddr));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 < 0) {
|
||||
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;
|
||||
debug_print_ipv4("Received NPDU->", &sin.sin_addr, sin.sin_port,
|
||||
npdu_len);
|
||||
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 {
|
||||
if (BIP_Debug) {
|
||||
fprintf(stderr, "BIP: NPDU dropped!\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
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;
|
||||
|
||||
if ((host_ent = gethostbyname(host_name)) == NULL)
|
||||
return 0;
|
||||
if ((host_ent = gethostbyname(host_name)) == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return *(long *) host_ent->h_addr;
|
||||
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;
|
||||
}
|
||||
|
||||
static int get_local_ifr_ioctl(
|
||||
char *ifname,
|
||||
struct ifreq *ifr,
|
||||
int request)
|
||||
/**
|
||||
* @brief Issue a specific request for an interface via an ioctl() call.
|
||||
* @param ifname - the interface name
|
||||
* @param ifr - interface request
|
||||
* @param request - the ioctl() request
|
||||
* @return 0 on success, else the error from the ioctl() call.
|
||||
*/
|
||||
static int get_local_ifr_ioctl(char *ifname, struct ifreq *ifr, int request)
|
||||
{
|
||||
int fd;
|
||||
int rv; /* return value */
|
||||
int rv; /* return value */
|
||||
|
||||
strncpy(ifr->ifr_name, ifname, sizeof(ifr->ifr_name));
|
||||
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
@@ -78,70 +434,81 @@ static int get_local_ifr_ioctl(
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* forward prototype required for compilers */
|
||||
int get_local_address_ioctl(
|
||||
char *ifname,
|
||||
struct in_addr *addr,
|
||||
int request);
|
||||
|
||||
int get_local_address_ioctl(
|
||||
char *ifname,
|
||||
struct in_addr *addr,
|
||||
int request)
|
||||
/**
|
||||
* @brief Issue a specific request foor an interface via an ioctl() call.
|
||||
* @param ifname - the interface name
|
||||
* @param addr [out] the address in host order.
|
||||
* @param request - the ioctl() request
|
||||
* @return 0 on success, else the error from the ioctl() call.
|
||||
*/
|
||||
int bip_get_local_address_ioctl(char *ifname, struct in_addr *addr, int request)
|
||||
{
|
||||
struct ifreq ifr = { {{0}} };
|
||||
struct ifreq ifr = { { { 0 } } };
|
||||
struct sockaddr_in *tcpip_address;
|
||||
int rv; /* return value */
|
||||
int rv; /* return value */
|
||||
|
||||
rv = get_local_ifr_ioctl(ifname, &ifr, request);
|
||||
if (rv >= 0) {
|
||||
tcpip_address = (struct sockaddr_in *) &ifr.ifr_addr;
|
||||
tcpip_address = (struct sockaddr_in *)&ifr.ifr_addr;
|
||||
memcpy(addr, &tcpip_address->sin_addr, sizeof(struct in_addr));
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the netmask of the BACnet/IP's interface via an ioctl() call.
|
||||
* @param netmask [out] The netmask, in host order.
|
||||
* @return 0 on success, else the error from the ioctl() call.
|
||||
*/
|
||||
int bip_get_local_netmask(struct in_addr *netmask)
|
||||
{
|
||||
int rv;
|
||||
char *ifname = getenv("BACNET_IFACE"); /* will probably be null */
|
||||
if (ifname == NULL) {
|
||||
ifname = "eth0";
|
||||
}
|
||||
rv = bip_get_local_address_ioctl(ifname, netmask, SIOCGIFNETMASK);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** 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 Linux, ifname is eth0, ath0, arc0, and others.
|
||||
*/
|
||||
void bip_set_interface(
|
||||
char *ifname)
|
||||
void bip_set_interface(char *ifname)
|
||||
{
|
||||
struct in_addr local_address;
|
||||
struct in_addr broadcast_address;
|
||||
struct in_addr netmask;
|
||||
int rv = 0;
|
||||
|
||||
/* setup local address */
|
||||
rv = get_local_address_ioctl(ifname, &local_address, SIOCGIFADDR);
|
||||
rv = bip_get_local_address_ioctl(ifname, &local_address, SIOCGIFADDR);
|
||||
if (rv < 0) {
|
||||
local_address.s_addr = 0;
|
||||
}
|
||||
bip_set_addr(local_address.s_addr);
|
||||
BIP_Address.s_addr = local_address.s_addr;
|
||||
if (BIP_Debug) {
|
||||
fprintf(stderr, "Interface: %s\n", ifname);
|
||||
fprintf(stderr, "IP Address: %s\n", inet_ntoa(local_address));
|
||||
fprintf(stderr, "BIP: Interface: %s\n", ifname);
|
||||
fprintf(stderr, "BIP: Address: %s\n", inet_ntoa(local_address));
|
||||
fflush(stderr);
|
||||
}
|
||||
/* setup local broadcast address */
|
||||
rv = get_local_address_ioctl(ifname, &netmask, SIOCGIFNETMASK);
|
||||
|
||||
rv = bip_get_local_address_ioctl(ifname, &netmask, SIOCGIFNETMASK);
|
||||
if (rv < 0) {
|
||||
broadcast_address.s_addr = ~0;
|
||||
BIP_Broadcast_Addr.s_addr = ~0;
|
||||
} else {
|
||||
BIP_Broadcast_Addr = local_address;
|
||||
BIP_Broadcast_Addr.s_addr |= (~netmask.s_addr);
|
||||
}
|
||||
else {
|
||||
broadcast_address = local_address;
|
||||
broadcast_address.s_addr |= (~netmask.s_addr);
|
||||
}
|
||||
bip_set_broadcast_addr(broadcast_address.s_addr);
|
||||
if (BIP_Debug) {
|
||||
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: Broadcast Address: %s\n",
|
||||
inet_ntoa(BIP_Broadcast_Addr));
|
||||
fprintf(stderr, "BIP: UDP Port: 0x%04X [%hu]\n", ntohs(BIP_Port),
|
||||
ntohs(BIP_Port));
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,87 +529,84 @@ void bip_set_interface(
|
||||
* @return True if the socket is successfully opened for BACnet/IP,
|
||||
* else False if the socket functions fail.
|
||||
*/
|
||||
bool bip_init(
|
||||
char *ifname)
|
||||
bool bip_init(char *ifname)
|
||||
{
|
||||
int status = 0; /* return from socket lib calls */
|
||||
int status = 0; /* return from socket lib calls */
|
||||
struct sockaddr_in sin;
|
||||
int sockopt = 0;
|
||||
int sock_fd = -1;
|
||||
char *ifname_default = "eth0";
|
||||
|
||||
if (ifname)
|
||||
if (ifname) {
|
||||
bip_set_interface(ifname);
|
||||
else
|
||||
bip_set_interface("eth0");
|
||||
} else {
|
||||
bip_set_interface(ifname_default);
|
||||
}
|
||||
if (BIP_Address.s_addr == 0) {
|
||||
fprintf(stderr, "BIP: Failed to get an IP address from %s!\n",
|
||||
ifname?ifname:ifname_default);
|
||||
fflush(stderr);
|
||||
return false;
|
||||
}
|
||||
/* assumes that the driver has already been initialized */
|
||||
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
bip_set_socket(sock_fd);
|
||||
if (sock_fd < 0)
|
||||
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));
|
||||
status = setsockopt(
|
||||
sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt));
|
||||
if (status < 0) {
|
||||
close(sock_fd);
|
||||
bip_set_socket(-1);
|
||||
BIP_Socket = -1;
|
||||
return status;
|
||||
}
|
||||
/* allow us to send a broadcast */
|
||||
status =
|
||||
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &sockopt,
|
||||
sizeof(sockopt));
|
||||
status = setsockopt(
|
||||
sock_fd, SOL_SOCKET, SO_BROADCAST, &sockopt, sizeof(sockopt));
|
||||
if (status < 0) {
|
||||
close(sock_fd);
|
||||
bip_set_socket(-1);
|
||||
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_get_port();
|
||||
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));
|
||||
bind(sock_fd, (const struct sockaddr *)&sin, sizeof(struct sockaddr));
|
||||
if (status < 0) {
|
||||
close(sock_fd);
|
||||
bip_set_socket(-1);
|
||||
BIP_Socket = -1;
|
||||
return false;
|
||||
}
|
||||
bvlc_init();
|
||||
|
||||
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 != -1);
|
||||
}
|
||||
|
||||
/** Cleanup and close out the BACnet/IP services by closing the socket.
|
||||
* @ingroup DLBIP
|
||||
*/
|
||||
void bip_cleanup(
|
||||
void)
|
||||
*/
|
||||
void bip_cleanup(void)
|
||||
{
|
||||
int sock_fd = 0;
|
||||
|
||||
if (bip_valid()) {
|
||||
sock_fd = bip_socket();
|
||||
close(sock_fd);
|
||||
if (BIP_Socket != -1) {
|
||||
close(BIP_Socket);
|
||||
}
|
||||
bip_set_socket(-1);
|
||||
BIP_Socket = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** Get the netmask of the BACnet/IP's interface via an ioctl() call.
|
||||
* @param netmask [out] The netmask, in host order.
|
||||
* @return 0 on success, else the error from the ioctl() call.
|
||||
*/
|
||||
int bip_get_local_netmask(
|
||||
struct in_addr *netmask)
|
||||
{
|
||||
int rv;
|
||||
char *ifname = getenv("BACNET_IFACE"); /* will probably be null */
|
||||
if (ifname == NULL)
|
||||
ifname = "eth0";
|
||||
rv = get_local_address_ioctl(ifname, netmask, SIOCGIFNETMASK);
|
||||
return rv;
|
||||
}
|
||||
|
||||
+1
-1
@@ -414,7 +414,7 @@ bool bip6_init(
|
||||
bip6_set_interface("eth0");
|
||||
}
|
||||
if (BIP6_Addr.port == 0) {
|
||||
bip6_set_port(0xBAC0);
|
||||
bip6_set_port(0xBAC0U);
|
||||
}
|
||||
debug_printf("BIP6: IPv6 UDP port: 0x%04X\n", htons(BIP6_Addr.port));
|
||||
if (BIP6_Broadcast_Addr.address[0] == 0) {
|
||||
|
||||
+11
-7
@@ -44,7 +44,7 @@
|
||||
/** @file bip.c Configuration and Operations for BACnet/IP */
|
||||
|
||||
/* port to use - stored in network byte order */
|
||||
static uint16_t BIP_Port = 0xBAC0;
|
||||
static uint16_t BIP_Port = 0xBAC0U;
|
||||
static bool BIP_Port_Changed;
|
||||
/* IP Address - stored in network byte order */
|
||||
static struct in_addr BIP_Address;
|
||||
@@ -102,13 +102,17 @@ uint32_t bip_get_broadcast_addr(
|
||||
return BIP_Broadcast_Address.s_addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{ /* in network byte order */
|
||||
if (BIP_Port != port) {
|
||||
{
|
||||
if (BIP_Port != htons(port)) {
|
||||
BIP_Port_Changed = true;
|
||||
BIP_Port = port;
|
||||
}
|
||||
BIP_Port = htons(port);
|
||||
}
|
||||
}
|
||||
|
||||
bool bip_port_changed(void)
|
||||
@@ -116,11 +120,11 @@ bool bip_port_changed(void)
|
||||
return BIP_Port_Changed;
|
||||
}
|
||||
|
||||
/* returns network byte order */
|
||||
/* returns host byte order */
|
||||
uint16_t bip_get_port(
|
||||
void)
|
||||
{
|
||||
return BIP_Port;
|
||||
return ntohs(BIP_Port);
|
||||
}
|
||||
|
||||
static void bip_mac_to_addr(
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@
|
||||
|
||||
static int BIP_Socket = -1;
|
||||
/* port to use - stored in host byte order */
|
||||
static uint16_t BIP_Port = 0xBAC0;
|
||||
static uint16_t BIP_Port = 0xBAC0U;
|
||||
/* IP Address - stored in host byte order */
|
||||
static struct in_addr BIP_Address;
|
||||
/* Broadcast Address - stored in host byte order */
|
||||
|
||||
@@ -42,9 +42,15 @@
|
||||
#if (!defined(USE_INADDR) || (USE_INADDR == 0)) && \
|
||||
(!defined(USE_CLASSADDR) || (USE_CLASSADDR == 0))
|
||||
#include <iphlpapi.h>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma comment(lib, "IPHLPAPI.lib")
|
||||
#endif
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#ifdef __MINGW32__
|
||||
@@ -71,10 +77,6 @@ and globals in favor of more secure versions. */
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
#define close closesocket
|
||||
|
||||
typedef int socklen_t;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define strncasecmp(x, y, z) _strnicmp(x, y, z)
|
||||
#endif
|
||||
|
||||
+594
-216
@@ -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;
|
||||
|
||||
+1
-1
@@ -530,7 +530,7 @@ bool bip6_init(
|
||||
exit(1);
|
||||
}
|
||||
if (BIP6_Addr.port == 0) {
|
||||
bip6_set_port(0xBAC0);
|
||||
bip6_set_port(0xBAC0U);
|
||||
}
|
||||
debug_printf("BIP6: IPv6 UDP port: 0x%04X\n", BIP6_Addr.port);
|
||||
bip6_set_interface(ifname);
|
||||
|
||||
Reference in New Issue
Block a user