/************************************************************************** * * Copyright (C) 2005 Steve Karg * Copyright (C) 2025 Testimony Adams * * SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 * *********************************************************************/ #include #include #include #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 #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; }