mirror of
https://github.com/stargieg/bacnet-stack
synced 2025-10-26 23:35:52 +08:00
275 lines
8.5 KiB
C
275 lines
8.5 KiB
C
/*####COPYRIGHTBEGIN####
|
|
-------------------------------------------
|
|
Copyright (C) 2005 Steve Karg
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to:
|
|
The Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA.
|
|
|
|
As a special exception, if other files instantiate templates or
|
|
use macros or inline functions from this file, or you compile
|
|
this file and link it with other works to produce a work based
|
|
on this file, this file does not by itself cause the resulting
|
|
work to be covered by the GNU General Public License. However
|
|
the source code for this file must still be made available in
|
|
accordance with section (3) of the GNU General Public License.
|
|
|
|
This exception does not invalidate any other reasons why a work
|
|
based on this file might be covered by the GNU General Public
|
|
License.
|
|
-------------------------------------------
|
|
####COPYRIGHTEND####*/
|
|
|
|
#include <stdint.h> /* for standard integer types uint8_t etc. */
|
|
#include <stdbool.h> /* for the standard bool type. */
|
|
#include "bacdcode.h"
|
|
#include "bip.h"
|
|
#include "net.h"
|
|
#include <ifaddrs.h>
|
|
|
|
/** @file linux/bip-init.c Initializes BACnet/IP interface (BSD/MAC OS X). */
|
|
|
|
bool BIP_Debug = false;
|
|
|
|
void *get_addr_ptr(
|
|
struct sockaddr *sockaddr_ptr)
|
|
{
|
|
void *addr_ptr;
|
|
if (sockaddr_ptr->sa_family == AF_INET) {
|
|
addr_ptr = &((struct sockaddr_in *) sockaddr_ptr)->sin_addr;
|
|
} else if (sockaddr_ptr->sa_family == AF_INET6) {
|
|
addr_ptr = &((struct sockaddr_in6 *) sockaddr_ptr)->sin6_addr;
|
|
}
|
|
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.
|
|
*
|
|
* @param ifname [in] The named interface to use for the network layer.
|
|
* Eg, for MAC OS X, ifname is en0, en1, and others.
|
|
* @param addr [out] The netmask addr, broadcast addr, ip addr.
|
|
* @param request [in] addr broadaddr netmask
|
|
*/
|
|
static int get_local_address(
|
|
char *ifname,
|
|
struct in_addr *addr,
|
|
char *request)
|
|
{
|
|
|
|
char rv; /* return value */
|
|
|
|
struct ifaddrs *ifaddrs_ptr;
|
|
int status;
|
|
status = getifaddrs(&ifaddrs_ptr);
|
|
if (BIP_Debug) {
|
|
if (status == -1) {
|
|
fprintf(stderr, "Error in 'getifaddrs': %d (%s)\n", errno,
|
|
strerror(errno));
|
|
}
|
|
}
|
|
while (ifaddrs_ptr) {
|
|
if ((ifaddrs_ptr->ifa_addr->sa_family == AF_INET) &&
|
|
(strcmp(ifaddrs_ptr->ifa_name, ifname) == 0)) {
|
|
void *addr_ptr;
|
|
if (!ifaddrs_ptr->ifa_addr) {
|
|
return rv;
|
|
}
|
|
if (strcmp(request, "addr") == 0) {
|
|
addr_ptr = get_addr_ptr(ifaddrs_ptr->ifa_addr);
|
|
} else if (strcmp(request, "broadaddr") == 0) {
|
|
addr_ptr = get_addr_ptr(ifaddrs_ptr->ifa_broadaddr);
|
|
} else if (strcmp(request, "netmask") == 0) {
|
|
addr_ptr = get_addr_ptr(ifaddrs_ptr->ifa_netmask);
|
|
}
|
|
if (addr_ptr) {
|
|
memcpy(addr, addr_ptr, sizeof(struct in_addr));
|
|
}
|
|
}
|
|
ifaddrs_ptr = ifaddrs_ptr->ifa_next;
|
|
}
|
|
freeifaddrs(ifaddrs_ptr);
|
|
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 MAC OS X, ifname is en0, en1, and others.
|
|
*/
|
|
void bip_set_interface(
|
|
char *ifname)
|
|
{
|
|
struct in_addr local_address;
|
|
struct in_addr broadcast_address;
|
|
int rv = 0;
|
|
|
|
/* setup local address */
|
|
char *request = "addr";
|
|
rv = get_local_address(ifname, &local_address, request);
|
|
if (rv < 0) {
|
|
local_address.s_addr = 0;
|
|
}
|
|
bip_set_addr(local_address.s_addr);
|
|
if (BIP_Debug) {
|
|
fprintf(stderr, "Interface: %s\n", ifname);
|
|
fprintf(stderr, "IP Address: %s\n", inet_ntoa(local_address));
|
|
}
|
|
/* setup local broadcast address */
|
|
request = "broadaddr";
|
|
rv = get_local_address(ifname, &broadcast_address, request);
|
|
if (rv < 0) {
|
|
broadcast_address.s_addr = ~0;
|
|
}
|
|
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()));
|
|
}
|
|
}
|
|
|
|
/** Initialize the BACnet/IP services at the given interface.
|
|
* @ingroup DLBIP
|
|
* -# Gets the local IP address and local broadcast address from the system,
|
|
* and saves it into the BACnet/IP data structures.
|
|
* -# Opens a UDP socket
|
|
* -# Configures the socket for sending and receiving
|
|
* -# Configures the socket so it can send broadcasts
|
|
* -# Binds the socket to the local IP address at the specified port for
|
|
* BACnet/IP (by default, 0xBAC0 = 47808).
|
|
*
|
|
* @note For MAC OS X, ifname is en0, en1, and others.
|
|
*
|
|
* @param ifname [in] The named interface to use for the network layer.
|
|
* If NULL, the "en0" interface is assigned.
|
|
* @return True if the socket is successfully opened for BACnet/IP,
|
|
* else False if the socket functions fail.
|
|
*/
|
|
bool bip_init(
|
|
char *ifname)
|
|
{
|
|
int status = 0; /* return from socket lib calls */
|
|
struct sockaddr_in sin;
|
|
int sockopt = 0;
|
|
int sock_fd = -1;
|
|
|
|
if (ifname) {
|
|
bip_set_interface(ifname);
|
|
if (BIP_Debug) {
|
|
printf("interface %s\n", ifname);
|
|
}
|
|
} else {
|
|
bip_set_interface("en0");
|
|
if (BIP_Debug) {
|
|
printf("interface default en0\n");
|
|
}
|
|
}
|
|
/* 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)
|
|
return false;
|
|
/* Allow us to use the same socket for sending and receiving */
|
|
/* This makes sure that the src port is correct when sending */
|
|
sockopt = 1;
|
|
status =
|
|
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt,
|
|
sizeof(sockopt));
|
|
if (status < 0) {
|
|
close(sock_fd);
|
|
bip_set_socket(-1);
|
|
return status;
|
|
}
|
|
/* allow us to send a broadcast */
|
|
status =
|
|
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &sockopt,
|
|
sizeof(sockopt));
|
|
if (status < 0) {
|
|
close(sock_fd);
|
|
bip_set_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();
|
|
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);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/** Cleanup and close out the BACnet/IP services by closing the socket.
|
|
* @ingroup DLBIP
|
|
*/
|
|
void bip_cleanup(
|
|
void)
|
|
{
|
|
int sock_fd = 0;
|
|
|
|
if (bip_valid()) {
|
|
sock_fd = bip_socket();
|
|
close(sock_fd);
|
|
}
|
|
bip_set_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";
|
|
if (BIP_Debug) {
|
|
printf("ifname %s", ifname);
|
|
}
|
|
char *request = "netmask";
|
|
rv = get_local_address(ifname, netmask, request);
|
|
|
|
return rv;
|
|
}
|