/*####COPYRIGHTBEGIN####
 -------------------------------------------
 Copyright (C) 2015 Steve Karg

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to:
 The Free Software Foundation, Inc.
 59 Temple Place - Suite 330
 Boston, MA  02111-1307, USA.

 As a special exception, if other files instantiate templates or
 use macros or inline functions from this file, or you compile
 this file and link it with other works to produce a work based
 on this file, this file does not by itself cause the resulting
 work to be covered by the GNU General Public License. However
 the source code for this file must still be made available in
 accordance with section (3) of the GNU General Public License.

 This exception does not invalidate any other reasons why a work
 based on this file might be covered by the GNU General Public
 License.
 -------------------------------------------
####COPYRIGHTEND####*/

#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include <stdio.h>
#include "bacnet/bacenum.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacint.h"
#include "bacnet/datalink/bvlc6.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(
    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(
    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,
    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(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,
    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(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(
    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, 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(
    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, 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(BACNET_IP6_ADDRESS *dst, 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;
}

/** 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(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;
}

/** 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(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, "::");
                if (buf) {
                    buf += len;
                }
                if (len > buf_size) {
                    buf_size = 0;
                } else {
                    buf_size -= len;
                }
                n += len;
            }
        } else {
            if (f > 0) {
                f = -1;
            } else if (i > 0) {
                len = snprintf(buf, buf_size, ":");
                if (buf) {
                    buf += len;
                }
                if (len > buf_size) {
                    buf_size = 0;
                } else {
                    buf_size -= len;
                }
                n += len;
            }
            len = snprintf(buf, buf_size, "%x", a);
            if (buf) {
                buf += len;
            }
            if (len > buf_size) {
                buf_size = 0;
            } else {
                buf_size -= len;
            }
            n += len;
        }
    }

    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(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,
    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(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(
    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(
    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(
    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,
    BACNET_IP6_ADDRESS *bip6_address,
    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(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(
    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,
    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(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);
            bytes_consumed = 3;
        }
        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, 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(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 && sbuf) {
        if (sbuf_len) {
            *sbuf_len = pdu_len;
        }
        if (pdu_len) {
            for (i = 0; i < pdu_len; i++) {
                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,
    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(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;
}
