Added more functionality to BVLC (untested and uncompiled), and corrected BIP.

This commit is contained in:
skarg
2007-10-16 01:24:22 +00:00
parent ae6d2e882b
commit b3896ea4f4
2 changed files with 383 additions and 40 deletions
+3 -2
View File
@@ -215,9 +215,10 @@ uint16_t bip_receive(BACNET_ADDRESS * src, /* source address */
if ((pdu[1] == BVLC_ORIGINAL_UNICAST_NPDU) ||
(pdu[1] == BVLC_ORIGINAL_BROADCAST_NPDU)) {
/* ignore messages from me */
if (sin.sin_addr.s_addr == htonl(BIP_Address.s_addr))
if ((sin.sin_addr.s_addr == htonl(BIP_Address.s_addr)) &&
(sin.sin_port == htonl(BIP_Port))) {
pdu_len = 0;
else {
} else {
/* copy the source address
FIXME: IPv6? */
src->mac_len = 6;
+380 -38
View File
@@ -85,6 +85,9 @@ typedef struct
#define MAX_FD_ENTRIES 128
static FD_TABLE_ENTRY FD_Table[MAX_FD_ENTRIES];
/* result from a client request */
BACNET_BVLC_RESULT BVLC_Result_Code = BVLC_RESULT_SUCCESSFUL_COMPLETION;
void bvlc_maintenance_timer(unsigned seconds)
{
unsigned i = 0;
@@ -225,6 +228,42 @@ int bvlc_encode_read_bdt_ack_init(
return len;
}
int bvlc_encode_read_bdt_ack(
uint8_t * pdu,
uint16_t max_pdu)
{
int pdu_len = 0; /* return value */
int len = 0;
unsigned count = 0;
unsigned i;
for (i = 0; i < MAX_BBMD_ENTRIES; i++) {
if (BBMD_Table[i].valid) {
count++;
}
}
len = bvlc_encode_read_bdt_ack_init(&pdu[0],count);
pdu_len += len;
for (i = 0; i < MAX_BBMD_ENTRIES; i++) {
if (BBMD_Table[i].valid) {
/* too much to send */
if ((pdu_len+10) > max_pdu) {
pdu_len = 0;
break;
}
len = bvlc_encode_address_entry(
&pdu[pdu_len],
&BBMD_Table[i].dest_address,
BBMD_Table[i].dest_port,
&BBMD_Table[i].broadcast_mask);
pdu_len += len;
}
}
return pdu_len;
}
int bvlc_encode_forwarded_npdu(uint8_t * pdu,
BACNET_ADDRESS * src,
uint8_t * npdu,
@@ -255,7 +294,6 @@ int bvlc_encode_forwarded_npdu(uint8_t * pdu,
return len;
}
int bvlc_encode_register_foreign_device(uint8_t * pdu,
uint16_t time_to_live_seconds)
{
@@ -268,7 +306,7 @@ int bvlc_encode_register_foreign_device(uint8_t * pdu,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 6);
encode_unsigned16(&pdu[2], time_to_live_seconds);
encode_unsigned16(&pdu[4], time_to_live_seconds);
len = 6;
}
@@ -312,6 +350,45 @@ int bvlc_encode_read_fdt_ack_init(
return len;
}
int bvlc_encode_read_fdt_ack(
uint8_t * pdu,
uint16_t max_pdu)
{
int pdu_len = 0; /* return value */
int len = 0;
unsigned count = 0;
unsigned i;
for (i = 0; i < MAX_FD_ENTRIES; i++) {
if (FD_Table[i].valid) {
count++;
}
}
len = bvlc_encode_read_fdt_ack_init(&pdu[0],count);
pdu_len += len;
for (i = 0; i < MAX_FD_ENTRIES; i++) {
if (FD_Table[i].valid) {
/* too much to send */
if ((pdu_len+10) > max_pdu) {
pdu_len = 0;
break;
}
len = bvlc_encode_bip_address(
&pdu[pdu_len],
&FD_Table[i].dest_address,
FD_Table[i].dest_port);
pdu_len += len;
encode_unsigned16(&pdu[pdu_len], FD_Table[i].time_to_live);
pdu_len += len;
encode_unsigned16(&pdu[pdu_len], FD_Table[i].seconds_remaining);
pdu_len += len;
}
}
return pdu_len;
}
int bvlc_encode_delete_fdt_entry(uint8_t * pdu,
struct in_addr *address,
uint16_t port)
@@ -326,8 +403,8 @@ int bvlc_encode_delete_fdt_entry(uint8_t * pdu,
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 10);
/* FDT Entry */
encode_unsigned32(&pdu[0], address->s_addr);
encode_unsigned16(&pdu[4], port);
encode_unsigned32(&pdu[4], address->s_addr);
encode_unsigned16(&pdu[8], port);
len = 10;
}
@@ -450,6 +527,95 @@ void bvlc_bacnet_to_internet_address(
return;
}
bool bvlc_create_bdt(
uint8_t * npdu,
uint16_t npdu_length)
{
bool status = false;
struct in_addr dest_address;
uint16_t dest_port;
unsigned i = 0;
for (i = 0; i < MAX_BBMD_ENTRIES; i++) {
if (npdu_length >= 10) {
BBMD_Table[i].valid = true;
decode_unsigned32(&pdu[0], &dest_address.s_addr);
BBMD_Table[i].dest_address.s_addr = ntohl(dest_address.s_addr);
decode_unsigned16(&pdu[4], &dest_port);
BBMD_Table[i].dest_port = ntohs(dest_port);
decode_unsigned32(&pdu[6], &dest_address.s_addr);
BBMD_Table[i].broadcast_mask.s_addr = ntohl(dest_address.s_addr);
npdu_length -= 10;
} else {
BBMD_Table[i].valid = false;
BBMD_Table[i].dest_address.s_addr = 0;
BBMD_Table[i].dest_port = 0;
BBMD_Table[i].broadcast_mask = 0;
}
}
/* did they all fit? */
if (npdu_length < 10) {
status = true;
}
}
bool bvlc_register_foreign_device(
struct sockaddr_in *sin, /* the source address */
uint16_t time_to_live) /* time in seconds */
{
unsigned i = 0;
bool status = false;
/* am I here already? If so, update my time to live... */
for (i = 0; i < MAX_FD_ENTRIES; i++) {
if (FD_Table[i].valid) {
if ((FD_Table[i].dest_address.s_addr == ntohl(sin->sin_addr.s_addr)) &&
(FD_Table[i].dest_port == ntohs(sin->sin_port))) {
status = true;
FD_Table[i].time_to_live = time_to_live;
FD_Table[i].seconds_remaining = time_to_live + 30;
break;
}
}
}
if (!status) {
for (i = 0; i < MAX_FD_ENTRIES; i++) {
if (!FD_Table[i].valid) {
FD_Table[i].dest_address.s_addr = ntohl(sin->sin_addr.s_addr);
FD_Table[i].dest_port = ntohs(sin->sin_port);
FD_Table[i].time_to_live = time_to_live;
FD_Table[i].seconds_remaining = time_to_live + 30;
FD_Table[i].valid = true;
status = true;
break;
}
}
}
return status;
}
bool bvlc_delete_foreign_device(uint8_t * pdu)
{
struct sockaddr_in sin; /* the ip address */
uint16_t port; /* the decoded port */
bool status = false; /* return value */
bvlc_decode_bip_address(pdu, &sin, &port);
for (i = 0; i < MAX_FD_ENTRIES; i++) {
if (FD_Table[i].valid) {
if ((FD_Table[i].dest_address.s_addr == sin.sin_addr.s_addr) &&
(FD_Table[i].dest_port == sin.sin_port)) {
FD_Table[i].valid = false;
FD_Table[i].seconds_remaining = 0;
status = true;
break;
}
}
}
return status;
}
void bvlc_bdt_forward_npdu(
struct sockaddr_in *sin, /* the source address */
uint8_t * npdu, /* the NPDU */
@@ -547,6 +713,85 @@ void bvlc_fdt_forward_npdu(
return;
}
void bvlc_send_mpdu(
struct sockaddr_in *dest, /* the destination address */
uint8_t * mtu, /* the data */
uint16_t mtu_len) /* amount of data to send */
{
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;
}
/* load destination IP address */
bvlc_dest.sin_family = AF_INET;
bvlc_dest.sin_addr.s_addr = dest->sin_addr.s_addr;
bvlc_dest.sin_port = dest->sin_port;
/* Send the packet */
bytes_sent =
sendto(bip_socket(), (char *) mtu, mtu_len, 0,
(struct sockaddr *) &bvlc_dest,
sizeof(struct sockaddr));
return;
}
void bvlc_send_result(
struct sockaddr_in *dest, /* the destination address */
BACNET_BVLC_RESULT result_code)
{
uint8_t mtu[MAX_MPDU] = {0};
int mtu_len = 0;
mtu_len = bvlc_encode_bvlc_result(&mtu[0], result_code);
bvlc_send_mpdu(dest, mtu, mtu_len);
return;
}
int bvlc_send_bdt(struct sockaddr_in *dest)
{
uint8_t mtu[MAX_MPDU] = {0};
int mtu_len = 0;
mtu_len = bvlc_encode_read_bdt_ack(&mtu[0], sizeof(mtu));
if (mtu_len) {
bvlc_send_mpdu(&sin, &mtu[0], mtu_len);
}
return mtu_len;
}
int bvlc_send_fdt(struct sockaddr_in *dest)
{
uint8_t mtu[MAX_MPDU] = {0};
int mtu_len = 0;
mtu_len = bvlc_encode_read_fdt_ack(&mtu[0], sizeof(mtu));
if (mtu_len) {
bvlc_send_mpdu(dest, &mtu[0], mtu_len);
}
return mtu_len;
}
bool bvlc_broadcast_address_same(
struct sockaddr_in *sin) /* network order address */
{
bool same = false;
if ((sin->sin_addr.s_addr == htonl(bip_get_broadcast_addr())) &&
(sin->sin_port == htons(bip_get_port()))) {
same = true;
}
return same;
}
/* returns:
Number of bytes received, or 0 if none or timeout. */
uint16_t bvlc_receive(
@@ -560,10 +805,14 @@ uint16_t bvlc_receive(
int max = 0;
struct timeval select_timeout;
struct sockaddr_in sin = { -1 };
struct sockaddr_in dest = { -1 };
socklen_t sin_len = sizeof(sin);
int function_type = 0;
int received_bytes = 0;
uint16_t result_code = 0;
unsigned i =0;
bool status = false;
uint16_t time_to_live = 0;
/* Make sure the socket is open */
if (bip_socket() < 0) {
@@ -613,58 +862,151 @@ uint16_t bvlc_receive(
npdu_len -= 4;
switch (function_type) {
case BVLC_RESULT:
/* Upon receipt of a BVLC-Result message containing a result code
of X'0000' indicating the successful completion of the
registration, a foreign device shall start a timer with a value
equal to the Time-to-Live parameter of the preceding Register-
Foreign-Device message. At the expiration of the timer, the
foreign device shall re-register with the BBMD by sending a BVLL
Register-Foreign-Device message */
/* FIXME: clients may need this result */
(void) decode_unsigned16(&npdu[4], &result_code);
BVLC_Result_Code = result_code;
break;
case BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE:
/* Upon receipt of a BVLL Write-Broadcast-Distribution-Table
message, a BBMD shall attempt to create or replace its BDT,
depending on whether or not a BDT has previously existed.
If the creation or replacement of the BDT is successful, the BBMD
shall return a BVLC-Result message to the originating device with
a result code of X'0000'. Otherwise, the BBMD shall return a
BVLC-Result message to the originating device with a result code
of X'0010' indicating that the write attempt has failed. */
message, a BBMD shall attempt to create or replace its BDT,
depending on whether or not a BDT has previously existed.
If the creation or replacement of the BDT is successful, the BBMD
shall return a BVLC-Result message to the originating device with
a result code of X'0000'. Otherwise, the BBMD shall return a
BVLC-Result message to the originating device with a result code
of X'0010' indicating that the write attempt has failed. */
status = bvlc_create_bdt(&npdu[4],npdu_len);
if (status) {
bvlc_send_result(&sin, BVLC_RESULT_SUCCESSFUL_COMPLETION);
} else {
bvlc_send_result(&sin, BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK);
}
break;
case BVLC_READ_BROADCAST_DISTRIBUTION_TABLE:
/* Upon receipt of a BVLL Read-Broadcast-Distribution-Table
message, a BBMD shall load the contents of its BDT into a BVLL
Read-Broadcast-Distribution-Table-Ack message and send it to the
originating device. If the BBMD is unable to perform the
read of its BDT, it shall return a BVLC-Result message to the
originating device with a result code of X'0020' indicating that
the read attempt has failed.*/
if (bvlc_send_bdt(&sin) <= 0) {
bvlc_send_result(&sin,
BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK);
}
break;
case BVLC_READ_BROADCAST_DISTRIBUTION_TABLE_ACK:
/* FIXME: complete the code for client side read */
break;
case BVLC_FORWARDED_NPDU:
/* Upon receipt of a BVLL Forwarded-NPDU message, a BBMD shall
process it according to whether it was received from a peer
BBMD as the result of a directed broadcast or a unicast
transmission. A BBMD may ascertain the method by which Forwarded-
NPDU messages will arrive by inspecting the broadcast distribution
mask field in its own BDT entry since all BDTs are required
to be identical. If the message arrived via directed broadcast,
it was also received by the other devices on the BBMD's subnet. In
this case the BBMD merely retransmits the message directly to each
foreign device currently in the BBMD's FDT. If the
message arrived via a unicast transmission it has not yet been
received by the other devices on the BBMD's subnet. In this case,
the message is sent to the devices on the BBMD's subnet using the
B/IP broadcast address as well as to each foreign device
currently in the BBMD's FDT. A BBMD on a subnet with no other
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. */
bvlc_broadcast_npdu(&sin, &npdu[4], npdu_len);
process it according to whether it was received from a peer
BBMD as the result of a directed broadcast or a unicast
transmission. A BBMD may ascertain the method by which Forwarded-
NPDU messages will arrive by inspecting the broadcast distribution
mask field in its own BDT entry since all BDTs are required
to be identical. If the message arrived via directed broadcast,
it was also received by the other devices on the BBMD's subnet. In
this case the BBMD merely retransmits the message directly to each
foreign device currently in the BBMD's FDT. If the
message arrived via a unicast transmission it has not yet been
received by the other devices on the BBMD's subnet. In this case,
the message is sent to the devices on the BBMD's subnet using the
B/IP broadcast address as well as to each foreign device
currently in the BBMD's FDT. A BBMD on a subnet with no other
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? */
if (!bvlc_broadcast_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_fdt_forward_npdu(&sin, &npdu[4], npdu_len);
break;
case BVLC_REGISTER_FOREIGN_DEVICE:
/* Upon receipt of a BVLL Register-Foreign-Device message, a BBMD
shall start a timer with a value equal to the Time-to-Live
parameter supplied plus a fixed grace period of 30 seconds. If,
within the period during which the timer is active, another BVLL
Register-Foreign-Device message from the same device is received,
the timer shall be reset and restarted. If the time expires
without the receipt of another BVLL Register-Foreign-Device
message from the same foreign device, the FDT entry for this
device shall be cleared.*/
(void) decode_unsigned16(&npdu[4], &time_to_live);
if (bvlc_register_foreign_device(&sin, time_to_live)) {
bvlc_send_result(&sin,
BVLC_RESULT_SUCCESSFUL_COMPLETION);
} else {
bvlc_send_result(&sin,
BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK);
}
break;
case BVLC_READ_FOREIGN_DEVICE_TABLE:
/* Upon receipt of a BVLL Read-Foreign-Device-Table message, a
BBMD shall load the contents of its FDT into a BVLL Read-
Foreign-Device-Table-Ack message and send it to the originating
device. If the BBMD is unable to perform the read of its FDT,
it shall return a BVLC-Result message to the originating device
with a result code of X'0040' indicating that the read attempt has
failed.*/
if (bvlc_send_fdt(&sin) <= 0) {
bvlc_send_result(&sin,
BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK);
}
break;
case BVLC_READ_FOREIGN_DEVICE_TABLE_ACK:
/* FIXME: complete the code for client side read */
break;
case BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY:
/* Upon receipt of a BVLL Delete-Foreign-Device-Table-Entry
message, a BBMD shall search its foreign device table for an entry
corresponding to the B/IP address supplied in the message. If an
entry is found, it shall be deleted and the BBMD shall return a
BVLC-Result message to the originating device with a result code
of X'0000'. Otherwise, the BBMD shall return a BVLCResult
message to the originating device with a result code of X'0050'
indicating that the deletion attempt has failed.*/
if (bvlc_delete_foreign_device(&npdu[4])) {
bvlc_send_result(&sin,
BVLC_RESULT_SUCCESSFUL_COMPLETION);
} else {
bvlc_send_result(&sin,
BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK);
}
break;
case BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK:
/* Upon receipt of a BVLL Distribute-Broadcast-To-Network message
from a foreign device, the receiving BBMD shall transmit a
BVLL Forwarded-NPDU message on its local IP subnet using the
local B/IP broadcast address as the destination address. In
addition, a Forwarded-NPDU message shall be sent to each entry
in its BDT as described above in the case of the receipt of a
BVLL Original-Broadcast-NPDU as well as directly to each foreign
device currently in the BBMD's FDT except the originating
node. If the BBMD is unable to perform the forwarding function,
it shall return a BVLC-Result message to the foreign device
with a result code of X'0060' indicating that the forwarding
attempt was unsuccessful*/
/* FIXME: complete the function code */
bvlc_broadcast_forward_npdu(&sin, &npdu[4], npdu_len);
bvlc_fdt_forward_npdu(&sin, &npdu[4], npdu_len);
break;
case BVLC_ORIGINAL_UNICAST_NPDU:
/* ignore messages from me */
if (sin.sin_addr.s_addr == htonl(bip_get_addr())) {
if ((sin.sin_addr.s_addr == htonl(bip_get_addr())) &&
(sin.sin_port == htons(bip_get_port()))) {
npdu_len = 0;
} else {
bvlc_internet_to_bacnet_address(src, &sin);
@@ -734,19 +1076,19 @@ int bvlc_send_pdu(BACNET_ADDRESS * dest, /* destination address */
return BIP_Socket;
mtu[0] = BVLL_TYPE_BACNET_IP;
bip_dest.sin_family = AF_INET;
bvlc_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);
bvlc_dest.sin_addr.s_addr = htonl(BIP_Broadcast_Address.s_addr);
bvlc_dest.sin_port = htons(BIP_Port);
memset(&(bvlc_dest.sin_zero), '\0', 8);
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
} else if (dest->mac_len == 6) {
/* valid unicast */
(void) decode_unsigned32(&dest->mac[0],
&(bip_dest.sin_addr.s_addr));
(void) decode_unsigned16(&dest->mac[4], &(bip_dest.sin_port));
memset(&(bip_dest.sin_zero), '\0', 8);
&(bvlc_dest.sin_addr.s_addr));
(void) decode_unsigned16(&dest->mac[4], &(bvlc_dest.sin_port));
memset(&(bvlc_dest.sin_zero), '\0', 8);
mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
} else {
/* invalid address */
@@ -762,7 +1104,7 @@ int bvlc_send_pdu(BACNET_ADDRESS * dest, /* destination address */
/* Send the packet */
bytes_sent = sendto(BIP_Socket, (char *) mtu, mtu_len, 0,
(struct sockaddr *) &bip_dest, sizeof(struct sockaddr));
(struct sockaddr *) &bvlc_dest, sizeof(struct sockaddr));
return bytes_sent;
}