diff --git a/bacnet-stack/demo/BACnetDemo.workspace b/bacnet-stack/demo/BACnetDemo.workspace
index a7f44ac8..2332eeef 100644
--- a/bacnet-stack/demo/BACnetDemo.workspace
+++ b/bacnet-stack/demo/BACnetDemo.workspace
@@ -2,7 +2,7 @@
-
-
+
+
diff --git a/bacnet-stack/demo/readprop/bacrp.cbp b/bacnet-stack/demo/readprop/bacrp.cbp
index 84ac404f..6da2bc52 100644
--- a/bacnet-stack/demo/readprop/bacrp.cbp
+++ b/bacnet-stack/demo/readprop/bacrp.cbp
@@ -14,9 +14,6 @@
-
-
-
@@ -43,6 +40,7 @@
+
diff --git a/bacnet-stack/demo/server/main.c b/bacnet-stack/demo/server/main.c
index 2b60da6e..8849b350 100644
--- a/bacnet-stack/demo/server/main.c
+++ b/bacnet-stack/demo/server/main.c
@@ -119,7 +119,8 @@ int main(
if (pEnv) {
datalink_set(pEnv));
} else {
- datalink_set("bip");
+ datalink_set(NULL);
+ }
#endif
#if defined(BACDL_BIP)
pEnv = getenv("BACNET_IP_PORT");
@@ -183,6 +184,7 @@ int main(
if (elapsed_seconds) {
last_seconds = current_seconds;
dcc_timer_seconds(elapsed_seconds);
+ bvlc_maintenance_timer(elapsed_seconds);
Load_Control_State_Machine_Handler();
elapsed_milliseconds = elapsed_seconds * 1000;
handler_cov_task(elapsed_seconds);
diff --git a/bacnet-stack/demo/whois/main.c b/bacnet-stack/demo/whois/main.c
index f7b062b2..022a8237 100644
--- a/bacnet-stack/demo/whois/main.c
+++ b/bacnet-stack/demo/whois/main.c
@@ -84,7 +84,7 @@ void MyRejectHandler(
static void Init_Service_Handlers(
void)
{
- /* we need to handle who-is
+ /* we need to handle who-is
to support dynamic device binding to us */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
/* set the handler for all the services we don't implement
@@ -146,10 +146,17 @@ int main(
BACNET_ADDRESS src = { 0 }; /* address where message came from */
uint16_t pdu_len = 0;
unsigned timeout = 100; /* milliseconds */
+ time_t total_seconds = 0;
time_t elapsed_seconds = 0;
time_t last_seconds = 0;
time_t current_seconds = 0;
time_t timeout_seconds = 0;
+ char *pEnv = NULL;
+#if defined(BACDL_BIP) && BBMD_ENABLED
+ long bbmd_port = 0xBAC0;
+ long bbmd_address = 0;
+ long bbmd_timetolive_seconds = 60000;
+#endif
if (argc < 2) {
printf
@@ -189,8 +196,47 @@ int main(
/* setup my info */
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
Init_Service_Handlers();
- if (!datalink_init(getenv("BACNET_IFACE")))
+#if defined(BACDL_BIP)
+ pEnv = getenv("BACNET_IP_PORT");
+ if (pEnv) {
+ bip_set_port(strtol(pEnv, NULL, 0));
+ } else {
+ bip_set_port(0xBAC0);
+ }
+#endif
+ if (!datalink_init(getenv("BACNET_IFACE"))) {
return 1;
+ }
+#if defined(BACDL_BIP) && BBMD_ENABLED
+ pEnv = getenv("BACNET_BBMD_PORT");
+ if (pEnv) {
+ bbmd_port = strtol(pEnv, NULL, 0);
+ if (bbmd_port > 0xFFFF) {
+ bbmd_port = 0xBAC0;
+ }
+ }
+ pEnv = getenv("BACNET_BBMD_TIMETOLIVE");
+ if (pEnv) {
+ bbmd_timetolive_seconds = strtol(pEnv, NULL, 0);
+ if (bbmd_timetolive_seconds > 0xFFFF) {
+ bbmd_timetolive_seconds = 0xFFFF;
+ }
+ }
+ pEnv = getenv("BACNET_BBMD_ADDRESS");
+ if (pEnv) {
+ bbmd_address = bip_getaddrbyname(pEnv);
+ if (bbmd_address) {
+ struct in_addr addr;
+ addr.s_addr = bbmd_address;
+ printf("WhoIs: Registering with BBMD at %s:%ld\n",
+ inet_ntoa(addr),bbmd_port);
+ bvlc_register_with_bbmd(
+ bbmd_address,
+ bbmd_port,
+ bbmd_timetolive_seconds);
+ }
+ }
+#endif
/* configure the timeout values */
last_seconds = time(NULL);
timeout_seconds = Device_APDU_Timeout() / 1000;
@@ -209,8 +255,12 @@ int main(
if (Error_Detected)
break;
/* increment timer - exit if timed out */
- elapsed_seconds += (current_seconds - last_seconds);
- if (elapsed_seconds > timeout_seconds)
+ elapsed_seconds = current_seconds - last_seconds;
+ if (elapsed_seconds) {
+ bvlc_maintenance_timer(elapsed_seconds);
+ }
+ total_seconds += elapsed_seconds;
+ if (total_seconds > timeout_seconds)
break;
/* keep track of time for next check */
last_seconds = current_seconds;
diff --git a/bacnet-stack/include/bip.h b/bacnet-stack/include/bip.h
index 4700353b..3b1e1247 100644
--- a/bacnet-stack/include/bip.h
+++ b/bacnet-stack/include/bip.h
@@ -110,6 +110,14 @@ extern "C" {
uint32_t bip_get_broadcast_addr(
void);
+ /* 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);
+
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/bacnet-stack/include/bvlc.h b/bacnet-stack/include/bvlc.h
index c2a81d00..4811efdc 100644
--- a/bacnet-stack/include/bvlc.h
+++ b/bacnet-stack/include/bvlc.h
@@ -45,6 +45,18 @@ extern "C" {
#endif /* __cplusplus */
+#if BBMD_ENABLED
+ void bvlc_maintenance_timer(
+ unsigned seconds);
+#else
+#define bvlc_maintenance_timer(x)
+#endif
+ /* registers with a bbmd as a foreign device */
+ void bvlc_register_with_bbmd(
+ long bbmd_address, /* in network byte order */
+ uint16_t bbmd_port,
+ uint16_t time_to_live_seconds);
+
uint16_t bvlc_receive(
BACNET_ADDRESS * src, /* returns the source address */
uint8_t * npdu, /* returns the NPDU */
diff --git a/bacnet-stack/include/datalink.h b/bacnet-stack/include/datalink.h
index 7bb1855f..7a5c4716 100644
--- a/bacnet-stack/include/datalink.h
+++ b/bacnet-stack/include/datalink.h
@@ -68,9 +68,7 @@
#elif defined(BACDL_BIP)
#include "bip.h"
-#ifdef BBMD_ENABLED
#include "bvlc.h"
-#endif
#define datalink_init bip_init
#ifdef BBMD_ENABLED
diff --git a/bacnet-stack/ports/linux/bip-init.c b/bacnet-stack/ports/linux/bip-init.c
index 8ced946a..e4e60786 100644
--- a/bacnet-stack/ports/linux/bip-init.c
+++ b/bacnet-stack/ports/linux/bip-init.c
@@ -40,6 +40,21 @@
bool BIP_Debug = false;
+/* 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;
+}
+
static int get_local_ifr_ioctl(
char *ifname,
struct ifreq *ifr,
diff --git a/bacnet-stack/ports/win32/bip-init.c b/bacnet-stack/ports/win32/bip-init.c
index 32681412..99bb6f8b 100644
--- a/bacnet-stack/ports/win32/bip-init.c
+++ b/bacnet-stack/ports/win32/bip-init.c
@@ -41,6 +41,22 @@
#include "net.h"
bool BIP_Debug = false;
+
+/* 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;
+}
+
/* To fill a need, we invent the gethostaddr() function. */
static long gethostaddr(
void)
diff --git a/bacnet-stack/src/bip.c b/bacnet-stack/src/bip.c
index 4aceaf3e..1454ab94 100644
--- a/bacnet-stack/src/bip.c
+++ b/bacnet-stack/src/bip.c
@@ -119,6 +119,24 @@ uint16_t bip_get_port(
return BIP_Port;
}
+static int bip_decode_bip_address(
+ uint8_t * pdu, /* buffer to extract encoded address */
+ struct in_addr *address, /* in host format */
+ uint16_t * port)
+{
+ int len = 0;
+ uint32_t raw_address = 0;
+
+ if (pdu) {
+ (void) decode_unsigned32(&pdu[0], &raw_address);
+ address->s_addr = raw_address;
+ (void) decode_unsigned16(&pdu[4], port);
+ len = 6;
+ }
+
+ return len;
+}
+
/* function to send a packet out the BACnet/IP socket (Annex J) */
/* returns number of bytes sent on success, negative number on failure */
int bip_send_pdu(
@@ -131,6 +149,10 @@ int bip_send_pdu(
uint8_t mtu[MAX_MPDU] = { 0 };
int mtu_len = 0;
int bytes_sent = 0;
+ /* addr and port in host format */
+ struct in_addr address;
+ uint16_t port = 0;
+
(void) npdu_data;
/* assumes that the driver has already been initialized */
@@ -141,22 +163,19 @@ int bip_send_pdu(
bip_dest.sin_family = AF_INET;
if (dest->net == BACNET_BROADCAST_NETWORK) {
/* broadcast */
- bip_dest.sin_addr.s_addr = htonl(BIP_Broadcast_Address.s_addr);
- bip_dest.sin_port = htons(BIP_Port);
- memset(&(bip_dest.sin_zero), '\0', 8);
+ address.s_addr = BIP_Broadcast_Address.s_addr;
+ port = BIP_Port;
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
} else if (dest->mac_len == 6) {
- /* valid unicast */
- (void) decode_unsigned32(&dest->mac[0],
- (uint32_t *) & (bip_dest.sin_addr.s_addr));
- (void) decode_unsigned16(&dest->mac[4], &(bip_dest.sin_port));
- memset(&(bip_dest.sin_zero), '\0', 8);
+ bip_decode_bip_address(&dest->mac[0], &address, &port);
mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
} else {
/* invalid address */
return -1;
}
-
+ bip_dest.sin_addr.s_addr = htonl(address.s_addr);
+ bip_dest.sin_port = htons(port);
+ memset(&(bip_dest.sin_zero), '\0', 8);
mtu_len = 2;
mtu_len +=
encode_unsigned16(&mtu[mtu_len],
@@ -237,11 +256,10 @@ uint16_t bip_receive(
fprintf(stderr,"BIP: src is me. Discarded!\n");
#endif
} else {
- /* copy the source address
- FIXME: IPv6? */
+ /* copy the source address - into host format */
src->mac_len = 6;
- (void) encode_unsigned32(&src->mac[0], sin.sin_addr.s_addr);
- (void) encode_unsigned16(&src->mac[4], sin.sin_port);
+ (void) encode_unsigned32(&src->mac[0], htonl(sin.sin_addr.s_addr));
+ (void) encode_unsigned16(&src->mac[4], htons(sin.sin_port));
/* FIXME: check destination address */
/* see if it is broadcast or for us */
/* decode the length of the PDU - length is inclusive of BVLC */
diff --git a/bacnet-stack/src/bvlc.c b/bacnet-stack/src/bvlc.c
index 47da096b..472b5e86 100644
--- a/bacnet-stack/src/bvlc.c
+++ b/bacnet-stack/src/bvlc.c
@@ -44,7 +44,6 @@
BACnet Broadcast Management Device,
Broadcast Distribution Table, and
Foreign Device Registration */
-
typedef struct {
/* true if valid entry - false if not */
bool valid;
@@ -107,6 +106,12 @@ void bvlc_maintenance_timer(
}
}
+/* Addressing within B/IP Networks
+ In the case of B/IP networks, six octets consisting of the four-octet
+ IP address followed by a two-octet UDP port number (both of
+ which shall be transmitted most significant octet first).
+ Note: for local storage, the storage order is host byte order.
+ Note: BACnet unsigned is encoded as most significant octet. */
int bvlc_encode_bip_address(
uint8_t * pdu, /* buffer to store encoding */
struct in_addr *address, /* in host format */
@@ -264,14 +269,15 @@ int bvlc_encode_read_bdt_ack(
return pdu_len;
}
-
int bvlc_encode_forwarded_npdu(
uint8_t * pdu,
- BACNET_ADDRESS * src,
+ struct sockaddr_in *sin, /* the source address */
uint8_t * npdu,
unsigned npdu_length)
{
int len = 0;
+ struct in_addr address;
+ uint16_t port;
unsigned i; /* for loop counter */
@@ -283,10 +289,11 @@ int bvlc_encode_forwarded_npdu(
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4 + 6 + npdu_length);
len = 4;
- for (i = 0; i < 6; i++) {
- pdu[len] = src->adr[i];
- len++;
- }
+ address.s_addr = ntohl(sin->sin_addr.s_addr);
+ port = ntohs(sin->sin_port);
+ len += bvlc_encode_bip_address(&pdu[len],
+ &address,
+ port);
for (i = 0; i < npdu_length; i++) {
pdu[len] = npdu[i];
len++;
@@ -390,7 +397,6 @@ int bvlc_encode_read_fdt_ack(
return pdu_len;
}
-
int bvlc_encode_delete_fdt_entry(
uint8_t * pdu,
struct in_addr *address,
@@ -462,7 +468,6 @@ int bvlc_encode_original_unicast_npdu(
return len;
}
-
int bvlc_encode_original_broadcast_npdu(
uint8_t * pdu,
uint8_t * npdu,
@@ -640,14 +645,12 @@ void bvlc_bdt_forward_npdu(
int bytes_sent = 0;
unsigned i = 0; /* loop counter */
struct sockaddr_in bip_dest;
- BACNET_ADDRESS src;
/* assumes that the driver has already been initialized */
if (bip_socket() < 0) {
return;
}
- bvlc_internet_to_bacnet_address(&src, sin);
- mtu_len = bvlc_encode_forwarded_npdu(&mtu[0], &src, npdu, npdu_length);
+ mtu_len = bvlc_encode_forwarded_npdu(&mtu[0], sin, npdu, npdu_length);
/* load destination IP address */
bip_dest.sin_family = AF_INET;
/* loop through the BDT and send one to each entry, except us */
@@ -699,14 +702,12 @@ void bvlc_fdt_forward_npdu(
int bytes_sent = 0;
unsigned i = 0; /* loop counter */
struct sockaddr_in bvlc_dest;
- BACNET_ADDRESS src;
/* assumes that the driver has already been initialized */
if (bip_socket() < 0) {
return;
}
- bvlc_internet_to_bacnet_address(&src, sin);
- mtu_len = bvlc_encode_forwarded_npdu(&mtu[0], &src, npdu, max_npdu);
+ mtu_len = bvlc_encode_forwarded_npdu(&mtu[0], sin, npdu, max_npdu);
/* load destination IP address */
bvlc_dest.sin_family = AF_INET;
/* loop through the FDT and send one to each entry */
@@ -748,6 +749,23 @@ void bvlc_send_mpdu(
return;
}
+void bvlc_register_with_bbmd(
+ long bbmd_address, /* in network byte order */
+ uint16_t bbmd_port, /* in host byte order */
+ uint16_t time_to_live_seconds)
+{
+ uint8_t mtu[MAX_MPDU] = { 0 };
+ int mtu_len = 0;
+ struct sockaddr_in dest;
+
+ dest.sin_addr.s_addr = bbmd_address;
+ dest.sin_port = htons(bbmd_port);
+ mtu_len = bvlc_encode_register_foreign_device(
+ &mtu[0],
+ time_to_live_seconds);
+ bvlc_send_mpdu(&dest, &mtu[0], mtu_len);
+}
+
void bvlc_send_result(
struct sockaddr_in *dest, /* the destination address */
BACNET_BVLC_RESULT result_code)
@@ -789,12 +807,12 @@ int bvlc_send_fdt(
return mtu_len;
}
-bool bvlc_broadcast_address_same(
- struct sockaddr_in * sin)
-{ /* network order address */
+static bool bvlc_address_same(
+ struct sockaddr_in * sin) /* network order address */
+{
bool same = false;
- if ((sin->sin_addr.s_addr == htonl(bip_get_broadcast_addr())) &&
+ if ((sin->sin_addr.s_addr == htonl(bip_get_addr())) &&
(sin->sin_port == htons(bip_get_port()))) {
same = true;
}
@@ -815,6 +833,7 @@ uint16_t bvlc_receive(
int max = 0;
struct timeval select_timeout;
struct sockaddr_in sin = { -1 };
+ struct sockaddr_in original_sin = { -1 };
struct sockaddr_in dest = { -1 };
socklen_t sin_len = sizeof(sin);
int function_type = 0;
@@ -933,26 +952,24 @@ uint16_t bvlc_receive(
BACnet devices may omit the broadcast using the B/IP
broadcast address. The method by which a BBMD determines whether
or not other BACnet devices are present is a local matter. */
- /* if this was received via Broadcast, don't broadcast it */
- /* FIXME: how do I know if I received a unicast or broadcast? */
- npdu_len -= 6; /* FIXME: very ugly */
- if (!bvlc_broadcast_address_same(&sin)) {
+ /* decode the 4 byte original address and 2 byte port */
+ bvlc_decode_bip_address(&npdu[4],
+ &original_sin.sin_addr,
+ &original_sin.sin_port);
+ npdu_len =- 6;
+ /* Broadcast it if this was received via unicast */
+ if (bvlc_address_same(&sin)) {
dest.sin_addr.s_addr = htonl(bip_get_broadcast_addr());
dest.sin_port = htons(bip_get_port());
- bvlc_send_mpdu(&dest, &npdu[4], npdu_len);
+ bvlc_send_mpdu(&dest, &npdu[4+6], npdu_len);
}
- bvlc_fdt_forward_npdu(&sin, &npdu[4], npdu_len);
- /* Extract the "real" source from the BVLC header */
- for (i = 0; i < 6; i++) {
- src->mac[i] = npdu[4 + i];
- }
- src->mac_len = 6;
- src->net = 0;
- src->len = 0;
+ bvlc_fdt_forward_npdu(&sin, &npdu[4+6], npdu_len);
+ /* use the original source from the BVLC header */
+ bvlc_internet_to_bacnet_address(src, &original_sin);
if (npdu_len < max_npdu) {
/* shift the buffer to return a valid PDU */
for (i = 0; i < npdu_len; i++) {
- npdu[i] = npdu[10 + i];
+ npdu[i] = npdu[4+6+i];
}
} else {
/* ignore packets that are too large */