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:
+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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user