Files
bacnet_stack/ports/pico/bip.c
T
2026-04-24 06:40:32 -05:00

394 lines
11 KiB
C

/**************************************************************************
*
* Copyright (C) 2005 Steve Karg
* Copyright (C) 2025 Testimony Adams <adamstestimony@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*
*********************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "bacnet/bacdcode.h"
#include "bacnet/bacint.h"
#include "bacnet/datalink/bvlc.h"
#include "bvlc-pico.h"
#include "bip.h"
#if PRINT_ENABLED || DEBUG
#include <stdio.h>
#endif
/** @file bip.c Configuration and Operations for BACnet/IP */
#define MAX_SOCK_NUM 8
static uint8_t BIP_Socket = MAX_SOCK_NUM;
/* port to use - stored in network byte order */
static uint16_t BIP_Port = 0;
/* broadcast destination port to use */
static uint16_t BIP_Broadcast_Port = 0;
/* IP Address - stored in network byte order */
static uint8_t BIP_Address[4] = { 0, 0, 0, 0 };
/* Broadcast Address - stored in network byte order */
static uint8_t BIP_Broadcast_Address[4] = { 0, 0, 0, 0 };
/** Converter from uint8_t[4] type address to uint32_t
*
*/
uint32_t convertBIP_Address2uint32(const uint8_t *bip_address)
{
return ((uint32_t)bip_address[0] << 24) | ((uint32_t)bip_address[1] << 16) |
((uint32_t)bip_address[2] << 8) | ((uint32_t)bip_address[3]);
}
/** Convert from uint32_t IPv4 address to uint8_t[4] address
*
*/
void convertUint32Address_2_uint8Address(uint32_t ip, uint8_t *address)
{
address[0] = (uint8_t)(ip >> 24);
address[1] = (uint8_t)(ip >> 16);
address[2] = (uint8_t)(ip >> 8);
address[3] = (uint8_t)(ip >> 0);
}
/** Setter for the BACnet/IP socket handle.
*
* @param sock_fd [in] Handle for the BACnet/IP socket.
*/
void bip_set_socket(uint8_t sock_fd)
{
BIP_Socket = sock_fd;
}
/** Getter for the BACnet/IP socket handle.
*
* @return The handle to the BACnet/IP socket.
*/
uint8_t bip_socket(void)
{
return BIP_Socket;
}
bool bip_valid(void)
{
return (BIP_Socket < MAX_SOCK_NUM);
}
void bip_set_addr(const uint8_t *net_address)
{
uint8_t i;
for (i = 0; i < 4; i++) {
BIP_Address[i] = net_address[i];
}
}
/* returns network byte order */
uint8_t *bip_get_addr(void)
{
return BIP_Address;
}
void bip_set_broadcast_addr(const uint8_t *net_address)
{
uint8_t i;
for (i = 0; i < 4; i++) {
BIP_Broadcast_Address[i] = net_address[i];
}
}
uint8_t *bip_get_broadcast_addr(void)
{
return BIP_Broadcast_Address;
}
void bip_set_port(uint16_t port)
{
BIP_Port = port;
}
void bip_set_broadcast_port(uint16_t port)
{
BIP_Broadcast_Port = port;
}
/* returns network byte order */
uint16_t bip_get_port(void)
{
return BIP_Port;
}
uint16_t bip_get_broadcast_port(void)
{
if (BIP_Broadcast_Port) {
return BIP_Broadcast_Port;
}
return BIP_Port;
}
static int bip_decode_bip_address(
const BACNET_ADDRESS *bac_addr, uint8_t *address, uint16_t *port)
{
int len = 0;
if (bac_addr) {
memcpy(address, &bac_addr->mac[0], 4);
memcpy(port, &bac_addr->mac[4], 2);
len = 6;
}
return len;
}
/** Function to send a packet out the BACnet/IP socket (Annex J).
* @ingroup DLBIP
*
* @param dest [in] Destination address (may encode an IP address and port #).
* @param npdu_data [in] The NPDU header (Network) information (not used).
* @param pdu [in] Buffer of data to be sent - may be null (why?).
* @param pdu_len [in] Number of bytes in the pdu buffer.
* @return Number of bytes sent on success, negative number on failure.
*/
int bip_send_pdu(
BACNET_ADDRESS *dest,
BACNET_NPDU_DATA *npdu_data,
uint8_t *pdu,
unsigned pdu_len)
{
uint8_t mtu[BIP_MPDU_MAX] = { 0 };
int mtu_len = 0;
int bytes_sent = 0;
uint8_t address[] = { 0, 0, 0, 0 };
uint16_t port = 0;
uint8_t i;
(void)npdu_data;
/* assumes that the driver has already been initialized */
if (BIP_Socket >= MAX_SOCK_NUM) {
return -1;
}
mtu[0] = BVLL_TYPE_BACNET_IP;
if ((dest->net == BACNET_BROADCAST_NETWORK) ||
((dest->net > 0) && (dest->len == 0)) || (dest->mac_len == 0)) {
/* broadcast */
for (i = 0; i < 4; i++) {
address[i] = BIP_Broadcast_Address[i];
}
port = bip_get_broadcast_port();
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
#ifdef DEBUG
fprintf(
stderr, "Send Broadcast NPDU to %d.%d.%d.%d:%d\n", address[0],
address[1], address[2], address[3], port);
#endif
} else if (dest->mac_len == 6) {
bip_decode_bip_address(dest, address, &port);
mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
#ifdef DEBUG
fprintf(
stderr, "Send Unicast NPDU to %d.%d.%d.%d:%d\n", address[0],
address[1], address[2], address[3], port);
#endif
} else {
/* invalid address */
return -1;
}
mtu_len = 2;
mtu_len += encode_unsigned16(
&mtu[mtu_len], (uint16_t)(pdu_len + 4 /*inclusive */));
memcpy(&mtu[mtu_len], pdu, pdu_len);
mtu_len += pdu_len;
#ifdef DEBUG
fprintf(stderr, "MTU size %d\n", mtu_len);
#endif
/* Send the packet using platform-specific function */
bytes_sent = bip_socket_send(address, port, mtu, mtu_len);
return bytes_sent;
}
/** Implementation of the receive() function for BACnet/IP; receives one
* packet, verifies its BVLC header, and removes the BVLC header from
* the PDU data before returning.
*
* @param src [out] Source of the packet - who should receive any response.
* @param pdu [out] A buffer to hold the PDU portion of the received packet,
* after the BVLC portion has been stripped off.
* @param max_pdu [in] Size of the pdu[] buffer.
* @param timeout [in] The number of milliseconds to wait for a packet.
* @return The number of octets (remaining) in the PDU, or zero on failure.
*/
uint16_t bip_receive(
BACNET_ADDRESS *src, uint8_t *pdu, uint16_t max_pdu, unsigned timeout)
{
int received_bytes = 0;
int max = 0;
uint16_t pdu_len = 0;
uint8_t src_addr[] = { 0, 0, 0, 0 };
uint16_t src_port = 0;
uint16_t i = 0;
int function = 0;
(void)timeout; /* unused parameter */
/* Make sure the socket is open */
if (BIP_Socket >= MAX_SOCK_NUM) {
return 0;
}
/* Receive packet using platform-specific function */
received_bytes = bip_socket_receive(pdu, max_pdu, src_addr, &src_port);
/* No data available */
if (received_bytes <= 0) {
return 0;
}
/* the signature of a BACnet/IP packet */
if (pdu[0] != BVLL_TYPE_BACNET_IP) {
return 0;
}
/* Erase up to 16 bytes after the received bytes as safety margin */
max = (int)max_pdu - received_bytes;
if (max > 0) {
if (max > 16) {
max = 16;
}
memset(&pdu[received_bytes], 0, max);
}
if (bvlc_for_non_bbmd(src_addr, &src_port, pdu, received_bytes) > 0) {
/* Handled, usually with a NACK. */
#if PRINT_ENABLED
fprintf(stderr, "BIP: BVLC discarded!\n");
#endif
return 0;
}
function = pico_bvlc_get_function_code();
if ((function == BVLC_ORIGINAL_UNICAST_NPDU) ||
(function == BVLC_ORIGINAL_BROADCAST_NPDU)) {
/* ignore messages from me */
if ((convertBIP_Address2uint32(src_addr) ==
convertBIP_Address2uint32(BIP_Address)) &&
(src_port == BIP_Port)) {
pdu_len = 0;
} else {
/* data in src->mac[] is in network format */
src->mac_len = 6;
memcpy(&src->mac[0], &src_addr, 4);
memcpy(&src->mac[4], &src_port, 2);
#ifdef DEBUG
fprintf(
stderr, "BIP receive from %d.%d.%d.%d\n", src->mac[0],
src->mac[1], src->mac[2], src->mac[3]);
#endif
/* decode the length of the PDU - length is inclusive of BVLC */
(void)decode_unsigned16(&pdu[2], &pdu_len);
/* subtract off the BVLC header */
pdu_len -= 4;
if (pdu_len < max_pdu) {
/* shift the buffer to return a valid PDU */
for (i = 0; i < pdu_len; i++) {
pdu[i] = pdu[4 + i];
}
} else {
/* ignore packets that are too large */
pdu_len = 0;
#if PRINT_ENABLED
fprintf(stderr, "BIP: PDU too large. Discarded!.\n");
#endif
}
}
} else if (function == BVLC_FORWARDED_NPDU) {
memcpy(&src_addr, &pdu[4], 4);
memcpy(&src_port, &pdu[8], 2);
if ((convertBIP_Address2uint32(src_addr) ==
convertBIP_Address2uint32(BIP_Address)) &&
(src_port == BIP_Port)) {
/* ignore messages from me */
pdu_len = 0;
} else {
/* data in src->mac[] is in network format */
src->mac_len = 6;
memcpy(&src->mac[0], &src_addr, 4);
memcpy(&src->mac[4], &src_port, 2);
/* decode the length of the PDU - length is inclusive of BVLC */
(void)decode_unsigned16(&pdu[2], &pdu_len);
/* subtract off the BVLC header */
pdu_len -= 10;
if (pdu_len < max_pdu) {
/* shift the buffer to return a valid PDU */
for (i = 0; i < pdu_len; i++) {
pdu[i] = pdu[4 + 6 + i];
}
} else {
/* ignore packets that are too large */
pdu_len = 0;
}
}
}
return pdu_len;
}
void bip_get_my_address(BACNET_ADDRESS *my_address)
{
int i = 0;
if (my_address) {
my_address->mac_len = 6;
memcpy(&my_address->mac[0], &BIP_Address, 4);
memcpy(&my_address->mac[4], &BIP_Port, 2);
my_address->net = 0; /* local only, no routing */
my_address->len = 0; /* no SLEN */
for (i = 0; i < MAX_MAC_LEN; i++) {
/* no SADR */
my_address->adr[i] = 0;
}
}
return;
}
void bip_get_broadcast_address(BACNET_ADDRESS *dest)
{
int i = 0;
if (dest) {
dest->mac_len = 6;
memcpy(&dest->mac[0], &BIP_Broadcast_Address, 4);
{
uint16_t port = bip_get_broadcast_port();
memcpy(&dest->mac[4], &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;
}
int bip_send_mpdu(
const BACNET_IP_ADDRESS *dest, const uint8_t *mtu, uint16_t mtu_len)
{
int bytes_sent = 0;
/* Send using platform-specific function */
bytes_sent = bip_socket_send(dest->address, dest->port, mtu, mtu_len);
return bytes_sent;
}