Adding BVLC handling.
This commit is contained in:
+314
-37
@@ -43,6 +43,106 @@
|
||||
Broadcast Distribution Table, and
|
||||
Foreign Device Registration */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* IP Address - stored in host byte order */
|
||||
struct in_addr address;
|
||||
uint16_t port;
|
||||
} BIP_ADDRESS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* true if valid entry - false if not */
|
||||
bool valid;
|
||||
/* BACnet/IP address */
|
||||
BIP_ADDRESS bip_address;
|
||||
/* Broadcast Distribution Mask - stored in host byte order */
|
||||
struct in_addr broadcast_mask;
|
||||
} BBMD_TABLE_ENTRY;
|
||||
|
||||
#define MAX_BBMD_ENTRIES 128
|
||||
static BBMD_TABLE_ENTRY BBMD_Table[MAX_BBMD_ENTRIES];
|
||||
|
||||
/*Each device that registers as a foreign device shall be placed
|
||||
in an entry in the BBMD's Foreign Device Table (FDT). Each
|
||||
entry shall consist of the 6-octet B/IP address of the registrant;
|
||||
the 2-octet Time-to-Live value supplied at the time of
|
||||
registration; and a 2-octet value representing the number of
|
||||
seconds remaining before the BBMD will purge the registrant's FDT
|
||||
entry if no re-registration occurs. This value will be initialized
|
||||
to the 2-octet Time-to-Live value supplied at the time of
|
||||
registration.*/
|
||||
typedef struct
|
||||
{
|
||||
bool valid;
|
||||
/* BACnet/IP address */
|
||||
BIP_ADDRESS bip_address;
|
||||
/* seconds for valid entry lifetime */
|
||||
uint16_t time_to_live;
|
||||
time_t seconds_remaining;
|
||||
} FD_TABLE_ENTRY;
|
||||
|
||||
#define MAX_FD_ENTRIES 128
|
||||
static FOREIGN_DEVICE_TABLE_ENTRY FD_Table[MAX_FD_ENTRIES];
|
||||
|
||||
void bvlc_maintenance_timer(unsigned seconds)
|
||||
{
|
||||
unsigned i = 0;
|
||||
|
||||
for (i = 0; i < MAX_FD_ENTRIES; i++) {
|
||||
if (FD_Table[i].valid)
|
||||
{
|
||||
if (FD_Table[i].time_to_live
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static FOREIGN_DEVICE_TABLE_ENTRY FD_Table[MAX_FD_ENTRIES];
|
||||
|
||||
|
||||
}
|
||||
|
||||
int bvlc_encode_bip_address(
|
||||
uint8_t *pdu,
|
||||
struct in_addr *address, /* in host format */
|
||||
uint16_t port)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
if (pdu) {
|
||||
len = encode_unsigned32(&pdu[0], address->s_addr);
|
||||
len += encode_unsigned16(&pdu[len], port);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int bvlc_decode_bip_address(
|
||||
uint8_t *pdu,
|
||||
struct in_addr *address, /* in host format */
|
||||
uint16_t *port)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* used for both read and write entries */
|
||||
int bvlc_encode_address_entry(
|
||||
uint8_t *pdu,
|
||||
struct in_addr *address,
|
||||
uint16_t port,
|
||||
struct in_addr *mask)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
if (pdu) {
|
||||
len = bvlc_encode_bip_address(pdu, address, port);
|
||||
len += encode_unsigned32(&pdu[len], mask->s_addr);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int bvlc_encode_bvlc_result(
|
||||
uint8_t *pdu,
|
||||
BACNET_BVLC_RESULT result_code)
|
||||
@@ -79,38 +179,6 @@ int bvlc_encode_write_bdt_init(
|
||||
return len;
|
||||
}
|
||||
|
||||
int bvlc_encode_address(
|
||||
uint8_t *pdu,
|
||||
struct in_addr *address, /* in host format */
|
||||
uint16_t port)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
if (pdu) {
|
||||
len = encode_unsigned32(&pdu[0], address->s_addr);
|
||||
len += encode_unsigned16(&pdu[len], port);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* used for both read and write entries */
|
||||
int bvlc_encode_address_entry(
|
||||
uint8_t *pdu,
|
||||
struct in_addr *address,
|
||||
uint16_t port,
|
||||
struct in_addr *mask)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
if (pdu) {
|
||||
len = bvlc_encode_address(pdu, address, port);
|
||||
len += encode_unsigned32(&pdu[len], mask->s_addr);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int bvlc_encode_read_bdt(
|
||||
uint8_t *pdu)
|
||||
{
|
||||
@@ -268,7 +336,7 @@ int bvlc_encode_distribute_broadcast_to_network(
|
||||
|
||||
if (pdu) {
|
||||
pdu[0] = BVLL_TYPE_BACNET_IP;
|
||||
pdu[1] = BVLC_FORWARDED_NPDU;
|
||||
pdu[1] = BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK;
|
||||
/* The 2-octet BVLC Length field is the length, in octets,
|
||||
of the entire BVLL message, including the two octets of the
|
||||
length field itself, most significant octet first. */
|
||||
@@ -330,24 +398,192 @@ int bvlc_encode_original_broadcast_npdu(
|
||||
return len;
|
||||
}
|
||||
|
||||
void bvlc_handler(uint8_t *buf, int len, struct sockaddr_in *sin)
|
||||
/* copy the source internet address to the BACnet address */
|
||||
/* FIXME: IPv6? */
|
||||
/* FIXME: is sockaddr_in host or network order? */
|
||||
void bvlc_internet_to_bacnet_address
|
||||
BACNET_ADDRESS * src, /* returns the BACnet source address */
|
||||
struct sockaddr_in * sin) /* source internet address */
|
||||
{
|
||||
int function_type = 0;
|
||||
int len = 0;
|
||||
|
||||
if (src && sin)
|
||||
{
|
||||
len = encode_unsigned32(&src->mac[0], sin->sin_addr.s_addr);
|
||||
len += encode_unsigned16(&src->mac[4], sin->sin_port);
|
||||
src->mac_len = len;
|
||||
src->net = 0;
|
||||
src->len = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* copy the source internet address to the BACnet address */
|
||||
/* FIXME: IPv6? */
|
||||
/* FIXME: is sockaddr_in host or network order? */
|
||||
void bvlc_bacnet_to_internet_address
|
||||
struct sockaddr_in * sin, /* source internet address */
|
||||
BACNET_ADDRESS * src) /* returns the BACnet source address */
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
if (src && sin)
|
||||
{
|
||||
if (src->mac_len == 6)
|
||||
{
|
||||
len = decode_unsigned32(&src->mac[0], &sin->sin_addr.s_addr);
|
||||
len += decode_unsigned16(&src->mac[4], &sin->sin_port);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void bvlc_bdt_forward_npdu(
|
||||
struct sockaddr_in *sin, /* the source address */
|
||||
uint8_t * npdu, /* the NPDU */
|
||||
uint16_t npdu_length) /* length of the NPDU */
|
||||
{
|
||||
uint8_t mtu[MAX_MPDU] = { 0 };
|
||||
int mtu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
unsigned i = 0; /* loop counter */
|
||||
|
||||
/* assumes that the driver has already been initialized */
|
||||
if (BIP_Socket < 0)
|
||||
return BIP_Socket;
|
||||
|
||||
mtu_len = bvlc_encode_forwarded_npdu(
|
||||
&mtu[0],
|
||||
src,
|
||||
npdu,
|
||||
npdu_length);
|
||||
|
||||
/* loop through the BDT and send one to each entry, except us */
|
||||
for (i = 0; i < MAX_BBMD_ENTRIES; i++)
|
||||
{
|
||||
if (BBMD_Table[i].valid)
|
||||
{
|
||||
/* The B/IP address to which the Forwarded-NPDU message is
|
||||
sent is formed by inverting the broadcast distribution
|
||||
mask in the BDT entry and logically ORing it with the
|
||||
BBMD address of the same entry. */
|
||||
|
||||
/* Send the packet */
|
||||
bytes_sent = sendto(BIP_Socket, (char *) mtu, mtu_len, 0,
|
||||
(struct sockaddr *) bip_dest, sizeof(struct sockaddr));
|
||||
}
|
||||
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
|
||||
void bvlc_fdt_forward_npdu(
|
||||
struct sockaddr_in *sin, /* the source address */
|
||||
uint8_t * npdu, /* returns the NPDU */
|
||||
uint16_t max_npdu) /* amount of space available in the NPDU */
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t bvlc_handler(
|
||||
BACNET_ADDRESS * src, /* returns the source address */
|
||||
uint8_t * npdu, /* returns the NPDU */
|
||||
uint16_t max_npdu, /* amount of space available in the NPDU */
|
||||
unsigned timeout) /* number of milliseconds to wait for a packet */
|
||||
{
|
||||
int received_bytes;
|
||||
uint8_t buf[MAX_MPDU] = { 0 }; /* data */
|
||||
uint16_t pdu_len = 0; /* return value */
|
||||
fd_set read_fds;
|
||||
int max;
|
||||
struct timeval select_timeout;
|
||||
struct sockaddr_in sin = { -1 };
|
||||
socklen_t sin_len = sizeof(sin);
|
||||
int function_type = 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((unsigned int) 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 *) &buf[0], MAX_MPDU, 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/IP packet */
|
||||
if (buf[0] != BVLL_TYPE_BACNET_IP)
|
||||
return;
|
||||
return 0;
|
||||
function_type = buf[1];
|
||||
/* decode the length of the PDU - length is inclusive of BVLC */
|
||||
(void)decode_unsigned16(&buf[2], &npdu_len);
|
||||
/* subtract off the BVLC header */
|
||||
npdu_len -= 4;
|
||||
switch (function_type)
|
||||
{
|
||||
case BVLC_RESULT:
|
||||
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. */
|
||||
break;
|
||||
case BVLC_READ_BROADCAST_DISTRIBUTION_TABLE:
|
||||
break;
|
||||
case BVLC_READ_BROADCAST_DISTRIBUTION_TABLE_ACK:
|
||||
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, &buf[4], npdu_len);
|
||||
bvlc_fdt_forward_npdu(&sin, &buf[4], npdu_len);
|
||||
break;
|
||||
case BVLC_REGISTER_FOREIGN_DEVICE:
|
||||
break;
|
||||
@@ -358,14 +594,55 @@ void bvlc_handler(uint8_t *buf, int len, struct sockaddr_in *sin)
|
||||
case BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY:
|
||||
break;
|
||||
case BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK:
|
||||
bvlc_broadcast_forward_npdu(&sin, &buf[4], npdu_len);
|
||||
bvlc_fdt_forward_npdu(&sin, &buf[4], npdu_len);
|
||||
break;
|
||||
case BVLC_ORIGINAL_UNICAST_NPDU:
|
||||
/* ignore messages from me */
|
||||
if (sin.sin_addr.s_addr == BIP_Address.s_addr)
|
||||
npdu_len = 0;
|
||||
else {
|
||||
bvlc_internet_to_bacnet_address(src,&sin);
|
||||
/* copy the buffer into the PDU */
|
||||
if (npdu_len < max_npdu)
|
||||
memmove(&npdu[0], &buf[4], npdu_len);
|
||||
/* ignore packets that are too large */
|
||||
/* clients should check my max-apdu first */
|
||||
else
|
||||
npdu_len = 0;
|
||||
}
|
||||
break;
|
||||
case BVLC_ORIGINAL_BROADCAST_NPDU:
|
||||
/* Upon receipt of a BVLL Original-Broadcast-NPDU message,
|
||||
a BBMD shall construct a BVLL Forwarded-NPDU message and
|
||||
send it to each IP subnet in its BDT with the exception
|
||||
of its own. The B/IP address to which the Forwarded-NPDU
|
||||
message is sent is formed by inverting the broadcast
|
||||
distribution mask in the BDT entry and logically ORing it
|
||||
with the BBMD address of the same entry. This process
|
||||
produces either the directed broadcast address of the remote
|
||||
subnet or the unicast address of the BBMD on that subnet
|
||||
depending on the contents of the broadcast distribution
|
||||
mask. See J.4.3.2.. In addition, the received BACnet NPDU
|
||||
shall be sent directly to each foreign device currently in
|
||||
the BBMD's FDT also using the BVLL Forwarded-NPDU message. */
|
||||
bvlc_internet_to_bacnet_address(src,&sin);
|
||||
/* copy the buffer into the PDU */
|
||||
if (npdu_len < max_npdu)
|
||||
memmove(&npdu[0], &buf[4], npdu_len);
|
||||
/* ignore packets that are too large */
|
||||
/* clients should check my max-apdu first */
|
||||
else
|
||||
npdu_len = 0;
|
||||
/* if BDT or FDT entries exist, Forward the NPDU */
|
||||
bvlc_bdt_forward_npdu(&sin, &buf[4], npdu_len);
|
||||
bvlc_fdt_forward_npdu(&sin, &buf[4], npdu_len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
return npdu_len;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* called from BACnet/IP handler */
|
||||
void bvlc_handler(uint8_t *buf, int len, struct sockaddr_in *sin);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in New Issue
Block a user