/** * @file * @brief BACnet/IPv6 virtual link control module encode and decode * @author Steve Karg * @date 2015 * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 * @defgroup DLBIP6 BACnet/IPv6 DataLink Network Layer * @ingroup DataLink */ #include /* for standard integer types uint8_t etc. */ #include /* for the standard bool type. */ #include #include "bacnet/bacenum.h" #include "bacnet/bacdcode.h" #include "bacnet/bacint.h" #include "bacnet/datalink/bvlc6.h" #include "bacnet/hostnport.h" /** Encode the BVLC header * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param message_type - BVLL Messages * @param length - number of bytes for this message type * * @return number of bytes encoded */ int bvlc6_encode_header( uint8_t *pdu, uint16_t pdu_size, uint8_t message_type, uint16_t length) { int bytes_encoded = 0; if (pdu && (pdu_size >= 2)) { pdu[0] = BVLL_TYPE_BACNET_IP6; pdu[1] = message_type; /* 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. */ encode_unsigned16(&pdu[2], length); bytes_encoded = 4; } return bytes_encoded; } /** Decode the BVLC Result message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param message_type - BVLL Messages * @param length - number of bytes for this message type * * @return number of bytes decoded */ int bvlc6_decode_header( const uint8_t *pdu, uint16_t pdu_len, uint8_t *message_type, uint16_t *length) { int bytes_consumed = 0; if (pdu && (pdu_len >= 4)) { if (pdu[0] == BVLL_TYPE_BACNET_IP6) { if (message_type) { *message_type = pdu[1]; } if (length) { decode_unsigned16(&pdu[2], length); } bytes_consumed = 4; } } return bytes_consumed; } /** Encode the BVLC Result message * * This message provides a mechanism to acknowledge the result * of those BVLL service requests that require an acknowledgment, * whether successful (ACK) or unsuccessful (NAK). * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param result_code - BVLC result code * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'00' BVLC-Result * BVLC Length: 2-octets X'0009' Length of the BVLL message * Source-Virtual-Address 3-octets * Result Code: 2-octets X'0000' Successful completion * X'0030' Address-Resolution NAK * X'0060' Virtual-Address-Resolution NAK * X'0090' Register-Foreign-Device NAK * X'00A0' Delete-Foreign-Device-Table-Entry * NAK X'00C0' Distribute-Broadcast-To-Network NAK */ int bvlc6_encode_result( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac, uint16_t result_code) { int bytes_encoded = 0; const uint16_t length = 9; if (pdu && (pdu_size >= 9) && (vmac <= 0xFFFFFF)) { bytes_encoded = bvlc6_encode_header(pdu, pdu_size, BVLC6_RESULT, length); if (bytes_encoded == 4) { encode_unsigned24(&pdu[4], vmac); encode_unsigned16(&pdu[7], result_code); bytes_encoded = (int)length; } } return bytes_encoded; } /** Decode the BVLC Result message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac - Virtual MAC address * @param result_code - BVLC result code * * @return number of bytes decoded */ int bvlc6_decode_result( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac, uint16_t *result_code) { int bytes_consumed = 0; if (pdu && (pdu_len >= 5)) { if (vmac) { decode_unsigned24(&pdu[0], vmac); } if (result_code) { decode_unsigned16(&pdu[3], result_code); } bytes_consumed = 5; } return bytes_consumed; } /** Encode the BVLC Original-Unicast-NPDU message * * This message is used to send directed NPDUs to another B/IPv6 node * or router. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac_src - Source-Virtual-Address * @param vmac_dst - Destination-Virtual-Address * @param npdu - BACnet NPDU buffer * @param npdu_len - size of the BACnet NPDU buffer * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'01' Original-Unicast-NPDU * BVLC Length: 2-octets L Length of the BVLL message * Source-Virtual-Address: 3-octets * Destination-Virtual-Address: 3-octets * BACnet NPDU: Variable length */ int bvlc6_encode_original_unicast( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac_src, uint32_t vmac_dst, const uint8_t *npdu, uint16_t npdu_len) { int bytes_encoded = 0; uint16_t length = 10; uint16_t i = 0; length += npdu_len; if (pdu && (pdu_size >= length) && (vmac_src <= 0xFFFFFF) && (vmac_dst <= 0xFFFFFF)) { bytes_encoded = bvlc6_encode_header( pdu, pdu_size, BVLC6_ORIGINAL_UNICAST_NPDU, length); if (bytes_encoded == 4) { encode_unsigned24(&pdu[4], vmac_src); encode_unsigned24(&pdu[7], vmac_dst); if (npdu && length) { for (i = 0; i < npdu_len; i++) { pdu[10 + i] = npdu[i]; } } bytes_encoded = (int)length; } } return bytes_encoded; } /** Decode the BVLC Original-Unicast-NPDU message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac_src - Source-Virtual-Address * @param vmac_dst - Destination-Virtual-Address * @param npdu - BACnet NPDU buffer * @param npdu_size - size of the buffer for the decoded BACnet NPDU * @param npdu_len - decoded length of the BACnet NPDU buffer * * @return number of bytes decoded */ int bvlc6_decode_original_unicast( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac_src, uint32_t *vmac_dst, uint8_t *npdu, uint16_t npdu_size, uint16_t *npdu_len) { int bytes_consumed = 0; uint16_t length = 0; uint16_t i = 0; if (pdu && (pdu_len >= 6)) { if (vmac_src) { decode_unsigned24(&pdu[0], vmac_src); } if (vmac_dst) { decode_unsigned24(&pdu[3], vmac_dst); } length = pdu_len - 6; if (npdu && length && (length <= npdu_size)) { for (i = 0; i < length; i++) { npdu[i] = pdu[6 + i]; } } if (npdu_len) { *npdu_len = length; } bytes_consumed = (int)pdu_len; } return bytes_consumed; } /** Encode the BVLC Original-Broadcast-NPDU message * * This message is used by B/IPv6 nodes which are not * foreign devices to broadcast NPDUs on a B/IPv6 network. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac - Source-Virtual-Address * @param npdu - BACnet NPDU buffer * @param npdu_len - size of the BACnet NPDU buffer * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'02' Original-Broadcast-NPDU * BVLC Length: 2-octets L Length of the BVLL message * Source-Virtual-Address: 3-octets * BACnet NPDU: Variable length */ int bvlc6_encode_original_broadcast( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac, const uint8_t *npdu, uint16_t npdu_len) { int bytes_encoded = 0; uint16_t length = 7; uint16_t i = 0; length += npdu_len; if (pdu && (pdu_size >= length) && (vmac <= 0xFFFFFF)) { bytes_encoded = bvlc6_encode_header( pdu, pdu_size, BVLC6_ORIGINAL_BROADCAST_NPDU, length); if (bytes_encoded == 4) { encode_unsigned24(&pdu[4], vmac); if (npdu && npdu_len) { for (i = 0; i < npdu_len; i++) { pdu[7 + i] = npdu[i]; } } bytes_encoded = (int)length; } } return bytes_encoded; } /** Decode the BVLC Original-Broadcast-NPDU message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac - decoded Source-Virtual-Address * @param npdu - buffer to copy the decoded BACnet NDPU * @param npdu_size - size of the buffer for the decoded BACnet NPDU * @param npdu_len - decoded length of the BACnet NPDU * * @return number of bytes decoded */ int bvlc6_decode_original_broadcast( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac, uint8_t *npdu, uint16_t npdu_size, uint16_t *npdu_len) { int bytes_consumed = 0; uint16_t length = 0; uint16_t i = 0; if (pdu && (pdu_len >= 3)) { if (vmac) { decode_unsigned24(&pdu[0], vmac); } length = pdu_len - 3; if (npdu && length && (length <= npdu_size)) { for (i = 0; i < length; i++) { npdu[i] = pdu[3 + i]; } } if (npdu_len) { *npdu_len = length; } bytes_consumed = (int)pdu_len; } return bytes_consumed; } /** Encode the BVLC Address-Resolution message * * This message is unicast by B/IPv6 BBMDs to determine * the B/IPv6 address of a known virtual address belonging to * a different multicast domain. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac_src - Source-Virtual-Address * @param vmac_target - Target-Virtual-Address * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'03' Address-Resolution * BVLC Length: 2-octets X'000A' Length of the BVLL message * Source-Virtual-Address: 3-octets * Target-Virtual-Address: 3-octets */ int bvlc6_encode_address_resolution( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac_src, uint32_t vmac_target) { int bytes_encoded = 0; uint16_t length = 10; if (pdu && (pdu_size >= length) && (vmac_src <= 0xFFFFFF) && (vmac_target <= 0xFFFFFF)) { bytes_encoded = bvlc6_encode_header( pdu, pdu_size, BVLC6_ADDRESS_RESOLUTION, length); if (bytes_encoded == 4) { encode_unsigned24(&pdu[4], vmac_src); encode_unsigned24(&pdu[7], vmac_target); bytes_encoded = (int)length; } } return bytes_encoded; } /** Decode the BVLC Address-Resolution message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac_src - Source-Virtual-Address * @param vmac_target - Target-Virtual-Address * * @return number of bytes decoded */ int bvlc6_decode_address_resolution( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac_src, uint32_t *vmac_target) { int bytes_consumed = 0; if (pdu && (pdu_len >= 6)) { if (vmac_src) { decode_unsigned24(&pdu[0], vmac_src); } if (vmac_target) { decode_unsigned24(&pdu[3], vmac_target); } bytes_consumed = 6; } return bytes_consumed; } /** Encode the BVLC Address * * Data link layer addressing between B/IPv6 nodes consists of a 128-bit * IPv6 address followed by a two-octet UDP port number (both of which * shall be transmitted with the most significant octet first). This * address shall be referred to as a B/IPv6 address. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param bip6_address - B/IPv6 address * * @return number of bytes encoded */ int bvlc6_encode_address( uint8_t *pdu, uint16_t pdu_size, const BACNET_IP6_ADDRESS *bip6_address) { int bytes_encoded = 0; uint16_t length = BIP6_ADDRESS_MAX; unsigned i = 0; if (pdu && (pdu_size >= length) && bip6_address) { for (i = 0; i < IP6_ADDRESS_MAX; i++) { pdu[i] = bip6_address->address[i]; } encode_unsigned16(&pdu[IP6_ADDRESS_MAX], bip6_address->port); bytes_encoded = (int)length; } return bytes_encoded; } /** Decode the BVLC Address-Resolution message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param bip6_address - B/IPv6 address * * @return number of bytes decoded */ int bvlc6_decode_address( const uint8_t *pdu, uint16_t pdu_len, BACNET_IP6_ADDRESS *bip6_address) { int bytes_consumed = 0; uint16_t length = BIP6_ADDRESS_MAX; unsigned i = 0; if (pdu && (pdu_len >= length) && bip6_address) { for (i = 0; i < IP6_ADDRESS_MAX; i++) { bip6_address->address[i] = pdu[i]; } decode_unsigned16(&pdu[IP6_ADDRESS_MAX], &bip6_address->port); bytes_consumed = (int)length; } return bytes_consumed; } /** Copy the BVLC Address * * Data link layer addressing between B/IPv6 nodes consists of a 128-bit * IPv6 address followed by a two-octet UDP port number (both of which * shall be transmitted with the most significant octet first). This * address shall be referred to as a B/IPv6 address. * * @param dst - B/IPv6 address that will be filled with src * @param src - B/IPv6 address that will be copied into dst * * @return true if the address was copied */ bool bvlc6_address_copy(BACNET_IP6_ADDRESS *dst, const BACNET_IP6_ADDRESS *src) { bool status = false; unsigned int i = 0; if (src && dst) { for (i = 0; i < IP6_ADDRESS_MAX; i++) { dst->address[i] = src->address[i]; } dst->port = src->port; status = true; } return status; } /** Compare the BVLC Address * * Data link layer addressing between B/IPv6 nodes consists of a 128-bit * IPv6 address followed by a two-octet UDP port number (both of which * shall be transmitted with the most significant octet first). This * address shall be referred to as a B/IPv6 address. * * @param dst - B/IPv6 address that will be compared to src * @param src - B/IPv6 address that will be compared to dst * * @return true if the addresses are different */ bool bvlc6_address_different( const BACNET_IP6_ADDRESS *dst, const BACNET_IP6_ADDRESS *src) { bool status = false; unsigned int i = 0; if (src && dst) { for (i = 0; i < IP6_ADDRESS_MAX; i++) { if (dst->address[i] != src->address[i]) { status = true; } } if (dst->port != src->port) { status = true; } } return status; } /** * @brief Set the IPv6 address and port number * @param addr - B/IPv6 address that be set * @param address - B/IPv6 address bytes * @param port - B/IPv6 address port * @return true if the address is set */ bool bvlc6_address_n_port_set( BACNET_IP6_ADDRESS *addr, uint8_t *address, uint16_t port) { bool status = false; unsigned i; if (addr) { for (i = 0; i < IP6_ADDRESS_MAX; i += 2) { addr->address[i] = address[i]; } addr->port = port; status = true; } return status; } /** Set a BVLC Address from 16-bit group chunks * * Data link layer addressing between B/IPv6 nodes consists of a 128-bit * IPv6 address followed by a two-octet UDP port number (both of which * shall be transmitted with the most significant octet first). This * address shall be referred to as a B/IPv6 address. * * @param addr - B/IPv6 address that be set * @param addr0 - B/IPv6 address 16-bit * @param addr1 - B/IPv6 address bytes * @param addr2 - B/IPv6 address bytes * @param addr3 - B/IPv6 address bytes * @param addr4 - B/IPv6 address bytes * @param addr5 - B/IPv6 address bytes * @param addr6 - B/IPv6 address bytes * @param addr7 - B/IPv6 address bytes * * @return true if the address is set */ bool bvlc6_address_set( BACNET_IP6_ADDRESS *addr, uint16_t addr0, uint16_t addr1, uint16_t addr2, uint16_t addr3, uint16_t addr4, uint16_t addr5, uint16_t addr6, uint16_t addr7) { bool status = false; if (addr) { encode_unsigned16(&addr->address[0], addr0); encode_unsigned16(&addr->address[2], addr1); encode_unsigned16(&addr->address[4], addr2); encode_unsigned16(&addr->address[6], addr3); encode_unsigned16(&addr->address[8], addr4); encode_unsigned16(&addr->address[10], addr5); encode_unsigned16(&addr->address[12], addr6); encode_unsigned16(&addr->address[14], addr7); status = true; } return status; } /** Get a BVLC Address into 16-bit group chunks * * Data link layer addressing between B/IPv6 nodes consists of a 128-bit * IPv6 address followed by a two-octet UDP port number (both of which * shall be transmitted with the most significant octet first). This * address shall be referred to as a B/IPv6 address. * * @param addr - B/IPv6 address that be set * @param addr0 - B/IPv6 address 16-bit * @param addr1 - B/IPv6 address bytes * @param addr2 - B/IPv6 address bytes * @param addr3 - B/IPv6 address bytes * @param addr4 - B/IPv6 address bytes * @param addr5 - B/IPv6 address bytes * @param addr6 - B/IPv6 address bytes * @param addr7 - B/IPv6 address bytes * * @return true if the address is set */ bool bvlc6_address_get( const BACNET_IP6_ADDRESS *addr, uint16_t *addr0, uint16_t *addr1, uint16_t *addr2, uint16_t *addr3, uint16_t *addr4, uint16_t *addr5, uint16_t *addr6, uint16_t *addr7) { bool status = false; if (addr) { if (addr0) { decode_unsigned16(&addr->address[0], addr0); } if (addr1) { decode_unsigned16(&addr->address[2], addr1); } if (addr2) { decode_unsigned16(&addr->address[4], addr2); } if (addr3) { decode_unsigned16(&addr->address[6], addr3); } if (addr4) { decode_unsigned16(&addr->address[8], addr4); } if (addr5) { decode_unsigned16(&addr->address[10], addr5); } if (addr6) { decode_unsigned16(&addr->address[12], addr6); } if (addr7) { decode_unsigned16(&addr->address[14], addr7); } status = true; } return status; } /** * @brief Shift the buffer pointer and decrease the size after an snprintf * @param len - number of bytes (excluding terminating NULL byte) from snprintf * @param buf - pointer to the buffer pointer * @param buf_size - pointer to the buffer size * @return number of bytes (excluding terminating NULL byte) from snprintf */ static int snprintf_shift(int len, char **buf, size_t *buf_size) { if (buf) { if (*buf) { *buf += len; } } if (buf_size) { if ((*buf_size) >= len) { *buf_size -= len; } else { *buf_size = 0; } } return len; } /** Convert IPv6 Address from ASCII * * IPv6 addresses are represented as eight groups, separated by colons, * of four hexadecimal digits. * * For convenience, an IPv6 address may be abbreviated to shorter notations * by application of the following rules according to RFC 5952 [1]: * - One or more leading zeros from any groups of hexadecimal digits * are removed; this is usually done to either all or none of the * leading zeros. For example, the group 0042 is converted to 42. * - Consecutive sections of zeros are replaced with a double colon (::). * The double colon may only be used once in an address, as multiple * use would render the address indeterminate. RFC 5952 requires that * a double colon not be used to denote an omitted single section of * zeros. * * [1] https://www.rfc-editor.org/rfc/rfc5952 * * Adapted from the uIP TCP/IP stack and the Contiki operating system. * Thank you, Adam Dunkel, and the Swedish Institute of Computer Science. * * @param addr - B/IPv6 address that is parsed * @param buf - B/IPv6 address in 16-bit ASCII hex compressed format * @param buf_size - B/IPv6 address size in bytes * * @return the number of characters which would be generated for the given * input, excluding the trailing null. * @note buf and buf_size may be null and zero to return only the size */ int bvlc6_address_to_ascii( const BACNET_IP6_ADDRESS *addr, char *buf, size_t buf_size) { uint16_t a; unsigned int i; int f = 0; int len = 0; int n = 0; if (!addr) { return n; } if (!buf) { return n; } for (i = 0; i < IP6_ADDRESS_MAX; i += 2) { a = (addr->address[i] << 8) + addr->address[i + 1]; if ((a == 0) && (f >= 0)) { if (f++ == 0) { len = snprintf(buf, buf_size, "::"); n += snprintf_shift(len, &buf, &buf_size); } } else { if (f > 0) { f = -1; } else if (i > 0) { len = snprintf(buf, buf_size, ":"); n += snprintf_shift(len, &buf, &buf_size); } len = snprintf(buf, buf_size, "%x", a); n += snprintf_shift(len, &buf, &buf_size); } } return true; } /** Convert IPv6 Address from ASCII * * IPv6 addresses are represented as eight groups, separated by colons, * of four hexadecimal digits. * * For convenience, an IPv6 address may be abbreviated to shorter notations * by application of the following rules. * - One or more leading zeros from any groups of hexadecimal digits * are removed; this is usually done to either all or none of the * leading zeros. For example, the group 0042 is converted to 42. * - Consecutive sections of zeros are replaced with a double colon (::). * The double colon may only be used once in an address, as multiple * use would render the address indeterminate. RFC 5952 requires that * a double colon not be used to denote an omitted single section of * zeros. * * Adapted from the uIP TCP/IP stack and the Contiki operating system. * Thank you, Adam Dunkel, and the Swedish Institute of Computer Science. * * @param addr - B/IPv6 address that is set * @param addrstr - B/IPv6 address in 16-bit ASCII hex compressed format * * @return true if a valid address was set */ bool bvlc6_address_from_ascii(BACNET_IP6_ADDRESS *addr, const char *addrstr) { uint16_t value = 0; int tmp, zero = -1; unsigned int len = 0; unsigned int i = 0; char c = 0; if (!addr) { return false; } if (!addrstr) { return false; } if (*addrstr == '[') { addrstr++; } for (len = 0; len < IP6_ADDRESS_MAX - 1; addrstr++) { c = *addrstr; if (c == ':' || c == '\0' || c == ']' || c == '/') { addr->address[len] = (value >> 8) & 0xff; addr->address[len + 1] = value & 0xff; len += 2; value = 0; if (c == '\0' || c == ']' || c == '/') { break; } if (*(addrstr + 1) == ':') { /* Zero compression */ if (zero < 0) { zero = len; } addrstr++; } } else { if (c >= '0' && c <= '9') { tmp = c - '0'; } else if (c >= 'a' && c <= 'f') { tmp = c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { tmp = c - 'A' + 10; } else { /* illegal char! */ return false; } value = (value << 4) + (tmp & 0xf); } } if (c != '\0' && c != ']' && c != '/') { /* expected termination. too long! */ return false; } if (len < IP6_ADDRESS_MAX) { uint8_t addr_end[IP6_ADDRESS_MAX]; if (zero < 0) { /* too short address! */ return false; } /* move end address values from zero position to end position */ for (i = 0; i < len - zero; i++) { addr_end[i] = addr->address[zero + i]; } for (i = 0; i < len - zero; i++) { addr->address[zero + IP6_ADDRESS_MAX - len + i] = addr_end[i]; } /* fill in middle zero values */ for (i = 0; i < (IP6_ADDRESS_MAX - len); i++) { addr->address[zero + i] = 0; } } /* note: should we look for []:BAC0 UDP port number? */ return true; } /** Set a BACnet VMAC Address from a Device ID * * @param addr - BACnet address that be set * @param device_id - 22-bit device ID * * @return true if the address is set */ bool bvlc6_vmac_address_set(BACNET_ADDRESS *addr, uint32_t device_id) { bool status = false; if (addr) { encode_unsigned24(&addr->mac[0], device_id); addr->mac_len = 3; addr->net = 0; addr->len = 0; status = true; } return status; } /** Get a BACnet VMAC Address from a Device ID * * @param addr - BACnet address that be set * @param device_id - 22-bit device ID * * @return true if the address is set */ bool bvlc6_vmac_address_get(const BACNET_ADDRESS *addr, uint32_t *device_id) { bool status = false; if (addr && device_id) { if (addr->mac_len == 3) { decode_unsigned24(&addr->mac[0], device_id); status = true; } } return status; } /** Encode the BVLC Forwarded-Address-Resolution message * * This message is unicast by B/IPv6 BBMDs to determine * the B/IPv6 address of a known virtual address belonging to * a different multicast domain. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac_src - Source-Virtual-Address * @param vmac_target - Target-Virtual-Address * @param bip6_address - Original-Source-B/IPv6-Address * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'04' Forwarded-Address-Resolution * BVLC Length: 2-octets X'001C' Length of this message * Original-Source-Virtual-Address: 3-octets * Target-Virtual-Address: 3-octets * Original-Source-B/IPv6-Address 18-octets */ int bvlc6_encode_forwarded_address_resolution( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac_src, uint32_t vmac_target, const BACNET_IP6_ADDRESS *bip6_address) { int bytes_encoded = 0; uint16_t length = 0x001C; uint16_t offset = 0; if (pdu && (pdu_size >= length) && (vmac_src <= 0xFFFFFF) && (vmac_target <= 0xFFFFFF) && bip6_address) { bytes_encoded = bvlc6_encode_header( pdu, pdu_size, BVLC6_FORWARDED_ADDRESS_RESOLUTION, length); if (bytes_encoded == 4) { offset = 4; encode_unsigned24(&pdu[offset], vmac_src); offset += 3; encode_unsigned24(&pdu[offset], vmac_target); offset += 3; bvlc6_encode_address(&pdu[offset], pdu_size - offset, bip6_address); bytes_encoded = (int)length; } } return bytes_encoded; } /** Decode the BVLC Forwarded-Address-Resolution message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac_src - Source-Virtual-Address * @param vmac_target - Target-Virtual-Address * @param bip6_address - Original-Source-B/IPv6-Address * * @return number of bytes decoded */ int bvlc6_decode_forwarded_address_resolution( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac_src, uint32_t *vmac_target, BACNET_IP6_ADDRESS *bip6_address) { int bytes_consumed = 0; const uint16_t length = 3 + 3 + BIP6_ADDRESS_MAX; uint16_t offset = 0; if (pdu && (pdu_len >= length)) { if (vmac_src) { decode_unsigned24(&pdu[offset], vmac_src); } offset += 3; if (vmac_target) { decode_unsigned24(&pdu[offset], vmac_target); } offset += 3; if (bip6_address) { bvlc6_decode_address(&pdu[offset], pdu_len - offset, bip6_address); } bytes_consumed = (int)length; } return bytes_consumed; } /** Encode generic BVLC Address-Ack message * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac_src - Source-Virtual-Address * @param vmac_dst - Destination-Virtual-Address * * @return number of bytes encoded */ static int bvlc6_encode_address_ack( uint8_t message_type, uint8_t *pdu, uint16_t pdu_size, uint32_t vmac_src, uint32_t vmac_dst) { int bytes_encoded = 0; const uint16_t length = 10; uint16_t offset = 0; if (pdu && (pdu_size >= length) && (vmac_src <= 0xFFFFFF) && (vmac_dst <= 0xFFFFFF)) { bytes_encoded = bvlc6_encode_header(pdu, pdu_size, message_type, length); if (bytes_encoded == 4) { offset = 4; encode_unsigned24(&pdu[offset], vmac_src); offset += 3; encode_unsigned24(&pdu[offset], vmac_dst); bytes_encoded = (int)length; } } return bytes_encoded; } /** Encode the BVLC Address-Resolution-Ack message * * This message is the reply to either the Address-Resolution or * the Forwarded-Address-Resolution messages. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac_src - Source-Virtual-Address * @param vmac_dst - Destination-Virtual-Address * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'05' Address-Resolution-Ack * BVLC Length: 2-octets X'000A' Length of the BVLL message * Source-Virtual-Address: 3-octets * Destination-Virtual-Address: 3-octets */ int bvlc6_encode_address_resolution_ack( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac_src, uint32_t vmac_dst) { return bvlc6_encode_address_ack( BVLC6_ADDRESS_RESOLUTION_ACK, pdu, pdu_size, vmac_src, vmac_dst); } /** Decode the BVLC Address-Resolution-Ack message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac_src - Source-Virtual-Address * @param vmac_dst - Destination-Virtual-Address * * @return number of bytes decoded */ int bvlc6_decode_address_resolution_ack( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac_src, uint32_t *vmac_dst) { int bytes_consumed = 0; const uint16_t length = 6; uint16_t offset = 0; if (pdu && (pdu_len >= length)) { if (vmac_src) { decode_unsigned24(&pdu[offset], vmac_src); } offset += 3; if (vmac_dst) { decode_unsigned24(&pdu[offset], vmac_dst); } bytes_consumed = (int)length; } return bytes_consumed; } /** Encode the BVLC Virtual-Address-Resolution message * * This message is unicast by B/IPv6 nodes to determine the * virtual address of a device with a known B/IPv6 address. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac_src - Source-Virtual-Address * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'06' Virtual-Address-Resolution * BVLC Length: 2-octets X'0007' Length of the BVLL message * Source-Virtual-Address: 3-octets */ int bvlc6_encode_virtual_address_resolution( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac_src) { int bytes_encoded = 0; const uint16_t length = 7; if (pdu && (pdu_size >= length) && (vmac_src <= 0xFFFFFF)) { bytes_encoded = bvlc6_encode_header( pdu, pdu_size, BVLC6_VIRTUAL_ADDRESS_RESOLUTION, length); if (bytes_encoded == 4) { encode_unsigned24(&pdu[4], vmac_src); bytes_encoded = (int)length; } } return bytes_encoded; } /** Decode the BVLC Virtual-Address-Resolution message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac_src - Source-Virtual-Address * * @return number of bytes decoded */ int bvlc6_decode_virtual_address_resolution( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac_src) { int bytes_consumed = 0; if (pdu && (pdu_len >= 3)) { if (vmac_src) { decode_unsigned24(&pdu[0], vmac_src); } bytes_consumed = 3; } return bytes_consumed; } /** Encode the BVLC Virtual-Address-Resolution-Ack message * * This message is the reply to the Virtual-Address-Resolution message * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac_src - Source-Virtual-Address * @param vmac_dst - Destination-Virtual-Address * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'07' Address-Resolution-Ack * BVLC Length: 2-octets X'000A' Length of the BVLL message * Source-Virtual-Address: 3-octets * Destination-Virtual-Address: 3-octets */ int bvlc6_encode_virtual_address_resolution_ack( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac_src, uint32_t vmac_dst) { return bvlc6_encode_address_ack( BVLC6_VIRTUAL_ADDRESS_RESOLUTION_ACK, pdu, pdu_size, vmac_src, vmac_dst); } /** Decode the BVLC Virtual-Address-Resolution-Ack message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac_src - Source-Virtual-Address * @param vmac_dst - Destination-Virtual-Address * * @return number of bytes decoded */ int bvlc6_decode_virtual_address_resolution_ack( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac_src, uint32_t *vmac_dst) { return bvlc6_decode_address_resolution_ack( pdu, pdu_len, vmac_src, vmac_dst); } /** Encode the BVLC Forwarded-NPDU message * * This BVLL message is used in multicast messages from a BBMD * as well as in messages forwarded to registered foreign * devices. It contains the source address of the original * node as well as the original BACnet NPDU. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac_src - Original-Source-Virtual-Address * @param bip6_address - Original-Source-B/IPv6-Address * @param npdu - BACnet NPDU from Originating Device buffer * @param npdu_len - size of the BACnet NPDU buffer * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'08' Forwarded-NPDU * BVLC Length: 2-octets L Length of the BVLL message * Original-Source-Virtual-Address: 3-octets * Original-Source-B-IPv6-Address: 18-octets * BACnet NPDU from Originating Device: N-octets (N=L-25) */ int bvlc6_encode_forwarded_npdu( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac_src, const BACNET_IP6_ADDRESS *bip6_address, const uint8_t *npdu, uint16_t npdu_len) { int bytes_encoded = 0; uint16_t length = 1 + 1 + 2 + 3 + BIP6_ADDRESS_MAX; uint16_t i = 0; uint16_t offset = 0; length += npdu_len; if (pdu && (pdu_size >= length) && (vmac_src <= 0xFFFFFF)) { bytes_encoded = bvlc6_encode_header(pdu, pdu_size, BVLC6_FORWARDED_NPDU, length); if (bytes_encoded == 4) { offset = 4; encode_unsigned24(&pdu[offset], vmac_src); offset += 3; bvlc6_encode_address(&pdu[offset], pdu_size - offset, bip6_address); offset += BIP6_ADDRESS_MAX; if (npdu && length) { for (i = 0; i < npdu_len; i++) { pdu[offset + i] = npdu[i]; } } bytes_encoded = (int)length; } } return bytes_encoded; } /** Decode the BVLC Forwarded-NPDU message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac_src - Original-Source-Virtual-Address * @param bip6_address - Original-Source-B/IPv6-Address * @param npdu - BACnet NPDU buffer * @param npdu_size - size of the buffer for the decoded BACnet NPDU * @param npdu_len - decoded length of the BACnet NPDU buffer * * @return number of bytes decoded */ int bvlc6_decode_forwarded_npdu( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac_src, BACNET_IP6_ADDRESS *bip6_address, uint8_t *npdu, uint16_t npdu_size, uint16_t *npdu_len) { int bytes_consumed = 0; uint16_t length = 0; uint16_t i = 0; const uint16_t address_len = 3 + BIP6_ADDRESS_MAX; uint16_t offset = 0; if (pdu && (pdu_len >= address_len)) { if (vmac_src) { decode_unsigned24(&pdu[offset], vmac_src); } offset += 3; if (bip6_address) { bvlc6_decode_address(&pdu[offset], pdu_len - offset, bip6_address); } offset += BIP6_ADDRESS_MAX; length = pdu_len - offset; if (npdu && length && (length <= npdu_size)) { for (i = 0; i < length; i++) { npdu[i] = pdu[offset + i]; } } if (npdu_len) { *npdu_len = length; } bytes_consumed = (int)pdu_len; } return bytes_consumed; } /** Encode the BVLC Register-Foreign-Device message * * This message allows a foreign device, as defined in X.4.5.1, * to register with a BBMD for the purpose of receiving * broadcast messages. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac_src - Source-Virtual-Address * @param ttl_seconds - Time-to-Live T, in seconds * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'09' Register-Foreign-Device * BVLC Length: 2-octets X'0009' Length of the BVLL message * Source-Virtual-Address: 3-octets * Time-to-Live: 2-octets T Time-to-Live T, in seconds */ int bvlc6_encode_register_foreign_device( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac_src, uint16_t ttl_seconds) { int bytes_encoded = 0; const uint16_t length = 9; uint16_t offset = 0; if (pdu && (pdu_size >= length) && (vmac_src <= 0xFFFFFF)) { bytes_encoded = bvlc6_encode_header( pdu, pdu_size, BVLC6_REGISTER_FOREIGN_DEVICE, length); if (bytes_encoded == 4) { offset = 4; encode_unsigned24(&pdu[offset], vmac_src); offset += 3; encode_unsigned16(&pdu[offset], ttl_seconds); bytes_encoded = (int)length; } } return bytes_encoded; } /** Decode the BVLC Register-Foreign-Device message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac_src - Source-Virtual-Address * @param ttl_seconds - Time-to-Live T, in seconds * * @return number of bytes decoded */ int bvlc6_decode_register_foreign_device( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac_src, uint16_t *ttl_seconds) { int bytes_consumed = 0; const uint16_t length = 5; uint16_t offset = 0; if (pdu && (pdu_len >= length)) { if (vmac_src) { decode_unsigned24(&pdu[offset], vmac_src); } offset += 3; if (ttl_seconds) { decode_unsigned16(&pdu[offset], ttl_seconds); } bytes_consumed = (int)length; } return bytes_consumed; } /** Encode the BVLC Delete-Foreign-Device message * * This message allows a foreign device, as defined in X.4.5.1, * to register with a BBMD for the purpose of receiving * broadcast messages. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac_src - Source-Virtual-Address * @param fdt_entry - FDT Entry * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'0A' Delete-Foreign-Device * BVLC Length: 2-octets X'0019' Length of the BVLL message * Source-Virtual-Address: 3-octets * FDT Entry: 18-octets The FDT entry is the B/IPv6 address * of the foreign device to be deleted. */ int bvlc6_encode_delete_foreign_device( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac_src, const BACNET_IP6_ADDRESS *bip6_address) { int bytes_encoded = 0; const uint16_t length = 0x0019; uint16_t offset = 0; if (pdu && (pdu_size >= length) && (vmac_src <= 0xFFFFFF)) { bytes_encoded = bvlc6_encode_header( pdu, pdu_size, BVLC6_DELETE_FOREIGN_DEVICE, length); if (bytes_encoded == 4) { offset = 4; encode_unsigned24(&pdu[offset], vmac_src); offset += 3; if (bip6_address) { bvlc6_encode_address( &pdu[offset], pdu_size - offset, bip6_address); bytes_encoded = (int)length; } } } return bytes_encoded; } /** Decode the BVLC Delete-Foreign-Device message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac_src - Source-Virtual-Address * @param fdt_entry - The FDT entry is the B/IPv6 address of the * foreign device to be deleted. * * @return number of bytes decoded */ int bvlc6_decode_delete_foreign_device( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac_src, BACNET_IP6_ADDRESS *bip6_address) { int bytes_consumed = 0; /* BVLL length less the header length */ const uint16_t length = 0x0019 - (1 + 1 + 2); uint16_t offset = 0; if (pdu && (pdu_len >= length)) { if (vmac_src) { decode_unsigned24(&pdu[offset], vmac_src); } offset += 3; if (bip6_address) { bvlc6_decode_address(&pdu[offset], pdu_len - offset, bip6_address); } bytes_consumed = (int)length; } return bytes_consumed; } /** Encode the BVLC Secure-BVLL message * * This message is used to secure BVLL messages that do not contain NPDUs. * Its use is described in Clause 24. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param sbuf - Security Wrapper buffer * @param sbuf_len - size of the Security Wrapper buffer * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'0B' Secure-BVLL * BVLC Length: 2-octets L Length of the BVLL message * Security Wrapper: Variable length */ int bvlc6_encode_secure_bvll( uint8_t *pdu, uint16_t pdu_size, const uint8_t *sbuf, uint16_t sbuf_len) { int bytes_encoded = 0; uint16_t length = 4; uint16_t i = 0; length += sbuf_len; if (pdu && (pdu_size >= length)) { bytes_encoded = bvlc6_encode_header(pdu, pdu_size, BVLC6_SECURE_BVLL, length); if (bytes_encoded == 4) { if (sbuf && sbuf_len) { for (i = 0; i < sbuf_len; i++) { pdu[4 + i] = sbuf[i]; } } bytes_encoded = (int)length; } } return bytes_encoded; } /** Decode the BVLC Secure-BVLL message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param sbuf - Security Wrapper buffer * @param sbuf_size - size of the Security Wrapper buffer * @param sbuf_len - number of bytes decoded into the Security Wrapper buffer * * @return number of bytes decoded */ int bvlc6_decode_secure_bvll( const uint8_t *pdu, uint16_t pdu_len, uint8_t *sbuf, uint16_t sbuf_size, uint16_t *sbuf_len) { int bytes_consumed = 0; uint16_t i = 0; if (pdu) { if (sbuf_len) { *sbuf_len = pdu_len; } if (pdu_len && sbuf && sbuf_size) { for (i = 0; i < pdu_len; i++) { if (i < sbuf_size) { sbuf[i] = pdu[i]; } } } bytes_consumed = (int)pdu_len; } return bytes_consumed; } /** Encode the BVLC Distribute-Broadcast-To-Network message * * This message provides a mechanism whereby a foreign device * shall cause a BBMD to distribute a Forwarded-NPDU * BVLC to the local multicast domain, to all BBMD's configured * in the BBMD's BDT, and to all foreign devices in the * BBMD's FDT. * * @param pdu - buffer to store the encoding * @param pdu_size - size of the buffer to store encoding * @param vmac - Original-Source-Virtual-Address * @param npdu - BACnet NPDU from Originating Device buffer * @param npdu_len - size of the BACnet NPDU buffer * * @return number of bytes encoded * * BVLC Type: 1-octet X'82' BVLL for BACnet/IPv6 * BVLC Function: 1-octet X'0C' Original-Unicast-NPDU * BVLC Length: 2-octets L Length of the BVLL message * Original-Source-Virtual-Address: 3-octets * BACnet NPDU from Originating Device: Variable length */ int bvlc6_encode_distribute_broadcast_to_network( uint8_t *pdu, uint16_t pdu_size, uint32_t vmac, const uint8_t *npdu, uint16_t npdu_len) { int bytes_encoded = 0; uint16_t length = 1 + 1 + 2 + 3; uint16_t i = 0; length += npdu_len; if (pdu && (pdu_size >= length) && (vmac <= 0xFFFFFF)) { bytes_encoded = bvlc6_encode_header( pdu, pdu_size, BVLC6_DISTRIBUTE_BROADCAST_TO_NETWORK, length); if (bytes_encoded == 4) { encode_unsigned24(&pdu[4], vmac); if (npdu && (npdu_len > 0)) { for (i = 0; i < npdu_len; i++) { pdu[7 + i] = npdu[i]; } } bytes_encoded = (int)length; } } return bytes_encoded; } /** Decode the BVLC Original-Broadcast-NPDU message * * @param pdu - buffer from which to decode the message * @param pdu_len - length of the buffer that needs decoding * @param vmac - decoded Original-Source-Virtual-Address * @param npdu - buffer to copy the decoded BACnet NDPU * @param npdu_size - size of the buffer for the decoded BACnet NPDU * @param npdu_len - decoded length of the BACnet NPDU * * @return number of bytes decoded */ int bvlc6_decode_distribute_broadcast_to_network( const uint8_t *pdu, uint16_t pdu_len, uint32_t *vmac, uint8_t *npdu, uint16_t npdu_size, uint16_t *npdu_len) { int bytes_consumed = 0; uint16_t length = 0; uint16_t i = 0; if (pdu && (pdu_len >= 3)) { if (vmac) { decode_unsigned24(&pdu[0], vmac); } length = pdu_len - 3; if (npdu && length && (length <= npdu_size)) { for (i = 0; i < length; i++) { npdu[i] = pdu[3 + i]; } } if (npdu_len) { *npdu_len = length; } bytes_consumed = (int)pdu_len; } return bytes_consumed; } /** * @brief Encode a BBMD Address for Network Port object * @param apdu - the APDU buffer * @param apdu_size - the APDU buffer size * @param ip6_address - IPv6 address and port number * @return length of the APDU buffer */ int bvlc6_foreign_device_bbmd_host_address_encode( uint8_t *apdu, uint16_t apdu_size, const BACNET_IP6_ADDRESS *ip6_address) { BACNET_HOST_N_PORT address = { 0 }; int apdu_len = 0; address.host_ip_address = true; address.host_name = false; octetstring_init( &address.host.ip_address, &ip6_address->address[0], IP6_ADDRESS_MAX); address.port = ip6_address->port; apdu_len = host_n_port_encode(NULL, &address); if (apdu_len <= apdu_size) { apdu_len = host_n_port_encode(apdu, &address); } return apdu_len; } /** * @brief Encode one Broadcast-Distribution-Table entry for Network Port object * * BACnetBDTEntry ::= SEQUENCE { * bbmd-address [0] BACnetHostNPort, * BACnetHostNPort ::= SEQUENCE { * host [0] BACnetHostAddress, * BACnetHostAddress ::= CHOICE { * ip-address [1] OCTET STRING, -- 4 octets for B/IP * } * port [1] Unsigned16 * } * broadcast-mask [1] OCTET STRING -- shall be present if BACnet/IP, and * absent for BACnet/IPv6 * } * * @param apdu - the APDU buffer, or NULL for length * @param bdt_head - one BACnetBDTEntry * @return length of the APDU buffer */ int bvlc6_broadcast_distribution_table_entry_encode( uint8_t *apdu, const BACNET_IP6_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry) { int len = 0; int apdu_len = 0; BACNET_OCTET_STRING octet_string; if (bdt_entry) { /* bbmd-address [0] BACnetHostNPort - opening */ len = encode_opening_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } /* host [0] BACnetHostAddress - opening */ len = encode_opening_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } /* CHOICE - ip-address [1] OCTET STRING */ octetstring_init( &octet_string, &bdt_entry->bip6_address.address[0], IP6_ADDRESS_MAX); len = encode_context_octet_string(apdu, 1, &octet_string); apdu_len += len; if (apdu) { apdu += len; } /* host [0] BACnetHostAddress - closing */ len = encode_closing_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } /* port [1] Unsigned16 */ len = encode_context_unsigned(apdu, 1, bdt_entry->bip6_address.port); apdu_len += len; if (apdu) { apdu += len; } /* bbmd-address [0] BACnetHostNPort - closing */ len = encode_closing_tag(apdu, 0); apdu_len += len; } return apdu_len; } /** * @brief Encode the Broadcast-Distribution-Table for Network Port object * * BACnetLIST of BACnetBDTEntry * * @param apdu - the APDU buffer * @param apdu_size - the APDU buffer size * @param bdt_head - head of the BDT linked list * @return length of the APDU buffer */ int bvlc6_broadcast_distribution_table_list_encode( uint8_t *apdu, BACNET_IP6_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_head) { int len = 0; int apdu_len = 0; BACNET_IP6_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_entry; bdt_entry = bdt_head; while (bdt_entry) { if (bdt_entry->valid) { len = bvlc6_broadcast_distribution_table_entry_encode( apdu, bdt_entry); apdu_len += len; if (apdu) { apdu += len; } } /* next entry */ bdt_entry = bdt_entry->next; } return apdu_len; } /** * @brief Encode the Broadcast-Distribution-Table for Network Port object * @param apdu - the APDU buffer * @param apdu_size - the APDU buffer size * @param bdt_head - head of the BDT linked list * @return length of the APDU buffer, or BACNET_STATUS_ERROR on error */ int bvlc6_broadcast_distribution_table_encode( uint8_t *apdu, uint16_t apdu_size, BACNET_IP6_BROADCAST_DISTRIBUTION_TABLE_ENTRY *bdt_head) { int len = 0; len = bvlc6_broadcast_distribution_table_list_encode(NULL, bdt_head); if (len <= apdu_size) { len = bvlc6_broadcast_distribution_table_list_encode(apdu, bdt_head); } else { len = BACNET_STATUS_ERROR; } return len; } /** * @brief Encode the Foreign_Device-Table for Network Port object * * BACnetFDTEntry ::= SEQUENCE { * bacnetip-address [0] OCTET STRING, -- the 6-octet B/IP or 18-octet * B/IPv6 address of the registrant time-to-live [1] Unsigned16, -- time to live * in seconds remaining-time-to-live [2] Unsigned16 -- remaining time in seconds * } * * @param apdu - the APDU buffer, or NULL for length * @param fdt_head - head of the BDT linked list * @return length of the APDU buffer */ int bvlc6_foreign_device_table_entry_encode( uint8_t *apdu, const BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY *fdt_entry) { int len = 0; int apdu_len = 0; BACNET_OCTET_STRING octet_string = { 0 }; if (fdt_entry) { /* bacnetip-address [0] OCTET STRING */ len = bvlc6_encode_address( octetstring_value(&octet_string), octetstring_capacity(&octet_string), &fdt_entry->bip6_address); octetstring_truncate(&octet_string, len); len = encode_context_octet_string(apdu, 0, &octet_string); apdu_len += len; if (apdu) { apdu += len; } /* time-to-live [1] Unsigned16 */ len = encode_context_unsigned(apdu, 1, fdt_entry->ttl_seconds); apdu_len += len; if (apdu) { apdu += len; } /* remaining-time-to-live [2] Unsigned16 */ len = encode_context_unsigned(apdu, 2, fdt_entry->ttl_seconds_remaining); apdu_len += len; } return apdu_len; } /** * @brief Encode the Foreign_Device-Table for Network Port object * * BACnetLIST of BACnetFDTEntry * * @param apdu - the APDU buffer, or NULL for length * @param fdt_head - head of the BDT linked list * @return length of the APDU buffer */ int bvlc6_foreign_device_table_list_encode( uint8_t *apdu, BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY *fdt_head) { int len = 0; int apdu_len = 0; BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY *fdt_entry; fdt_entry = fdt_head; while (fdt_entry) { if (fdt_entry->valid) { len = bvlc6_foreign_device_table_entry_encode(apdu, fdt_entry); apdu_len += len; if (apdu) { apdu += len; } } /* next entry */ fdt_entry = fdt_entry->next; } return apdu_len; } /** * @brief Encode the Foreign_Device-Table for Network Port object * @param apdu - the APDU buffer * @param apdu_size - the APDU buffer size * @param fdt_head - head of the BDT linked list * @return length of the APDU buffer */ int bvlc6_foreign_device_table_encode( uint8_t *apdu, uint16_t apdu_size, BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY *fdt_head) { int len = 0; len = bvlc6_foreign_device_table_list_encode(NULL, fdt_head); if (len <= apdu_size) { len = bvlc6_foreign_device_table_list_encode(apdu, fdt_head); } else { len = BACNET_STATUS_ERROR; } return len; }