diff --git a/demo/handler/h_bbmd6.c b/demo/handler/h_bbmd6.c index 1554a05e..e929f0ce 100644 --- a/demo/handler/h_bbmd6.c +++ b/demo/handler/h_bbmd6.c @@ -1,1228 +1,1228 @@ -/*####COPYRIGHTBEGIN#### - ------------------------------------------- - Copyright (C) 2016 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 /* for standard i/o, like printing */ -#include /* for standard integer types uint8_t etc. */ -#include /* for the standard bool type. */ -#include /* for memcpy */ -#include "bacdcode.h" -#include "bip6.h" -#include "bvlc6.h" -#include "debug.h" -#include "device.h" -#include "vmac.h" -#ifndef TEST -#include "net.h" -#endif - -/** result from a client request */ -static uint16_t BVLC6_Result_Code = BVLC6_RESULT_SUCCESSFUL_COMPLETION; -/** incoming function */ -static uint8_t BVLC6_Function_Code = BVLC6_RESULT; - -/** if we are a foreign device, store the remote BBMD address/port here */ -static BACNET_IP6_ADDRESS Remote_BBMD; -#if defined(BACDL_BIP6) && BBMD6_ENABLED -/* local buffer & length for sending */ -static uint8_t BVLC6_Buffer[MAX_MPDU]; -static uint16_t BVLC6_Buffer_Len; -/* Broadcast Distribution Table */ -#ifndef MAX_BBMD6_ENTRIES -#define MAX_BBMD6_ENTRIES 128 -#endif -static BACNET_IP6_BROADCAST_DISTRIBUTION_TABLE_ENTRY BBMD_Table[MAX_BBMD6_ENTRIES]; -/* Foreign Device Table */ -#ifndef MAX_FD6_ENTRIES -#define MAX_FD6_ENTRIES 128 -#endif -static BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY FD_Table[MAX_FD6_ENTRIES]; -#endif - -#if defined(BACDL_BIP6) && BBMD6_ENABLED -/** A timer function that is called about once a second. - * - * @param seconds - number of elapsed seconds since the last call - */ -void bbmd6_maintenance_timer( - time_t seconds) -{ - unsigned i = 0; - - for (i = 0; i < MAX_FD_ENTRIES; i++) { - if (FD_Table[i].valid) { - if (FD_Table[i].seconds_remaining) { - if (FD_Table[i].seconds_remaining < seconds) { - FD_Table[i].seconds_remaining = 0; - } else { - FD_Table[i].seconds_remaining -= seconds; - } - if (FD_Table[i].seconds_remaining == 0) { - FD_Table[i].valid = false; - } - } - } - } -} -#endif - -/** - * Sets the IPv6 source address from a VMAC address structure - * - * @param addr - IPv6 source address - * @param vmac - VMAC address that will be use for holding the IPv6 address - * - * @return true if the address was set - */ -static bool bbmd6_address_from_vmac( - BACNET_IP6_ADDRESS *addr, - struct vmac_data *vmac) -{ - bool status = false; - unsigned int i = 0; - - if (vmac && addr && (vmac->mac_len == 18)) { - for (i = 0; i < IP6_ADDRESS_MAX; i++) { - addr->address[i] = vmac->mac[i]; - } - decode_unsigned16(&vmac->mac[16], &addr->port); - status = true; - } - - return status; -} - -/** - * Sets the IPv6 source address to a VMAC address structure - * - * @param vmac - VMAC address that will be use for holding the IPv6 address - * @param addr - IPv6 source address - * - * @return true if the address was set - */ -static bool bbmd6_address_to_vmac( - struct vmac_data *vmac, - BACNET_IP6_ADDRESS *addr) -{ - bool status = false; - unsigned int i = 0; - - if (vmac && addr) { - for (i = 0; i < IP6_ADDRESS_MAX; i++) { - vmac->mac[i] = addr->address[i]; - } - encode_unsigned16(&vmac->mac[16], addr->port); - vmac->mac_len = 18; - status = true; - } - - return status; -} - -/** - * Adds an IPv6 source address and Device ID key to a VMAC address cache - * - * @param device_id - device ID used as the key-pair - * @param addr - IPv6 source address - * - * @return true if the VMAC address was added - */ -static bool bbmd6_add_vmac( - uint32_t device_id, - BACNET_IP6_ADDRESS *addr) -{ - bool status = false; - struct vmac_data *vmac; - struct vmac_data new_vmac; - - if (addr) { - vmac = VMAC_Find_By_Key(device_id); - if (vmac) { - /* already exists - replace? */ - } else if (bbmd6_address_to_vmac(&new_vmac, addr)) { - /* new entry - add it! */ - status = VMAC_Add(device_id, &new_vmac); - debug_printf("BVLC6: Adding VMAC %lu.\n", (unsigned long)device_id); - } - } - - return status; -} - -/** - * Compares the IPv6 source address to my VMAC - * - * @param addr - IPv6 source address - * - * @return true if the IPv6 from sin match me - */ -static bool bbmd6_address_match_self( - BACNET_IP6_ADDRESS *addr) -{ - BACNET_IP6_ADDRESS my_addr = {{0}}; - bool status = false; - - if (bip6_get_addr(&my_addr)) { - if (!bvlc6_address_different(&my_addr, addr)) { - status = true; - } - } - - return status; -} - -/** - * Finds the BACNET_IP6_ADDRESS for the #BACNET_ADDRESS via VMAC - * - * @param addr - IPv6 address - * @param vmac_src - VMAC address (Device ID) - * @param baddr - Points to a #BACNET_ADDRESS structure containing the - * VMAC address. - * - * @return true if the address was in the VMAC table - */ -static bool bbmd6_address_from_bacnet_address( - BACNET_IP6_ADDRESS * addr, - uint32_t * vmac_src, - BACNET_ADDRESS * baddr) -{ - struct vmac_data *vmac; - bool status = false; - uint32_t device_id = 0; - - if (addr && baddr) { - status = bvlc6_vmac_address_get(baddr, &device_id); - if (status) { - vmac = VMAC_Find_By_Key(device_id); - if (vmac) { - debug_printf("BVLC6: Found VMAC %lu (len=%u).\n", - (unsigned long)device_id, - (unsigned)vmac->mac_len); - status = bbmd6_address_from_vmac(addr, vmac); - if (vmac_src) { - *vmac_src = device_id; - } - } - } - } - - return status; -} - - - -/** - * The common send function for BACnet/IPv6 application layer - * - * @param dest - Points to a #BACNET_ADDRESS structure containing the - * destination address. - * @param npdu_data - Points to a BACNET_NPDU_DATA structure containing the - * destination network layer control flags and data. - * @param mtu - the bytes of data to send - * @param mtu_len - the number of bytes of data to send - * @return Upon successful completion, returns the number of bytes sent. - * Otherwise, -1 shall be returned and errno set to indicate the error. - */ -int bip6_send_pdu( - BACNET_ADDRESS * dest, - BACNET_NPDU_DATA * npdu_data, - uint8_t * pdu, - unsigned pdu_len) -{ - BACNET_IP6_ADDRESS bvlc_dest = {{0}}; - uint8_t mtu[MAX_MPDU] = { 0 }; - uint16_t mtu_len = 0; - uint32_t vmac_src = 0; - uint32_t vmac_dst = 0; - - /* this datalink doesn't need to know the npdu data */ - (void) npdu_data; - /* handle various broadcasts: */ - if ((dest->net == BACNET_BROADCAST_NETWORK) || (dest->mac_len == 0)) { - /* mac_len = 0 is a broadcast address */ - /* net = 0 indicates local, net = 65535 indicates global */ - if (Remote_BBMD.port) { - /* we are a foreign device */ - bvlc6_address_copy(&bvlc_dest, &Remote_BBMD); - vmac_src = Device_Object_Instance_Number(); - mtu_len = bvlc6_encode_distribute_broadcast_to_network( - mtu, sizeof(mtu), vmac_src, pdu, pdu_len); - debug_printf("BVLC6: Sent Distribute-Broadcast-to-Network.\n"); - } else { - bip6_get_broadcast_addr(&bvlc_dest); - vmac_src = Device_Object_Instance_Number(); - mtu_len = bvlc6_encode_original_broadcast( - mtu, sizeof(mtu), vmac_src, pdu, pdu_len); - debug_printf("BVLC6: Sent Original-Broadcast-NPDU.\n"); - } - } else if ((dest->net > 0) && (dest->len == 0)) { - /* net > 0 and net < 65535 are network specific broadcast if len = 0 */ - if (dest->mac_len == 3) { - /* network specific broadcast to address */ - bbmd6_address_from_bacnet_address(&bvlc_dest, &vmac_dst, dest); - } else { - bip6_get_broadcast_addr(&bvlc_dest); - } - vmac_src = Device_Object_Instance_Number(); - mtu_len = bvlc6_encode_original_broadcast( - mtu, sizeof(mtu), vmac_src, pdu, pdu_len); - debug_printf("BVLC6: Sent Original-Broadcast-NPDU.\n"); - } else if (dest->mac_len == 3) { - /* valid unicast */ - bbmd6_address_from_bacnet_address(&bvlc_dest, &vmac_dst, dest); - debug_printf("BVLC6: Sending to VMAC %lu.\n", (unsigned long)vmac_dst); - vmac_src = Device_Object_Instance_Number(); - mtu_len = bvlc6_encode_original_unicast( - mtu, sizeof(mtu), vmac_src, vmac_dst, pdu, pdu_len); - debug_printf("BVLC6: Sent Original-Unicast-NPDU.\n"); - } else { - debug_printf("BVLC6: Send failure. Invalid Address.\n"); - return -1; - } - - return bip6_send_mpdu(&bvlc_dest, mtu, mtu_len); -} - -#if defined(BACDL_BIP6) && BBMD6_ENABLED -/** - * The send function for Broacast Distribution Table - * - * @param addr - Points to a #BACNET_IP6_ADDRESS structure containing the - * source IPv6 address. - * @param vmac_src - Source-Virtual-Address - * @param npdu - the bytes of NPDU+APDU data to send - * @param npdu_len - the number of bytes of NPDU+APDU data to send - */ -static void bbmd6_send_pdu_bdt( - uint8_t * mtu, - unsigned int mtu_len) -{ - BACNET_IP6_ADDRESS my_addr = {{0}}; - unsigned i = 0; /* loop counter */ - - if (mtu) { - bip6_get_addr(&my_addr); - for (i = 0; i < MAX_BBMD_ENTRIES; i++) { - if (BBMD_Table[i].valid) { - if (bvlc6_address_different(&my_addr, - &BBMD_Table[i].bip6_address)) { - bip6_send_mpdu(&BBMD_Table[i].bip6_address, mtu, mtu_len); - } - } - } - } -} - -/** - * The send function for Broacast Distribution Table - * - * @param addr - Points to a #BACNET_IP6_ADDRESS structure containing the - * source IPv6 address. - * @param vmac_src - Source-Virtual-Address - * @param npdu - the bytes of NPDU+APDU data to send - * @param npdu_len - the number of bytes of NPDU+APDU data to send - */ -static void bbmd6_send_pdu_fdt( - uint8_t * mtu, - unsigned int mtu_len) -{ - BACNET_IP6_ADDRESS my_addr = {{0}}; - unsigned i = 0; /* loop counter */ - - if (mtu) { - bip6_get_addr(&my_addr); - for (i = 0; i < MAX_FD_ENTRIES; i++) { - if (FD_Table[i].valid) { - if (bvlc6_address_different(&my_addr, - &FD_Table[i].bip6_address)) { - bip6_send_mpdu(&FD_Table[i].bip6_address, mtu, mtu_len); - } - } - } - } -} - -/** - * The Forward NPDU send function for Broacast Distribution Table - * - * @param addr - Points to a #BACNET_IP6_ADDRESS structure containing the - * source IPv6 address. - * @param vmac_src - Source-Virtual-Address - * @param npdu - the bytes of NPDU+APDU data to send - * @param npdu_len - the number of bytes of NPDU+APDU data to send - */ -static void bbmd6_send_forward_npdu( - BACNET_IP6_ADDRESS *address, - uint32_t vmac_src, - uint8_t * npdu, - unsigned int npdu_len) -{ - uint8_t mtu[MAX_MPDU] = { 0 }; - uint16_t mtu_len = 0; - unsigned i = 0; /* loop counter */ - - for (i = 0; i < MAX_BBMD_ENTRIES; i++) { - if (BBMD_Table[i].valid) { - if (bbmd6_address_match_self(&BBMD_Table[i].bip6_address)) { - /* don't forward to our selves */ - } else { - bip6_send_mpdu(&BBMD_Table[i].bip6_address, mtu, mtu_len); - } - } - } - for (i = 0; i < MAX_FD_ENTRIES; i++) { - if (FD_Table[i].valid) { - if (bbmd6_address_match_self(&FD_Table[i].bip6_address)) { - /* don't forward to our selves */ - } else { - bip6_send_mpdu(&FD_Table[i].bip6_address, mtu, mtu_len); - } - } - } -} - -#endif - -/** - * The Result Code send function for BACnet/IPv6 application layer - * - * @param dest_addr - Points to a #BACNET_IP6_ADDRESS structure containing the - * destination IPv6 address. - * @param vmac_src - Source-Virtual-Address - * @param result_code - BVLC result code - * - * @return Upon successful completion, returns the number of bytes sent. - * Otherwise, -1 shall be returned and errno set to indicate the error. - */ -static int bvlc6_send_result( - BACNET_IP6_ADDRESS *dest_addr, - uint32_t vmac_src, - uint16_t result_code) -{ - uint8_t mtu[MAX_MPDU] = { 0 }; - uint16_t mtu_len = 0; - - mtu_len = bvlc6_encode_result(&mtu[0], sizeof(mtu), vmac_src, result_code); - - return bip6_send_mpdu(dest_addr, mtu, mtu_len); -} - -/** - * The Address Resolution Ack send function for BACnet/IPv6 application layer - * - * @param dest_addr - Points to a #BACNET_IP6_ADDRESS structure containing the - * destination IPv6 address. - * @param vmac_src - Source-Virtual-Address - * @param vmac_dst - Destination-Virtual-Address - * - * @return Upon successful completion, returns the number of bytes sent. - * Otherwise, -1 shall be returned and errno set to indicate the error. - */ -static int bvlc6_send_address_resolution_ack( - BACNET_IP6_ADDRESS *dest_addr, - uint32_t vmac_src, - uint32_t vmac_dst) -{ - uint8_t mtu[MAX_MPDU] = { 0 }; - uint16_t mtu_len = 0; - - mtu_len = bvlc6_encode_address_resolution_ack( - &mtu[0], sizeof(mtu), - vmac_src, vmac_dst); - - return bip6_send_mpdu(dest_addr, mtu, mtu_len); -} - -/** - * The Virtual Address Resolution Ack send function for BACnet/IPv6 - * application layer - * - * @param dest_addr - Points to a #BACNET_IP6_ADDRESS structure containing the - * destination IPv6 address. - * @param vmac_src - Source-Virtual-Address - * @param vmac_dst - Destination-Virtual-Address - * - * @return Upon successful completion, returns the number of bytes sent. - * Otherwise, -1 shall be returned and errno set to indicate the error. - */ -static int bvlc6_send_virtual_address_resolution_ack( - BACNET_IP6_ADDRESS *dest_addr, - uint32_t vmac_src, - uint32_t vmac_dst) -{ - uint8_t mtu[MAX_MPDU] = { 0 }; - uint16_t mtu_len = 0; - - mtu_len = bvlc6_encode_virtual_address_resolution_ack( - &mtu[0], sizeof(mtu), - vmac_src, vmac_dst); - - return bip6_send_mpdu(dest_addr, mtu, mtu_len); -} - -/** - * Handler for Virtual-Address-Resolution - * - * @param addr - BACnet/IPv6 source address any NAK or reply back to. - * @param pdu - The received NPDU+APDU buffer. - * @param pdu_len - How many bytes in NPDU+APDU buffer. - */ -static void bbmd6_virtual_address_resolution_handler( - BACNET_IP6_ADDRESS *addr, - uint8_t * pdu, - uint16_t pdu_len) -{ - int function_len = 0; - uint32_t vmac_src = 0; - uint32_t vmac_me = 0; - - if (addr && pdu) { - debug_printf("BIP6: Received Virtual-Address-Resolution.\n"); - if (bbmd6_address_match_self(addr)) { - /* ignore messages from my IPv6 address */ - } else { - function_len = bvlc6_decode_virtual_address_resolution( - pdu, pdu_len, - &vmac_src); - if (function_len) { - bbmd6_add_vmac(vmac_src, addr); - /* The Address-Resolution-ACK message is unicast - to the B/IPv6 node that originally initiated - the Address-Resolution message. */ - vmac_me = Device_Object_Instance_Number(); - bvlc6_send_virtual_address_resolution_ack( - addr, vmac_me, vmac_src); - } - } - } -} - -/** - * Handler for Virtual-Address-Resolution-ACK - * - * @param addr - BACnet/IPv6 source address any NAK or reply back to. - * @param pdu - The received NPDU+APDU buffer. - * @param pdu_len - How many bytes in NPDU+APDU buffer. - */ -static void bbmd6_virtual_address_resolution_ack_handler( - BACNET_IP6_ADDRESS *addr, - uint8_t * pdu, - uint16_t pdu_len) -{ - int function_len = 0; - uint32_t vmac_src = 0; - uint32_t vmac_dst = 0; - - if (addr && pdu) { - debug_printf("BIP6: Received Virtual-Address-Resolution-ACK.\n"); - if (bbmd6_address_match_self(addr)) { - /* ignore messages from my IPv6 address */ - } else { - function_len = bvlc6_decode_virtual_address_resolution_ack( - pdu, pdu_len, - &vmac_src, &vmac_dst); - if (function_len) { - bbmd6_add_vmac(vmac_src, addr); - } - } - } -} - -/** - * Handler for Address-Resolution - * - * @param addr - BACnet/IPv6 source address any NAK or reply back to. - * @param pdu - The received NPDU+APDU buffer. - * @param pdu_len - How many bytes in NPDU+APDU buffer. - */ -static void bbmd6_address_resolution_handler( - BACNET_IP6_ADDRESS *addr, - uint8_t * pdu, - uint16_t pdu_len) -{ - int function_len = 0; - uint32_t vmac_src = 0; - uint32_t vmac_target = 0; - uint32_t vmac_me = 0; - - if (addr && pdu) { - debug_printf("BIP6: Received Address-Resolution.\n"); - if (bbmd6_address_match_self(addr)) { - /* ignore messages from my IPv6 address */ - } else { - function_len = bvlc6_decode_address_resolution( - pdu, pdu_len, - &vmac_src, &vmac_target); - if (function_len) { - bbmd6_add_vmac(vmac_src, addr); - vmac_me = Device_Object_Instance_Number(); - if (vmac_target == vmac_me) { - /* The Address-Resolution-ACK message is unicast - to the B/IPv6 node that originally initiated - the Address-Resolution message. */ - bvlc6_send_address_resolution_ack( - addr, vmac_me, vmac_src); - } - } - } - } -} - -/** - * Handler for Address-Resolution-ACK - * - * @param addr - BACnet/IPv6 source address any NAK or reply back to. - * @param pdu - The received NPDU+APDU buffer. - * @param pdu_len - How many bytes in NPDU+APDU buffer. - */ -static void bbmd6_address_resolution_ack_handler( - BACNET_IP6_ADDRESS *addr, - uint8_t * pdu, - uint16_t pdu_len) -{ - int function_len = 0; - uint32_t vmac_src = 0; - uint32_t vmac_dst = 0; - - if (addr && pdu) { - debug_printf("BIP6: Received Address-Resolution-ACK.\n"); - if (bbmd6_address_match_self(addr)) { - /* ignore messages from my IPv6 address */ - } else { - function_len = bvlc6_decode_address_resolution_ack( - pdu, pdu_len, - &vmac_src, &vmac_dst); - if (function_len) { - bbmd6_add_vmac(vmac_src, addr); - } - } - } -} - -/** - * Use this handler when you are not a BBMD. - * Sets the BVLC6_Function_Code in case it is needed later. - * - * @param addr - BACnet/IPv6 source address any NAK or reply back to. - * @param src - BACnet style the source address interpreted VMAC - * @param mtu - The received MTU buffer. - * @param mtu_len - How many bytes in MTU buffer. - * - * @return number of bytes offset into the NPDU for APDU, or 0 if handled - */ -static int handler_bbmd6_for_non_bbmd( - BACNET_IP6_ADDRESS *addr, - BACNET_ADDRESS * src, - uint8_t * mtu, - uint16_t mtu_len) -{ - uint16_t result_code = BVLC6_RESULT_SUCCESSFUL_COMPLETION; - uint32_t vmac_src = 0; - uint32_t vmac_dst = 0; - uint8_t message_type = 0; - uint16_t message_length = 0; - int header_len = 0; - int function_len = 0; - uint8_t * pdu = NULL; - uint16_t pdu_len = 0; - uint16_t npdu_len = 0; - bool send_result = false; - uint16_t offset = 0; - BACNET_IP6_ADDRESS fwd_address = {{0}}; - - header_len = bvlc6_decode_header(mtu, mtu_len, &message_type, - &message_length); - if (header_len == 4) { - BVLC6_Function_Code = message_type; - pdu = &mtu[header_len]; - pdu_len = mtu_len - header_len; - switch (BVLC6_Function_Code) { - case BVLC6_RESULT: - function_len = bvlc6_decode_result(pdu, pdu_len, &vmac_src, - &result_code); - if (function_len) { - BVLC6_Result_Code = result_code; - /* The Virtual MAC address table shall be updated - using the respective parameter values of the - incoming messages. */ - bbmd6_add_vmac(vmac_src, addr); - bvlc6_vmac_address_set(src, vmac_src); - debug_printf("BIP6: Received Result Code=%d\n", - BVLC6_Result_Code); - } - break; - case BVLC6_REGISTER_FOREIGN_DEVICE: - result_code = BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK; - send_result = true; - break; - case BVLC6_DELETE_FOREIGN_DEVICE: - result_code = BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK; - send_result = true; - break; - case BVLC6_DISTRIBUTE_BROADCAST_TO_NETWORK: - result_code = BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK; - send_result = true; - break; - case BVLC6_ORIGINAL_UNICAST_NPDU: - /* This message is used to send directed NPDUs to - another B/IPv6 node or router. */ - debug_printf("BIP6: Received Original-Unicast-NPDU.\n"); - if (bbmd6_address_match_self(addr)) { - /* ignore messages from my IPv6 address */ - debug_printf("BIP6: Original-Unicast-NPDU is me!.\n"); - } else { - function_len = bvlc6_decode_original_unicast( - pdu, pdu_len, - &vmac_src, &vmac_dst, - NULL, 0, &npdu_len); - if (function_len) { - if (vmac_dst == Device_Object_Instance_Number()) { - /* The Virtual MAC address table shall be updated - using the respective parameter values of the - incoming messages. */ - bbmd6_add_vmac(vmac_src, addr); - bvlc6_vmac_address_set(src, vmac_src); - offset = header_len + (function_len - npdu_len); - } else { - debug_printf( - "BIP6: Original-Unicast-NPDU: " - "VMAC is not me!\n"); - } - } else { - debug_printf( - "BIP6: Original-Unicast-NPDU: Unable to decode!\n"); - } - } - break; - case BVLC6_ORIGINAL_BROADCAST_NPDU: - debug_printf("BIP6: Received Original-Broadcast-NPDU.\n"); - if (bbmd6_address_match_self(addr)) { - /* ignore messages from my IPv6 address */ - debug_printf("BIP6: Original-Broadcast-NPDU is me!\n"); - } else { - function_len = bvlc6_decode_original_broadcast( - pdu, pdu_len, - &vmac_src, - NULL, 0, &npdu_len); - if (function_len) { - /* The Virtual MAC address table shall be updated - using the respective parameter values of the - incoming messages. */ - bbmd6_add_vmac(vmac_src, addr); - bvlc6_vmac_address_set(src, vmac_src); - offset = header_len + (function_len - npdu_len); - } else { - debug_printf( - "BIP6: Original-Broadcast-NPDU: Unable to decode!\n"); - } - } - break; - case BVLC6_FORWARDED_NPDU: - debug_printf("BIP6: Received Forwarded-NPDU.\n"); - if (bbmd6_address_match_self(addr)) { - /* ignore messages from my IPv6 address */ - debug_printf("BIP6: Forwarded-NPDU is me!\n"); - } else { - function_len = bvlc6_decode_forwarded_npdu( - pdu, pdu_len, - &vmac_src, &fwd_address, - NULL, 0, &npdu_len); - if (function_len) { - /* The Virtual MAC address table shall be updated - using the respective parameter values of the - incoming messages. */ - bbmd6_add_vmac(vmac_src, &fwd_address); - bvlc6_vmac_address_set(src, vmac_src); - offset = header_len + (function_len - npdu_len); - } else { - debug_printf( - "BIP6: Forwarded-NPDU: Unable to decode!\n"); - } - } - break; - case BVLC6_FORWARDED_ADDRESS_RESOLUTION: - result_code = BVLC6_RESULT_ADDRESS_RESOLUTION_NAK; - send_result = true; - break; - case BVLC6_ADDRESS_RESOLUTION: - bbmd6_address_resolution_handler(addr, pdu, pdu_len); - break; - case BVLC6_ADDRESS_RESOLUTION_ACK: - bbmd6_address_resolution_ack_handler(addr, pdu, pdu_len); - break; - case BVLC6_VIRTUAL_ADDRESS_RESOLUTION: - bbmd6_virtual_address_resolution_handler(addr, pdu, pdu_len); - break; - case BVLC6_VIRTUAL_ADDRESS_RESOLUTION_ACK: - bbmd6_virtual_address_resolution_ack_handler(addr, pdu, pdu_len); - break; - case BVLC6_SECURE_BVLL: - break; - default: - break; - } - if (send_result) { - vmac_src = Device_Object_Instance_Number(); - bvlc6_send_result(addr, vmac_src, result_code); - debug_printf("BIP6: sent result code=%d\n", result_code); - } - } - - return offset; -} - -#if defined(BACDL_BIP6) && BBMD6_ENABLED -#warning FIXME: Needs ported to IPv6 -/** - * Use this handler when you are a BBMD. - * Sets the BVLC6_Function_Code in case it is needed later. - * - * @param addr - BACnet/IPv6 source address any NAK or reply back to. - * @param src - BACnet style the source address interpreted VMAC - * @param mtu - The received MTU buffer. - * @param mtu_len - How many bytes in MTU buffer. - * - * @return number of bytes offset into the NPDU for APDU, or 0 if handled - */ -static int handler_bbmd6_for_bbmd( - BACNET_IP6_ADDRESS *addr, - BACNET_ADDRESS * src, - uint8_t * mtu, - uint16_t mtu_len) -{ - uint16_t result_code = BVLC6_RESULT_SUCCESSFUL_COMPLETION; - uint32_t vmac_me = 0; - uint32_t vmac_src = 0; - uint32_t vmac_dst = 0; - uint32_t vmac_target = 0; - uint8_t message_type = 0; - uint16_t message_length = 0; - int header_len = 0; - int function_len = 0; - uint8_t * pdu = NULL; - uint16_t pdu_len = 0; - uint8_t * npdu = NULL; - uint16_t npdu_len = 0; - bool send_result = false; - uint16_t offset = 0; - BACNET_IP6_ADDRESS fwd_address = {{0}}; - - header_len = bvlc6_decode_header(mtu, mtu_len, &message_type, - &message_length); - if (header_len == 4) { - BVLC6_Function_Code = message_type; - pdu = &mtu[header_len]; - pdu_len = mtu_len - header_len; - switch (BVLC6_Function_Code) { - case BVLC6_RESULT: - function_len = bvlc6_decode_result(pdu, pdu_len, &vmac_src, - &result_code); - if (function_len) { - BVLC6_Result_Code = result_code; - /* The Virtual MAC address table shall be updated - using the respective parameter values of the - incoming messages. */ - bbmd6_add_vmac(vmac_src, addr); - bvlc6_vmac_address_set(src, vmac_src); - debug_printf("BIP6: Received Result Code=%d\n", - BVLC6_Result_Code); - } - break; - case BVLC6_REGISTER_FOREIGN_DEVICE: - result_code = BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK; - send_result = true; - break; - case BVLC6_DELETE_FOREIGN_DEVICE: - result_code = BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK; - send_result = true; - break; - case BVLC6_DISTRIBUTE_BROADCAST_TO_NETWORK: - result_code = BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK; - send_result = true; - break; - case BVLC6_ORIGINAL_UNICAST_NPDU: - /* This message is used to send directed NPDUs to - another B/IPv6 node or router. */ - debug_printf("BIP6: Received Original-Unicast-NPDU.\n"); - if (bbmd6_address_match_self(addr)) { - debug_printf("BIP6: Dropped Original-Unicast-NPDU.\n"); - /* ignore messages from my IPv6 address */ - offset = 0; - } else { - function_len = bvlc6_decode_original_unicast( - pdu, pdu_len, - &vmac_src, &vmac_dst, - NULL, 0, &npdu_len); - if (function_len) { - if (vmac_dst == Device_Object_Instance_Number()) { - /* The Virtual MAC address table shall be updated - using the respective parameter values of the - incoming messages. */ - bbmd6_add_vmac(vmac_src, addr); - bvlc6_vmac_address_set(src, vmac_src); - offset = header_len + (function_len - npdu_len); - } - } - } - break; - case BVLC6_ORIGINAL_BROADCAST_NPDU: - debug_printf("BIP6: Received Original-Broadcast-NPDU.\n"); - function_len = bvlc6_decode_original_broadcast( - pdu, pdu_len, - &vmac_src, - NULL, 0, &npdu_len); - if (function_len) { - offset = header_len + (function_len - npdu_len); - npdu = &mtu[offset]; - /* Upon receipt of a BVLL Original-Broadcast-NPDU - message from the local multicast domain, a BBMD - shall construct a BVLL Forwarded-NPDU message and - unicast it to each entry in its BDT. In addition, - the constructed BVLL Forwarded-NPDU message shall - be unicast to each foreign device currently in - the BBMD's FDT */ - BVLC6_Buffer_Len = bvlc6_encode_forwarded_npdu( - &BVLC6_Buffer[0], sizeof(BVLC6_Buffer), - vmac_src, addr, - npdu, npdu_len); - bbmd6_send_pdu_bdt(&BVLC6_Buffer[0], BVLC6_Buffer_Len); - bbmd6_send_pdu_fdt(&BVLC6_Buffer[0], BVLC6_Buffer_Len); - if (!bbmd6_address_match_self(addr)) { - /* The Virtual MAC address table shall be updated - using the respective parameter values of the - incoming messages. */ - bbmd6_add_vmac(vmac_src, addr); - bvlc6_vmac_address_set(src, vmac_src); - } - } - break; - case BVLC6_FORWARDED_NPDU: - debug_printf("BIP6: Received Forwarded-NPDU.\n"); - function_len = bvlc6_decode_forwarded_npdu( - pdu, pdu_len, - &vmac_src, &fwd_address, - NULL, 0, &npdu_len); - if (function_len) { - offset = header_len + (function_len - npdu_len); - npdu = &mtu[offset]; - /* Upon receipt of a BVLL Forwarded-NPDU message - from a BBMD which is in the receiving BBMD's BDT, - a BBMD shall construct a BVLL Forwarded-NPDU and - transmit it via multicast to B/IPv6 devices in the - local multicast domain. */ - BVLC6_Buffer_Len = bvlc6_encode_forwarded_npdu( - &BVLC6_Buffer[0], sizeof(BVLC6_Buffer), - vmac_src, addr, - npdu, npdu_len); - bip6_get_broadcast_addr(&bvlc_dest); - bip6_send_mpdu(&bvlc_dest, &BVLC6_Buffer[0], BVLC6_Buffer_Len); - /* In addition, the constructed BVLL Forwarded-NPDU - message shall be unicast to each foreign device in - the BBMD's FDT. If the BBMD is unable to transmit - the Forwarded-NPDU, or the message was not received - from a BBMD which is in the receiving BBMD's BDT, - no BVLC-Result shall be returned and the message - shall be discarded. */ - bbmd6_send_pdu_fdt(&BVLC6_Buffer[0], BVLC6_Buffer_Len); - if (!bbmd6_address_match_self(addr)) { - /* The Virtual MAC address table shall be updated - using the respective parameter values of the - incoming messages. */ - bbmd6_add_vmac(vmac_src, &fwd_address); - bvlc6_vmac_address_set(src, vmac_src); - } - } - break; - case BVLC6_FORWARDED_ADDRESS_RESOLUTION: - result_code = BVLC6_RESULT_ADDRESS_RESOLUTION_NAK; - send_result = true; - break; - case BVLC6_ADDRESS_RESOLUTION: - bbmd6_address_resolution_handler(addr, pdu, pdu_len); - break; - case BVLC6_ADDRESS_RESOLUTION_ACK: - bbmd6_address_resolution_ack_handler(addr, pdu, pdu_len); - break; - case BVLC6_VIRTUAL_ADDRESS_RESOLUTION: - bbmd6_virtual_address_resolution_handler(addr, pdu, pdu_len); - break; - case BVLC6_VIRTUAL_ADDRESS_RESOLUTION_ACK: - bbmd6_virtual_address_resolution_ack_handler(addr, pdu, pdu_len); - break; - case BVLC6_SECURE_BVLL: - break; - default: - break; - } - if (send_result) { - vmac_src = Device_Object_Instance_Number(); - bvlc6_send_result(addr, vmac_src, result_code); - debug_printf("BIP6: sent result code=%d\n", result_code); - } - } - - return offset; -} -#endif - -/** - * Use this handler for BACnet/IPv6 BVLC - * - * @param addr [in] IPv6 address to send any NAK back to. - * @param src [out] returns the source address - * @param npdu [in] The received buffer. - * @param npdu_len [in] How many bytes in npdu[]. - * - * @return number of bytes offset into the NPDU for APDU, or 0 if handled - */ -int bvlc6_handler( - BACNET_IP6_ADDRESS *addr, - BACNET_ADDRESS * src, - uint8_t * npdu, - uint16_t npdu_len) -{ -#if defined(BACDL_BIP6) && BBMD6_ENABLED - return handler_bbmd6_for_bbmd(addr, src, npdu, npdu_len); -#else - return handler_bbmd6_for_non_bbmd(addr, src, npdu, npdu_len); -#endif -} - -/** Register as a foreign device with the indicated BBMD. - * @param bbmd_address - IPv4 address (long) of BBMD to register with, - * in network byte order. - * @param bbmd_port - Network port of BBMD, in network byte order - * @param time_to_live_seconds - Lease time to use when registering. - * @return Positive number (of bytes sent) on success, - * 0 if no registration request is sent, or - * -1 if registration fails. - */ -int bvlc6_register_with_bbmd( - BACNET_IP6_ADDRESS *bbmd_addr, - uint32_t vmac_src, - uint16_t time_to_live_seconds) -{ - uint8_t mtu[MAX_MPDU] = { 0 }; - uint16_t mtu_len = 0; - - mtu_len = bvlc6_encode_register_foreign_device( - &mtu[0], sizeof(mtu), vmac_src, time_to_live_seconds); - - return bip6_send_mpdu(bbmd_addr, &mtu[0], mtu_len); -} - -/** Returns the last BVLL Result we received, either as the result of a BBMD - * request we sent, or (if not a BBMD or Client), from trying to register - * as a foreign device. - * - * @return BVLC6_RESULT_SUCCESSFUL_COMPLETION on success, - * BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK if registration failed, - * or one of the other codes (if we are a BBMD). - */ -uint16_t bvlc6_get_last_result( - void) -{ - return BVLC6_Result_Code; -} - -/** Returns the current BVLL Function Code we are processing. - * We have to store this higher layer code for when the lower layers - * need to know what it is, especially to differentiate between - * BVLC6_ORIGINAL_UNICAST_NPDU and BVLC6_ORIGINAL_BROADCAST_NPDU. - * - * @return A BVLC6_ code, such as BVLC6_ORIGINAL_UNICAST_NPDU. - */ -uint8_t bvlc6_get_function_code( - void) -{ - return BVLC6_Function_Code; -} - -void bvlc6_init(void) -{ - VMAC_Init(); -} - -#ifdef TEST -#include -#include -#include "ctest.h" -static uint32_t Device_ID = 0; -static uint32_t Test_Device_ID = 12345; -static BACNET_IP6_ADDRESS BIP6_Addr; -static BACNET_IP6_ADDRESS Test_BIP6_Addr; -static BACNET_IP6_ADDRESS BIP6_Broadcast_Addr; -static uint8_t BIP6_MTU_Buffer[MAX_MPDU]; - -/* network stub functions */ -/** - * BACnet/IP Datalink Receive handler. - * - * @param src - returns the source address - * @param npdu - returns the NPDU buffer - * @param max_npdu -maximum size of the NPDU buffer - * @param timeout - number of milliseconds to wait for a packet - * - * @return Number of bytes received, or 0 if none or timeout. - */ -uint16_t bip6_receive( - BACNET_ADDRESS * src, - uint8_t * npdu, - uint16_t max_npdu, - unsigned timeout) -{ - return 0; -} - -/** - * The send function for BACnet/IPv6 driver layer - * - * @param dest - Points to a BACNET_IP6_ADDRESS structure containing the - * destination address. - * @param mtu - the bytes of data to send - * @param mtu_len - the number of bytes of data to send - * - * @return Upon successful completion, returns the number of bytes sent. - * Otherwise, -1 shall be returned and errno set to indicate the error. - */ -int bip6_send_mpdu( - BACNET_IP6_ADDRESS *dest, - uint8_t * mtu, - uint16_t mtu_len) -{ - return 0; -} - -/** Return the Object Instance number for our (single) Device Object. - * This is a key function, widely invoked by the handler code, since - * it provides "our" (ie, local) address. - * - * @return The Instance number used in the BACNET_OBJECT_ID for the Device. - */ -uint32_t Device_Object_Instance_Number( - void) -{ - return Device_ID; -} - -/** - * Get the BACnet/IP address - * - * @return BACnet/IP address - */ -bool bip6_get_addr( - BACNET_IP6_ADDRESS *addr) -{ - return bvlc6_address_copy(addr, &BIP6_Addr); -} - -/** - * Get the BACnet/IP address - * - * @return BACnet/IP address - */ -bool bip6_get_broadcast_addr( - BACNET_IP6_ADDRESS *addr) -{ - return bvlc6_address_copy(addr, &BIP6_Broadcast_Addr); -} - -static void test_BBMD_Result( - Test * pTest) -{ - int result = 0; - uint32_t vmac_src = 0x1234; - uint16_t result_code[6] = { - BVLC6_RESULT_SUCCESSFUL_COMPLETION, - BVLC6_RESULT_ADDRESS_RESOLUTION_NAK, - BVLC6_RESULT_VIRTUAL_ADDRESS_RESOLUTION_NAK, - BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK, - BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK, - BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK - }; - uint16_t test_result_code = 0; - uint8_t test_function_code = 0; - BACNET_IP6_ADDRESS addr; - BACNET_ADDRESS src; - unsigned int i = 0; - uint8_t mtu[MAX_MPDU] = { 0 }; - uint16_t mtu_len = 0; - - bvlc6_address_set(&addr, - BIP6_MULTICAST_LINK_LOCAL, 0, 0, 0, 0, 0, 0, - BIP6_MULTICAST_GROUP_ID); - addr.port = 0xBAC0; - bvlc6_vmac_address_set(&src, vmac_src); - for (i = 0; i < 6; i++) { - mtu_len = bvlc6_encode_result(&mtu[0], sizeof(mtu), - vmac_src, result_code[i]); - result = handler_bbmd6_for_non_bbmd(&addr, &src, &mtu[0], mtu_len); - /* validate that the result is handled (0) */ - ct_test(pTest, result == 0); - test_result_code = bvlc6_get_last_result(); - ct_test(pTest, test_result_code == result_code[i]); - test_function_code = bvlc6_get_function_code(); - ct_test(pTest, test_function_code == BVLC6_RESULT); - } -} - -static void test_BBMD6( - Test * pTest) -{ - bool rc; - - /* individual tests */ - rc = ct_addTestFunction(pTest, test_BBMD_Result); - assert(rc); -} - -#ifdef TEST_BBMD6 -int main( - void) -{ - Test *pTest; - - pTest = ct_create("BACnet Broadcast Management Device IP/v6", NULL); - test_BBMD6(pTest); - /* configure output */ - ct_setStream(pTest, stdout); - ct_run(pTest); - (void) ct_report(pTest); - ct_destroy(pTest); - - return 0; -} -#endif -#endif - - +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2016 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 /* for standard i/o, like printing */ +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include /* for memcpy */ +#include "bacdcode.h" +#include "bip6.h" +#include "bvlc6.h" +#include "debug.h" +#include "device.h" +#include "vmac.h" +#ifndef TEST +#include "net.h" +#endif + +/** result from a client request */ +static uint16_t BVLC6_Result_Code = BVLC6_RESULT_SUCCESSFUL_COMPLETION; +/** incoming function */ +static uint8_t BVLC6_Function_Code = BVLC6_RESULT; + +/** if we are a foreign device, store the remote BBMD address/port here */ +static BACNET_IP6_ADDRESS Remote_BBMD; +#if defined(BACDL_BIP6) && BBMD6_ENABLED +/* local buffer & length for sending */ +static uint8_t BVLC6_Buffer[MAX_MPDU]; +static uint16_t BVLC6_Buffer_Len; +/* Broadcast Distribution Table */ +#ifndef MAX_BBMD6_ENTRIES +#define MAX_BBMD6_ENTRIES 128 +#endif +static BACNET_IP6_BROADCAST_DISTRIBUTION_TABLE_ENTRY BBMD_Table[MAX_BBMD6_ENTRIES]; +/* Foreign Device Table */ +#ifndef MAX_FD6_ENTRIES +#define MAX_FD6_ENTRIES 128 +#endif +static BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY FD_Table[MAX_FD6_ENTRIES]; +#endif + +#if defined(BACDL_BIP6) && BBMD6_ENABLED +/** A timer function that is called about once a second. + * + * @param seconds - number of elapsed seconds since the last call + */ +void bbmd6_maintenance_timer( + time_t seconds) +{ + unsigned i = 0; + + for (i = 0; i < MAX_FD_ENTRIES; i++) { + if (FD_Table[i].valid) { + if (FD_Table[i].seconds_remaining) { + if (FD_Table[i].seconds_remaining < seconds) { + FD_Table[i].seconds_remaining = 0; + } else { + FD_Table[i].seconds_remaining -= seconds; + } + if (FD_Table[i].seconds_remaining == 0) { + FD_Table[i].valid = false; + } + } + } + } +} +#endif + +/** + * Sets the IPv6 source address from a VMAC address structure + * + * @param addr - IPv6 source address + * @param vmac - VMAC address that will be use for holding the IPv6 address + * + * @return true if the address was set + */ +static bool bbmd6_address_from_vmac( + BACNET_IP6_ADDRESS *addr, + struct vmac_data *vmac) +{ + bool status = false; + unsigned int i = 0; + + if (vmac && addr && (vmac->mac_len == 18)) { + for (i = 0; i < IP6_ADDRESS_MAX; i++) { + addr->address[i] = vmac->mac[i]; + } + decode_unsigned16(&vmac->mac[16], &addr->port); + status = true; + } + + return status; +} + +/** + * Sets the IPv6 source address to a VMAC address structure + * + * @param vmac - VMAC address that will be use for holding the IPv6 address + * @param addr - IPv6 source address + * + * @return true if the address was set + */ +static bool bbmd6_address_to_vmac( + struct vmac_data *vmac, + BACNET_IP6_ADDRESS *addr) +{ + bool status = false; + unsigned int i = 0; + + if (vmac && addr) { + for (i = 0; i < IP6_ADDRESS_MAX; i++) { + vmac->mac[i] = addr->address[i]; + } + encode_unsigned16(&vmac->mac[16], addr->port); + vmac->mac_len = 18; + status = true; + } + + return status; +} + +/** + * Adds an IPv6 source address and Device ID key to a VMAC address cache + * + * @param device_id - device ID used as the key-pair + * @param addr - IPv6 source address + * + * @return true if the VMAC address was added + */ +static bool bbmd6_add_vmac( + uint32_t device_id, + BACNET_IP6_ADDRESS *addr) +{ + bool status = false; + struct vmac_data *vmac; + struct vmac_data new_vmac; + + if (addr) { + vmac = VMAC_Find_By_Key(device_id); + if (vmac) { + /* already exists - replace? */ + } else if (bbmd6_address_to_vmac(&new_vmac, addr)) { + /* new entry - add it! */ + status = VMAC_Add(device_id, &new_vmac); + debug_printf("BVLC6: Adding VMAC %lu.\n", (unsigned long)device_id); + } + } + + return status; +} + +/** + * Compares the IPv6 source address to my VMAC + * + * @param addr - IPv6 source address + * + * @return true if the IPv6 from sin match me + */ +static bool bbmd6_address_match_self( + BACNET_IP6_ADDRESS *addr) +{ + BACNET_IP6_ADDRESS my_addr = {{0}}; + bool status = false; + + if (bip6_get_addr(&my_addr)) { + if (!bvlc6_address_different(&my_addr, addr)) { + status = true; + } + } + + return status; +} + +/** + * Finds the BACNET_IP6_ADDRESS for the #BACNET_ADDRESS via VMAC + * + * @param addr - IPv6 address + * @param vmac_src - VMAC address (Device ID) + * @param baddr - Points to a #BACNET_ADDRESS structure containing the + * VMAC address. + * + * @return true if the address was in the VMAC table + */ +static bool bbmd6_address_from_bacnet_address( + BACNET_IP6_ADDRESS * addr, + uint32_t * vmac_src, + BACNET_ADDRESS * baddr) +{ + struct vmac_data *vmac; + bool status = false; + uint32_t device_id = 0; + + if (addr && baddr) { + status = bvlc6_vmac_address_get(baddr, &device_id); + if (status) { + vmac = VMAC_Find_By_Key(device_id); + if (vmac) { + debug_printf("BVLC6: Found VMAC %lu (len=%u).\n", + (unsigned long)device_id, + (unsigned)vmac->mac_len); + status = bbmd6_address_from_vmac(addr, vmac); + if (vmac_src) { + *vmac_src = device_id; + } + } + } + } + + return status; +} + + + +/** + * The common send function for BACnet/IPv6 application layer + * + * @param dest - Points to a #BACNET_ADDRESS structure containing the + * destination address. + * @param npdu_data - Points to a BACNET_NPDU_DATA structure containing the + * destination network layer control flags and data. + * @param mtu - the bytes of data to send + * @param mtu_len - the number of bytes of data to send + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +int bip6_send_pdu( + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data, + uint8_t * pdu, + unsigned pdu_len) +{ + BACNET_IP6_ADDRESS bvlc_dest = {{0}}; + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + uint32_t vmac_src = 0; + uint32_t vmac_dst = 0; + + /* this datalink doesn't need to know the npdu data */ + (void) npdu_data; + /* handle various broadcasts: */ + if ((dest->net == BACNET_BROADCAST_NETWORK) || (dest->mac_len == 0)) { + /* mac_len = 0 is a broadcast address */ + /* net = 0 indicates local, net = 65535 indicates global */ + if (Remote_BBMD.port) { + /* we are a foreign device */ + bvlc6_address_copy(&bvlc_dest, &Remote_BBMD); + vmac_src = Device_Object_Instance_Number(); + mtu_len = bvlc6_encode_distribute_broadcast_to_network( + mtu, sizeof(mtu), vmac_src, pdu, pdu_len); + debug_printf("BVLC6: Sent Distribute-Broadcast-to-Network.\n"); + } else { + bip6_get_broadcast_addr(&bvlc_dest); + vmac_src = Device_Object_Instance_Number(); + mtu_len = bvlc6_encode_original_broadcast( + mtu, sizeof(mtu), vmac_src, pdu, pdu_len); + debug_printf("BVLC6: Sent Original-Broadcast-NPDU.\n"); + } + } else if ((dest->net > 0) && (dest->len == 0)) { + /* net > 0 and net < 65535 are network specific broadcast if len = 0 */ + if (dest->mac_len == 3) { + /* network specific broadcast to address */ + bbmd6_address_from_bacnet_address(&bvlc_dest, &vmac_dst, dest); + } else { + bip6_get_broadcast_addr(&bvlc_dest); + } + vmac_src = Device_Object_Instance_Number(); + mtu_len = bvlc6_encode_original_broadcast( + mtu, sizeof(mtu), vmac_src, pdu, pdu_len); + debug_printf("BVLC6: Sent Original-Broadcast-NPDU.\n"); + } else if (dest->mac_len == 3) { + /* valid unicast */ + bbmd6_address_from_bacnet_address(&bvlc_dest, &vmac_dst, dest); + debug_printf("BVLC6: Sending to VMAC %lu.\n", (unsigned long)vmac_dst); + vmac_src = Device_Object_Instance_Number(); + mtu_len = bvlc6_encode_original_unicast( + mtu, sizeof(mtu), vmac_src, vmac_dst, pdu, pdu_len); + debug_printf("BVLC6: Sent Original-Unicast-NPDU.\n"); + } else { + debug_printf("BVLC6: Send failure. Invalid Address.\n"); + return -1; + } + + return bip6_send_mpdu(&bvlc_dest, mtu, mtu_len); +} + +#if defined(BACDL_BIP6) && BBMD6_ENABLED +/** + * The send function for Broacast Distribution Table + * + * @param addr - Points to a #BACNET_IP6_ADDRESS structure containing the + * source IPv6 address. + * @param vmac_src - Source-Virtual-Address + * @param npdu - the bytes of NPDU+APDU data to send + * @param npdu_len - the number of bytes of NPDU+APDU data to send + */ +static void bbmd6_send_pdu_bdt( + uint8_t * mtu, + unsigned int mtu_len) +{ + BACNET_IP6_ADDRESS my_addr = {{0}}; + unsigned i = 0; /* loop counter */ + + if (mtu) { + bip6_get_addr(&my_addr); + for (i = 0; i < MAX_BBMD_ENTRIES; i++) { + if (BBMD_Table[i].valid) { + if (bvlc6_address_different(&my_addr, + &BBMD_Table[i].bip6_address)) { + bip6_send_mpdu(&BBMD_Table[i].bip6_address, mtu, mtu_len); + } + } + } + } +} + +/** + * The send function for Broacast Distribution Table + * + * @param addr - Points to a #BACNET_IP6_ADDRESS structure containing the + * source IPv6 address. + * @param vmac_src - Source-Virtual-Address + * @param npdu - the bytes of NPDU+APDU data to send + * @param npdu_len - the number of bytes of NPDU+APDU data to send + */ +static void bbmd6_send_pdu_fdt( + uint8_t * mtu, + unsigned int mtu_len) +{ + BACNET_IP6_ADDRESS my_addr = {{0}}; + unsigned i = 0; /* loop counter */ + + if (mtu) { + bip6_get_addr(&my_addr); + for (i = 0; i < MAX_FD_ENTRIES; i++) { + if (FD_Table[i].valid) { + if (bvlc6_address_different(&my_addr, + &FD_Table[i].bip6_address)) { + bip6_send_mpdu(&FD_Table[i].bip6_address, mtu, mtu_len); + } + } + } + } +} + +/** + * The Forward NPDU send function for Broacast Distribution Table + * + * @param addr - Points to a #BACNET_IP6_ADDRESS structure containing the + * source IPv6 address. + * @param vmac_src - Source-Virtual-Address + * @param npdu - the bytes of NPDU+APDU data to send + * @param npdu_len - the number of bytes of NPDU+APDU data to send + */ +static void bbmd6_send_forward_npdu( + BACNET_IP6_ADDRESS *address, + uint32_t vmac_src, + uint8_t * npdu, + unsigned int npdu_len) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + unsigned i = 0; /* loop counter */ + + for (i = 0; i < MAX_BBMD_ENTRIES; i++) { + if (BBMD_Table[i].valid) { + if (bbmd6_address_match_self(&BBMD_Table[i].bip6_address)) { + /* don't forward to our selves */ + } else { + bip6_send_mpdu(&BBMD_Table[i].bip6_address, mtu, mtu_len); + } + } + } + for (i = 0; i < MAX_FD_ENTRIES; i++) { + if (FD_Table[i].valid) { + if (bbmd6_address_match_self(&FD_Table[i].bip6_address)) { + /* don't forward to our selves */ + } else { + bip6_send_mpdu(&FD_Table[i].bip6_address, mtu, mtu_len); + } + } + } +} + +#endif + +/** + * The Result Code send function for BACnet/IPv6 application layer + * + * @param dest_addr - Points to a #BACNET_IP6_ADDRESS structure containing the + * destination IPv6 address. + * @param vmac_src - Source-Virtual-Address + * @param result_code - BVLC result code + * + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +static int bvlc6_send_result( + BACNET_IP6_ADDRESS *dest_addr, + uint32_t vmac_src, + uint16_t result_code) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + + mtu_len = bvlc6_encode_result(&mtu[0], sizeof(mtu), vmac_src, result_code); + + return bip6_send_mpdu(dest_addr, mtu, mtu_len); +} + +/** + * The Address Resolution Ack send function for BACnet/IPv6 application layer + * + * @param dest_addr - Points to a #BACNET_IP6_ADDRESS structure containing the + * destination IPv6 address. + * @param vmac_src - Source-Virtual-Address + * @param vmac_dst - Destination-Virtual-Address + * + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +static int bvlc6_send_address_resolution_ack( + BACNET_IP6_ADDRESS *dest_addr, + uint32_t vmac_src, + uint32_t vmac_dst) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + + mtu_len = bvlc6_encode_address_resolution_ack( + &mtu[0], sizeof(mtu), + vmac_src, vmac_dst); + + return bip6_send_mpdu(dest_addr, mtu, mtu_len); +} + +/** + * The Virtual Address Resolution Ack send function for BACnet/IPv6 + * application layer + * + * @param dest_addr - Points to a #BACNET_IP6_ADDRESS structure containing the + * destination IPv6 address. + * @param vmac_src - Source-Virtual-Address + * @param vmac_dst - Destination-Virtual-Address + * + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +static int bvlc6_send_virtual_address_resolution_ack( + BACNET_IP6_ADDRESS *dest_addr, + uint32_t vmac_src, + uint32_t vmac_dst) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + + mtu_len = bvlc6_encode_virtual_address_resolution_ack( + &mtu[0], sizeof(mtu), + vmac_src, vmac_dst); + + return bip6_send_mpdu(dest_addr, mtu, mtu_len); +} + +/** + * Handler for Virtual-Address-Resolution + * + * @param addr - BACnet/IPv6 source address any NAK or reply back to. + * @param pdu - The received NPDU+APDU buffer. + * @param pdu_len - How many bytes in NPDU+APDU buffer. + */ +static void bbmd6_virtual_address_resolution_handler( + BACNET_IP6_ADDRESS *addr, + uint8_t * pdu, + uint16_t pdu_len) +{ + int function_len = 0; + uint32_t vmac_src = 0; + uint32_t vmac_me = 0; + + if (addr && pdu) { + debug_printf("BIP6: Received Virtual-Address-Resolution.\n"); + if (bbmd6_address_match_self(addr)) { + /* ignore messages from my IPv6 address */ + } else { + function_len = bvlc6_decode_virtual_address_resolution( + pdu, pdu_len, + &vmac_src); + if (function_len) { + bbmd6_add_vmac(vmac_src, addr); + /* The Address-Resolution-ACK message is unicast + to the B/IPv6 node that originally initiated + the Address-Resolution message. */ + vmac_me = Device_Object_Instance_Number(); + bvlc6_send_virtual_address_resolution_ack( + addr, vmac_me, vmac_src); + } + } + } +} + +/** + * Handler for Virtual-Address-Resolution-ACK + * + * @param addr - BACnet/IPv6 source address any NAK or reply back to. + * @param pdu - The received NPDU+APDU buffer. + * @param pdu_len - How many bytes in NPDU+APDU buffer. + */ +static void bbmd6_virtual_address_resolution_ack_handler( + BACNET_IP6_ADDRESS *addr, + uint8_t * pdu, + uint16_t pdu_len) +{ + int function_len = 0; + uint32_t vmac_src = 0; + uint32_t vmac_dst = 0; + + if (addr && pdu) { + debug_printf("BIP6: Received Virtual-Address-Resolution-ACK.\n"); + if (bbmd6_address_match_self(addr)) { + /* ignore messages from my IPv6 address */ + } else { + function_len = bvlc6_decode_virtual_address_resolution_ack( + pdu, pdu_len, + &vmac_src, &vmac_dst); + if (function_len) { + bbmd6_add_vmac(vmac_src, addr); + } + } + } +} + +/** + * Handler for Address-Resolution + * + * @param addr - BACnet/IPv6 source address any NAK or reply back to. + * @param pdu - The received NPDU+APDU buffer. + * @param pdu_len - How many bytes in NPDU+APDU buffer. + */ +static void bbmd6_address_resolution_handler( + BACNET_IP6_ADDRESS *addr, + uint8_t * pdu, + uint16_t pdu_len) +{ + int function_len = 0; + uint32_t vmac_src = 0; + uint32_t vmac_target = 0; + uint32_t vmac_me = 0; + + if (addr && pdu) { + debug_printf("BIP6: Received Address-Resolution.\n"); + if (bbmd6_address_match_self(addr)) { + /* ignore messages from my IPv6 address */ + } else { + function_len = bvlc6_decode_address_resolution( + pdu, pdu_len, + &vmac_src, &vmac_target); + if (function_len) { + bbmd6_add_vmac(vmac_src, addr); + vmac_me = Device_Object_Instance_Number(); + if (vmac_target == vmac_me) { + /* The Address-Resolution-ACK message is unicast + to the B/IPv6 node that originally initiated + the Address-Resolution message. */ + bvlc6_send_address_resolution_ack( + addr, vmac_me, vmac_src); + } + } + } + } +} + +/** + * Handler for Address-Resolution-ACK + * + * @param addr - BACnet/IPv6 source address any NAK or reply back to. + * @param pdu - The received NPDU+APDU buffer. + * @param pdu_len - How many bytes in NPDU+APDU buffer. + */ +static void bbmd6_address_resolution_ack_handler( + BACNET_IP6_ADDRESS *addr, + uint8_t * pdu, + uint16_t pdu_len) +{ + int function_len = 0; + uint32_t vmac_src = 0; + uint32_t vmac_dst = 0; + + if (addr && pdu) { + debug_printf("BIP6: Received Address-Resolution-ACK.\n"); + if (bbmd6_address_match_self(addr)) { + /* ignore messages from my IPv6 address */ + } else { + function_len = bvlc6_decode_address_resolution_ack( + pdu, pdu_len, + &vmac_src, &vmac_dst); + if (function_len) { + bbmd6_add_vmac(vmac_src, addr); + } + } + } +} + +/** + * Use this handler when you are not a BBMD. + * Sets the BVLC6_Function_Code in case it is needed later. + * + * @param addr - BACnet/IPv6 source address any NAK or reply back to. + * @param src - BACnet style the source address interpreted VMAC + * @param mtu - The received MTU buffer. + * @param mtu_len - How many bytes in MTU buffer. + * + * @return number of bytes offset into the NPDU for APDU, or 0 if handled + */ +static int handler_bbmd6_for_non_bbmd( + BACNET_IP6_ADDRESS *addr, + BACNET_ADDRESS * src, + uint8_t * mtu, + uint16_t mtu_len) +{ + uint16_t result_code = BVLC6_RESULT_SUCCESSFUL_COMPLETION; + uint32_t vmac_src = 0; + uint32_t vmac_dst = 0; + uint8_t message_type = 0; + uint16_t message_length = 0; + int header_len = 0; + int function_len = 0; + uint8_t * pdu = NULL; + uint16_t pdu_len = 0; + uint16_t npdu_len = 0; + bool send_result = false; + uint16_t offset = 0; + BACNET_IP6_ADDRESS fwd_address = {{0}}; + + header_len = bvlc6_decode_header(mtu, mtu_len, &message_type, + &message_length); + if (header_len == 4) { + BVLC6_Function_Code = message_type; + pdu = &mtu[header_len]; + pdu_len = mtu_len - header_len; + switch (BVLC6_Function_Code) { + case BVLC6_RESULT: + function_len = bvlc6_decode_result(pdu, pdu_len, &vmac_src, + &result_code); + if (function_len) { + BVLC6_Result_Code = result_code; + /* The Virtual MAC address table shall be updated + using the respective parameter values of the + incoming messages. */ + bbmd6_add_vmac(vmac_src, addr); + bvlc6_vmac_address_set(src, vmac_src); + debug_printf("BIP6: Received Result Code=%d\n", + BVLC6_Result_Code); + } + break; + case BVLC6_REGISTER_FOREIGN_DEVICE: + result_code = BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK; + send_result = true; + break; + case BVLC6_DELETE_FOREIGN_DEVICE: + result_code = BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK; + send_result = true; + break; + case BVLC6_DISTRIBUTE_BROADCAST_TO_NETWORK: + result_code = BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK; + send_result = true; + break; + case BVLC6_ORIGINAL_UNICAST_NPDU: + /* This message is used to send directed NPDUs to + another B/IPv6 node or router. */ + debug_printf("BIP6: Received Original-Unicast-NPDU.\n"); + if (bbmd6_address_match_self(addr)) { + /* ignore messages from my IPv6 address */ + debug_printf("BIP6: Original-Unicast-NPDU is me!.\n"); + } else { + function_len = bvlc6_decode_original_unicast( + pdu, pdu_len, + &vmac_src, &vmac_dst, + NULL, 0, &npdu_len); + if (function_len) { + if (vmac_dst == Device_Object_Instance_Number()) { + /* The Virtual MAC address table shall be updated + using the respective parameter values of the + incoming messages. */ + bbmd6_add_vmac(vmac_src, addr); + bvlc6_vmac_address_set(src, vmac_src); + offset = header_len + (function_len - npdu_len); + } else { + debug_printf( + "BIP6: Original-Unicast-NPDU: " + "VMAC is not me!\n"); + } + } else { + debug_printf( + "BIP6: Original-Unicast-NPDU: Unable to decode!\n"); + } + } + break; + case BVLC6_ORIGINAL_BROADCAST_NPDU: + debug_printf("BIP6: Received Original-Broadcast-NPDU.\n"); + if (bbmd6_address_match_self(addr)) { + /* ignore messages from my IPv6 address */ + debug_printf("BIP6: Original-Broadcast-NPDU is me!\n"); + } else { + function_len = bvlc6_decode_original_broadcast( + pdu, pdu_len, + &vmac_src, + NULL, 0, &npdu_len); + if (function_len) { + /* The Virtual MAC address table shall be updated + using the respective parameter values of the + incoming messages. */ + bbmd6_add_vmac(vmac_src, addr); + bvlc6_vmac_address_set(src, vmac_src); + offset = header_len + (function_len - npdu_len); + } else { + debug_printf( + "BIP6: Original-Broadcast-NPDU: Unable to decode!\n"); + } + } + break; + case BVLC6_FORWARDED_NPDU: + debug_printf("BIP6: Received Forwarded-NPDU.\n"); + if (bbmd6_address_match_self(addr)) { + /* ignore messages from my IPv6 address */ + debug_printf("BIP6: Forwarded-NPDU is me!\n"); + } else { + function_len = bvlc6_decode_forwarded_npdu( + pdu, pdu_len, + &vmac_src, &fwd_address, + NULL, 0, &npdu_len); + if (function_len) { + /* The Virtual MAC address table shall be updated + using the respective parameter values of the + incoming messages. */ + bbmd6_add_vmac(vmac_src, &fwd_address); + bvlc6_vmac_address_set(src, vmac_src); + offset = header_len + (function_len - npdu_len); + } else { + debug_printf( + "BIP6: Forwarded-NPDU: Unable to decode!\n"); + } + } + break; + case BVLC6_FORWARDED_ADDRESS_RESOLUTION: + result_code = BVLC6_RESULT_ADDRESS_RESOLUTION_NAK; + send_result = true; + break; + case BVLC6_ADDRESS_RESOLUTION: + bbmd6_address_resolution_handler(addr, pdu, pdu_len); + break; + case BVLC6_ADDRESS_RESOLUTION_ACK: + bbmd6_address_resolution_ack_handler(addr, pdu, pdu_len); + break; + case BVLC6_VIRTUAL_ADDRESS_RESOLUTION: + bbmd6_virtual_address_resolution_handler(addr, pdu, pdu_len); + break; + case BVLC6_VIRTUAL_ADDRESS_RESOLUTION_ACK: + bbmd6_virtual_address_resolution_ack_handler(addr, pdu, pdu_len); + break; + case BVLC6_SECURE_BVLL: + break; + default: + break; + } + if (send_result) { + vmac_src = Device_Object_Instance_Number(); + bvlc6_send_result(addr, vmac_src, result_code); + debug_printf("BIP6: sent result code=%d\n", result_code); + } + } + + return offset; +} + +#if defined(BACDL_BIP6) && BBMD6_ENABLED +#warning FIXME: Needs ported to IPv6 +/** + * Use this handler when you are a BBMD. + * Sets the BVLC6_Function_Code in case it is needed later. + * + * @param addr - BACnet/IPv6 source address any NAK or reply back to. + * @param src - BACnet style the source address interpreted VMAC + * @param mtu - The received MTU buffer. + * @param mtu_len - How many bytes in MTU buffer. + * + * @return number of bytes offset into the NPDU for APDU, or 0 if handled + */ +static int handler_bbmd6_for_bbmd( + BACNET_IP6_ADDRESS *addr, + BACNET_ADDRESS * src, + uint8_t * mtu, + uint16_t mtu_len) +{ + uint16_t result_code = BVLC6_RESULT_SUCCESSFUL_COMPLETION; + uint32_t vmac_me = 0; + uint32_t vmac_src = 0; + uint32_t vmac_dst = 0; + uint32_t vmac_target = 0; + uint8_t message_type = 0; + uint16_t message_length = 0; + int header_len = 0; + int function_len = 0; + uint8_t * pdu = NULL; + uint16_t pdu_len = 0; + uint8_t * npdu = NULL; + uint16_t npdu_len = 0; + bool send_result = false; + uint16_t offset = 0; + BACNET_IP6_ADDRESS fwd_address = {{0}}; + + header_len = bvlc6_decode_header(mtu, mtu_len, &message_type, + &message_length); + if (header_len == 4) { + BVLC6_Function_Code = message_type; + pdu = &mtu[header_len]; + pdu_len = mtu_len - header_len; + switch (BVLC6_Function_Code) { + case BVLC6_RESULT: + function_len = bvlc6_decode_result(pdu, pdu_len, &vmac_src, + &result_code); + if (function_len) { + BVLC6_Result_Code = result_code; + /* The Virtual MAC address table shall be updated + using the respective parameter values of the + incoming messages. */ + bbmd6_add_vmac(vmac_src, addr); + bvlc6_vmac_address_set(src, vmac_src); + debug_printf("BIP6: Received Result Code=%d\n", + BVLC6_Result_Code); + } + break; + case BVLC6_REGISTER_FOREIGN_DEVICE: + result_code = BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK; + send_result = true; + break; + case BVLC6_DELETE_FOREIGN_DEVICE: + result_code = BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK; + send_result = true; + break; + case BVLC6_DISTRIBUTE_BROADCAST_TO_NETWORK: + result_code = BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK; + send_result = true; + break; + case BVLC6_ORIGINAL_UNICAST_NPDU: + /* This message is used to send directed NPDUs to + another B/IPv6 node or router. */ + debug_printf("BIP6: Received Original-Unicast-NPDU.\n"); + if (bbmd6_address_match_self(addr)) { + debug_printf("BIP6: Dropped Original-Unicast-NPDU.\n"); + /* ignore messages from my IPv6 address */ + offset = 0; + } else { + function_len = bvlc6_decode_original_unicast( + pdu, pdu_len, + &vmac_src, &vmac_dst, + NULL, 0, &npdu_len); + if (function_len) { + if (vmac_dst == Device_Object_Instance_Number()) { + /* The Virtual MAC address table shall be updated + using the respective parameter values of the + incoming messages. */ + bbmd6_add_vmac(vmac_src, addr); + bvlc6_vmac_address_set(src, vmac_src); + offset = header_len + (function_len - npdu_len); + } + } + } + break; + case BVLC6_ORIGINAL_BROADCAST_NPDU: + debug_printf("BIP6: Received Original-Broadcast-NPDU.\n"); + function_len = bvlc6_decode_original_broadcast( + pdu, pdu_len, + &vmac_src, + NULL, 0, &npdu_len); + if (function_len) { + offset = header_len + (function_len - npdu_len); + npdu = &mtu[offset]; + /* Upon receipt of a BVLL Original-Broadcast-NPDU + message from the local multicast domain, a BBMD + shall construct a BVLL Forwarded-NPDU message and + unicast it to each entry in its BDT. In addition, + the constructed BVLL Forwarded-NPDU message shall + be unicast to each foreign device currently in + the BBMD's FDT */ + BVLC6_Buffer_Len = bvlc6_encode_forwarded_npdu( + &BVLC6_Buffer[0], sizeof(BVLC6_Buffer), + vmac_src, addr, + npdu, npdu_len); + bbmd6_send_pdu_bdt(&BVLC6_Buffer[0], BVLC6_Buffer_Len); + bbmd6_send_pdu_fdt(&BVLC6_Buffer[0], BVLC6_Buffer_Len); + if (!bbmd6_address_match_self(addr)) { + /* The Virtual MAC address table shall be updated + using the respective parameter values of the + incoming messages. */ + bbmd6_add_vmac(vmac_src, addr); + bvlc6_vmac_address_set(src, vmac_src); + } + } + break; + case BVLC6_FORWARDED_NPDU: + debug_printf("BIP6: Received Forwarded-NPDU.\n"); + function_len = bvlc6_decode_forwarded_npdu( + pdu, pdu_len, + &vmac_src, &fwd_address, + NULL, 0, &npdu_len); + if (function_len) { + offset = header_len + (function_len - npdu_len); + npdu = &mtu[offset]; + /* Upon receipt of a BVLL Forwarded-NPDU message + from a BBMD which is in the receiving BBMD's BDT, + a BBMD shall construct a BVLL Forwarded-NPDU and + transmit it via multicast to B/IPv6 devices in the + local multicast domain. */ + BVLC6_Buffer_Len = bvlc6_encode_forwarded_npdu( + &BVLC6_Buffer[0], sizeof(BVLC6_Buffer), + vmac_src, addr, + npdu, npdu_len); + bip6_get_broadcast_addr(&bvlc_dest); + bip6_send_mpdu(&bvlc_dest, &BVLC6_Buffer[0], BVLC6_Buffer_Len); + /* In addition, the constructed BVLL Forwarded-NPDU + message shall be unicast to each foreign device in + the BBMD's FDT. If the BBMD is unable to transmit + the Forwarded-NPDU, or the message was not received + from a BBMD which is in the receiving BBMD's BDT, + no BVLC-Result shall be returned and the message + shall be discarded. */ + bbmd6_send_pdu_fdt(&BVLC6_Buffer[0], BVLC6_Buffer_Len); + if (!bbmd6_address_match_self(addr)) { + /* The Virtual MAC address table shall be updated + using the respective parameter values of the + incoming messages. */ + bbmd6_add_vmac(vmac_src, &fwd_address); + bvlc6_vmac_address_set(src, vmac_src); + } + } + break; + case BVLC6_FORWARDED_ADDRESS_RESOLUTION: + result_code = BVLC6_RESULT_ADDRESS_RESOLUTION_NAK; + send_result = true; + break; + case BVLC6_ADDRESS_RESOLUTION: + bbmd6_address_resolution_handler(addr, pdu, pdu_len); + break; + case BVLC6_ADDRESS_RESOLUTION_ACK: + bbmd6_address_resolution_ack_handler(addr, pdu, pdu_len); + break; + case BVLC6_VIRTUAL_ADDRESS_RESOLUTION: + bbmd6_virtual_address_resolution_handler(addr, pdu, pdu_len); + break; + case BVLC6_VIRTUAL_ADDRESS_RESOLUTION_ACK: + bbmd6_virtual_address_resolution_ack_handler(addr, pdu, pdu_len); + break; + case BVLC6_SECURE_BVLL: + break; + default: + break; + } + if (send_result) { + vmac_src = Device_Object_Instance_Number(); + bvlc6_send_result(addr, vmac_src, result_code); + debug_printf("BIP6: sent result code=%d\n", result_code); + } + } + + return offset; +} +#endif + +/** + * Use this handler for BACnet/IPv6 BVLC + * + * @param addr [in] IPv6 address to send any NAK back to. + * @param src [out] returns the source address + * @param npdu [in] The received buffer. + * @param npdu_len [in] How many bytes in npdu[]. + * + * @return number of bytes offset into the NPDU for APDU, or 0 if handled + */ +int bvlc6_handler( + BACNET_IP6_ADDRESS *addr, + BACNET_ADDRESS * src, + uint8_t * npdu, + uint16_t npdu_len) +{ +#if defined(BACDL_BIP6) && BBMD6_ENABLED + return handler_bbmd6_for_bbmd(addr, src, npdu, npdu_len); +#else + return handler_bbmd6_for_non_bbmd(addr, src, npdu, npdu_len); +#endif +} + +/** Register as a foreign device with the indicated BBMD. + * @param bbmd_address - IPv4 address (long) of BBMD to register with, + * in network byte order. + * @param bbmd_port - Network port of BBMD, in network byte order + * @param time_to_live_seconds - Lease time to use when registering. + * @return Positive number (of bytes sent) on success, + * 0 if no registration request is sent, or + * -1 if registration fails. + */ +int bvlc6_register_with_bbmd( + BACNET_IP6_ADDRESS *bbmd_addr, + uint32_t vmac_src, + uint16_t time_to_live_seconds) +{ + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + + mtu_len = bvlc6_encode_register_foreign_device( + &mtu[0], sizeof(mtu), vmac_src, time_to_live_seconds); + + return bip6_send_mpdu(bbmd_addr, &mtu[0], mtu_len); +} + +/** Returns the last BVLL Result we received, either as the result of a BBMD + * request we sent, or (if not a BBMD or Client), from trying to register + * as a foreign device. + * + * @return BVLC6_RESULT_SUCCESSFUL_COMPLETION on success, + * BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK if registration failed, + * or one of the other codes (if we are a BBMD). + */ +uint16_t bvlc6_get_last_result( + void) +{ + return BVLC6_Result_Code; +} + +/** Returns the current BVLL Function Code we are processing. + * We have to store this higher layer code for when the lower layers + * need to know what it is, especially to differentiate between + * BVLC6_ORIGINAL_UNICAST_NPDU and BVLC6_ORIGINAL_BROADCAST_NPDU. + * + * @return A BVLC6_ code, such as BVLC6_ORIGINAL_UNICAST_NPDU. + */ +uint8_t bvlc6_get_function_code( + void) +{ + return BVLC6_Function_Code; +} + +void bvlc6_init(void) +{ + VMAC_Init(); +} + +#ifdef TEST +#include +#include +#include "ctest.h" +static uint32_t Device_ID = 0; +static uint32_t Test_Device_ID = 12345; +static BACNET_IP6_ADDRESS BIP6_Addr; +static BACNET_IP6_ADDRESS Test_BIP6_Addr; +static BACNET_IP6_ADDRESS BIP6_Broadcast_Addr; +static uint8_t BIP6_MTU_Buffer[MAX_MPDU]; + +/* network stub functions */ +/** + * BACnet/IP Datalink Receive handler. + * + * @param src - returns the source address + * @param npdu - returns the NPDU buffer + * @param max_npdu -maximum size of the NPDU buffer + * @param timeout - number of milliseconds to wait for a packet + * + * @return Number of bytes received, or 0 if none or timeout. + */ +uint16_t bip6_receive( + BACNET_ADDRESS * src, + uint8_t * npdu, + uint16_t max_npdu, + unsigned timeout) +{ + return 0; +} + +/** + * The send function for BACnet/IPv6 driver layer + * + * @param dest - Points to a BACNET_IP6_ADDRESS structure containing the + * destination address. + * @param mtu - the bytes of data to send + * @param mtu_len - the number of bytes of data to send + * + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +int bip6_send_mpdu( + BACNET_IP6_ADDRESS *dest, + uint8_t * mtu, + uint16_t mtu_len) +{ + return 0; +} + +/** Return the Object Instance number for our (single) Device Object. + * This is a key function, widely invoked by the handler code, since + * it provides "our" (ie, local) address. + * + * @return The Instance number used in the BACNET_OBJECT_ID for the Device. + */ +uint32_t Device_Object_Instance_Number( + void) +{ + return Device_ID; +} + +/** + * Get the BACnet/IP address + * + * @return BACnet/IP address + */ +bool bip6_get_addr( + BACNET_IP6_ADDRESS *addr) +{ + return bvlc6_address_copy(addr, &BIP6_Addr); +} + +/** + * Get the BACnet/IP address + * + * @return BACnet/IP address + */ +bool bip6_get_broadcast_addr( + BACNET_IP6_ADDRESS *addr) +{ + return bvlc6_address_copy(addr, &BIP6_Broadcast_Addr); +} + +static void test_BBMD_Result( + Test * pTest) +{ + int result = 0; + uint32_t vmac_src = 0x1234; + uint16_t result_code[6] = { + BVLC6_RESULT_SUCCESSFUL_COMPLETION, + BVLC6_RESULT_ADDRESS_RESOLUTION_NAK, + BVLC6_RESULT_VIRTUAL_ADDRESS_RESOLUTION_NAK, + BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK, + BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK, + BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK + }; + uint16_t test_result_code = 0; + uint8_t test_function_code = 0; + BACNET_IP6_ADDRESS addr; + BACNET_ADDRESS src; + unsigned int i = 0; + uint8_t mtu[MAX_MPDU] = { 0 }; + uint16_t mtu_len = 0; + + bvlc6_address_set(&addr, + BIP6_MULTICAST_LINK_LOCAL, 0, 0, 0, 0, 0, 0, + BIP6_MULTICAST_GROUP_ID); + addr.port = 0xBAC0; + bvlc6_vmac_address_set(&src, vmac_src); + for (i = 0; i < 6; i++) { + mtu_len = bvlc6_encode_result(&mtu[0], sizeof(mtu), + vmac_src, result_code[i]); + result = handler_bbmd6_for_non_bbmd(&addr, &src, &mtu[0], mtu_len); + /* validate that the result is handled (0) */ + ct_test(pTest, result == 0); + test_result_code = bvlc6_get_last_result(); + ct_test(pTest, test_result_code == result_code[i]); + test_function_code = bvlc6_get_function_code(); + ct_test(pTest, test_function_code == BVLC6_RESULT); + } +} + +static void test_BBMD6( + Test * pTest) +{ + bool rc; + + /* individual tests */ + rc = ct_addTestFunction(pTest, test_BBMD_Result); + assert(rc); +} + +#ifdef TEST_BBMD6 +int main( + void) +{ + Test *pTest; + + pTest = ct_create("BACnet Broadcast Management Device IP/v6", NULL); + test_BBMD6(pTest); + /* configure output */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif +#endif + + diff --git a/demo/ptransfer/ptransfer.sln b/demo/ptransfer/ptransfer.sln index aba19789..17dcfeab 100644 --- a/demo/ptransfer/ptransfer.sln +++ b/demo/ptransfer/ptransfer.sln @@ -1,20 +1,20 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual C++ Express 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rdproperty", "rdproperty.vcproj", "{7AC60281-278A-4060-B900-A1343E438701}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7AC60281-278A-4060-B900-A1343E438701}.Debug|Win32.ActiveCfg = Debug|Win32 - {7AC60281-278A-4060-B900-A1343E438701}.Debug|Win32.Build.0 = Debug|Win32 - {7AC60281-278A-4060-B900-A1343E438701}.Release|Win32.ActiveCfg = Release|Win32 - {7AC60281-278A-4060-B900-A1343E438701}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rdproperty", "rdproperty.vcproj", "{7AC60281-278A-4060-B900-A1343E438701}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7AC60281-278A-4060-B900-A1343E438701}.Debug|Win32.ActiveCfg = Debug|Win32 + {7AC60281-278A-4060-B900-A1343E438701}.Debug|Win32.Build.0 = Debug|Win32 + {7AC60281-278A-4060-B900-A1343E438701}.Release|Win32.ActiveCfg = Release|Win32 + {7AC60281-278A-4060-B900-A1343E438701}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/demo/readrange/readrange/readrange.sln b/demo/readrange/readrange/readrange.sln index c80b2c2f..5ad3d204 100644 --- a/demo/readrange/readrange/readrange.sln +++ b/demo/readrange/readrange/readrange.sln @@ -1,20 +1,20 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual C++ Express 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "readrange", "readrange.vcproj", "{6017A2EA-1296-4E67-995F-6344A3CC27C2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Debug|Win32.ActiveCfg = Debug|Win32 - {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Debug|Win32.Build.0 = Debug|Win32 - {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Release|Win32.ActiveCfg = Release|Win32 - {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "readrange", "readrange.vcproj", "{6017A2EA-1296-4E67-995F-6344A3CC27C2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Debug|Win32.ActiveCfg = Debug|Win32 + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Debug|Win32.Build.0 = Debug|Win32 + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Release|Win32.ActiveCfg = Release|Win32 + {6017A2EA-1296-4E67-995F-6344A3CC27C2}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/demo/router-ipv6/Makefile b/demo/router-ipv6/Makefile index e3838abe..cc51df2a 100644 --- a/demo/router-ipv6/Makefile +++ b/demo/router-ipv6/Makefile @@ -1,59 +1,59 @@ -#Makefile to build BACnet Application for the GCC port - -# tools - only if you need them. -# Most platforms have this already defined -# CC = gcc - -# Executable file name -TARGET = bacroute - -TARGET_BIN = ${TARGET}$(TARGET_EXT) - -CFLAGS += -DPRINT_ENABLED=1 - -BACNET_SOURCE_DIR = ../../src -BACNET_HANDLER_DIR = ../handler -BACNET_OBJECT_DIR = ../object - -SRC = main.c \ - $(BACNET_OBJECT_DIR)/netport.c \ - $(BACNET_OBJECT_DIR)/device-client.c - -PORT_BIP6_SRC = \ - $(BACNET_PORT_DIR)/bip6.c \ - $(BACNET_SOURCE_DIR)/bvlc6.c \ - $(BACNET_HANDLER_DIR)/h_bbmd6.c \ - $(BACNET_SOURCE_DIR)/vmac.c - -PORT_BIP_SRC = \ - $(BACNET_PORT_DIR)/bip-init.c \ - $(BACNET_SOURCE_DIR)/bvlc.c \ - $(BACNET_SOURCE_DIR)/bip.c - -SRCS = ${SRC} ${PORT_BIP6_SRC} ${PORT_BIP_SRC} - -OBJS = ${SRCS:.c=.o} - -all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} - -${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} - ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ - size $@ - cp $@ ../../bin - -lib: ${BACNET_LIB_TARGET} - -${BACNET_LIB_TARGET}: - ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) - -.c.o: - ${CC} -c ${CFLAGS} $*.c -o $@ - -depend: - rm -f .depend - ${CC} -MM ${CFLAGS} *.c >> .depend - -clean: - rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map - -include: .depend +#Makefile to build BACnet Application for the GCC port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacroute + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +CFLAGS += -DPRINT_ENABLED=1 + +BACNET_SOURCE_DIR = ../../src +BACNET_HANDLER_DIR = ../handler +BACNET_OBJECT_DIR = ../object + +SRC = main.c \ + $(BACNET_OBJECT_DIR)/netport.c \ + $(BACNET_OBJECT_DIR)/device-client.c + +PORT_BIP6_SRC = \ + $(BACNET_PORT_DIR)/bip6.c \ + $(BACNET_SOURCE_DIR)/bvlc6.c \ + $(BACNET_HANDLER_DIR)/h_bbmd6.c \ + $(BACNET_SOURCE_DIR)/vmac.c + +PORT_BIP_SRC = \ + $(BACNET_PORT_DIR)/bip-init.c \ + $(BACNET_SOURCE_DIR)/bvlc.c \ + $(BACNET_SOURCE_DIR)/bip.c + +SRCS = ${SRC} ${PORT_BIP6_SRC} ${PORT_BIP_SRC} + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/router-ipv6/main.c b/demo/router-ipv6/main.c index 63fdf539..4e580f39 100644 --- a/demo/router-ipv6/main.c +++ b/demo/router-ipv6/main.c @@ -1,1253 +1,1253 @@ -/** -* @file -* @author Steve Karg -* @date 2016 -* @brief Simple BACnet/IP to BACnet/IPv6 router -* -* @section LICENSE -* -* Copyright (C) 2016 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*/ -#include -#include -#include -#include -#include -#include -#include - -#include "bacdef.h" -#include "config.h" -#include "debug.h" -#include "bactext.h" -#include "bacerror.h" -#include "iam.h" -#include "arf.h" -#include "tsm.h" -#include "address.h" -#include "npdu.h" -#include "apdu.h" -#include "client.h" -#include "net.h" -#include "version.h" - -/* our datalink layers */ -#include "bvlc6.h" -#include "bip6.h" -#undef MAX_HEADER -#undef MAX_MPDU -#include "bip.h" -#include "bvlc.h" - -/** -* 6.6.1 Routing Tables -* -* By definition, a router is a device that is connected to at least -* two BACnet networks. Each attachment is through a "port." A -* "routing table" consists of the following information for each port: -* (a) the MAC address of the port's connection to its network; -* (b) the 2-octet network number of the directly connected network; -* (c) a list of network numbers reachable through the port along -* with the MAC address of the next router on the path to each -* network number and the reachability status of each such network. -* -* The "reachability status" is an implementation-dependent value -* that indicates whether the associated network is able to -* receive traffic. The reachability status shall be able to -* distinguish, at a minimum, between "permanent" failures of a route, -* such as might result from the failure of a router, and "temporary" -* unreachability due to the imposition of a congestion control -* restriction. -*/ -typedef struct _dnet { - uint8_t mac[MAX_MAC_LEN]; - uint8_t mac_len; - uint16_t net; - bool enabled; - struct _dnet *dnets; - struct _dnet *next; -} DNET; -/* The list of DNETs that our router can reach. */ -static DNET *Router_Table_Head; -/* track our directly connected ports network number */ -static uint16_t BIP_Net; -static uint16_t BIP6_Net; -/* buffer for receiving packets from the directly connected ports */ -static uint8_t BIP_Rx_Buffer[MAX_MPDU]; -static uint8_t BIP6_Rx_Buffer[MAX_MPDU]; -/* buffer for transmitting from any port */ -static uint8_t Tx_Buffer[MAX_MPDU]; -/* main loop exit control */ -static bool Exit_Requested; - -/** - * Search the router table to find a matching DNET entry - * - * @param net - network number to find a match - * @param addr - address to be filled with remote router address - * - * @return NULL if not found, or a pointer to the directly connected port. - * If addr is not NULL, the DNET entry address is copied to addr - * The caller will need to compare the sought after net with the - * returned port->net to determine if the addr is filled. - */ -static DNET *dnet_find( - uint16_t net, - BACNET_ADDRESS * addr) -{ - - DNET *port = Router_Table_Head; - DNET *dnet = NULL; - unsigned int i = 0; - - while (port != NULL) { - if (net == port->net) { - /* DNET is directly connected to the router */ - return port; - } else if (port->dnets) { - /* search router ports DNET list */ - dnet = port->dnets; - while (dnet != NULL) { - if (net == dnet->net) { - if (addr) { - addr->mac_len = dnet->mac_len; - for (i = 0; i < MAX_MAC_LEN; i++) { - addr->mac[i] = dnet->mac[i]; - } - } - return port; - } - dnet = dnet->next; - } - } - port = port->next; - } - - return NULL; -} - -static bool port_find( - uint16_t snet, - BACNET_ADDRESS *addr) -{ - DNET *port = NULL; - bool found = false; - unsigned int i = 0; - - port = Router_Table_Head; - while (port) { - if (port->net == snet) { - if (addr) { - addr->mac_len = port->mac_len; - for (i = 0; i < MAX_MAC_LEN; i++) { - addr->mac[i] = port->mac[i]; - } - } - found = true; - break; - } - port = port->next; - } - - return found; -} - -/** - * Add a directly connected port to the router table - * - * @param snet - router port SNET - * @param addr - address of port at the net to be added - */ -static void port_add( - uint16_t snet, - BACNET_ADDRESS *addr) -{ - DNET *port = NULL; - DNET *dnet = NULL; - unsigned int i = 0; - - port = dnet_find(snet, NULL); - if (!port) { - port = Router_Table_Head; - if (!port) { - /* create first port */ - port = (DNET *) calloc(1, sizeof(DNET)); - assert(port); - Router_Table_Head = port; - } else { - while (port) { - if (port->next) { - port = port->next; - } else { - /* create next port */ - dnet = (DNET *) calloc(1, sizeof(DNET)); - assert(dnet); - port->next = dnet; - port = port->next; - break; - } - } - } - port->net = snet; - if (addr) { - port->mac_len = addr->mac_len; - for (i = 0; i < MAX_MAC_LEN; i++) { - port->mac[i] = addr->mac[i]; - } - } else { - port->mac_len = 0; - } - port->enabled = true; - } -} - -/** - * Add a route to the router table - * - * @param snet - router port SNET - * @param net - net to be added - * @param addr - address of router at the net to be added - */ -static void dnet_add( - uint16_t snet, - uint16_t net, - BACNET_ADDRESS *addr) -{ - - DNET *dnet = NULL; - DNET *port = NULL; - DNET *prior_dnet = NULL; - unsigned int i = 0; - - /* make sure NETs are not repeated */ - dnet = dnet_find(net, NULL); - if (dnet) { - return; - } - /* start with the source network number table */ - port = dnet_find(snet, NULL); - if (!port) { - return; - } - dnet = port->dnets; - if (dnet == NULL) { - /* first DNET to add */ - dnet = (DNET *) calloc(1, sizeof(DNET)); - assert(dnet); - port->dnets = dnet; - if (addr) { - dnet->mac_len = addr->mac_len; - for (i = 0; i < MAX_MAC_LEN; i++) { - dnet->mac[i] = addr->mac[i]; - } - } - dnet->net = net; - dnet->enabled = true; - dnet->next = NULL; - } else { - while (dnet != NULL) { - if (dnet->net == net) { - /* make sure NETs are not repeated */ - return; - } - prior_dnet = dnet; - dnet = dnet->next; - } - /* next DNET to add */ - dnet = (DNET *) calloc(1, sizeof(DNET)); - if (addr) { - dnet->mac_len = addr->mac_len; - for (i = 0; i < MAX_MAC_LEN; i++) { - dnet->mac[i] = addr->mac[i]; - } - } - dnet->net = net; - dnet->enabled = true; - dnet->next = NULL; - prior_dnet->next = dnet; - } -} - -/** - * Free the DNET data of a route - * - * @param dnets - router info to be freed - */ -static void dnet_cleanup( - DNET * dnets) -{ - DNET *dnet = dnets; - while (dnet != NULL) { - debug_printf("DNET %u removed\n", (unsigned)dnet->net); - dnet = dnet->next; - free(dnets); - dnets = dnet; - } -} - -/** - * Initialize the a data link broadcast address - * - * @param dest - address to be filled with broadcast designator - */ -static void datalink_get_broadcast_address( - BACNET_ADDRESS * dest) -{ - if (dest) { - dest->mac_len = 0; - dest->net = BACNET_BROADCAST_NETWORK; - dest->len = 0; - } - - return; -} - -/** - * function to send a packet out the BACnet/IP and BACnet/IPv6 ports - * - * @param snet - network number of the directly connected port to send - * @param dest - address to where packet is sent - * @param npdu_data - NPCI data to control network destination - * @param pdu - protocol data unit to be sent - * @param pdu_len - number of bytes to send - * - * @return number of bytes sent - */ -static int datalink_send_pdu( - uint16_t snet, - BACNET_ADDRESS * dest, - BACNET_NPDU_DATA * npdu_data, - uint8_t * pdu, - unsigned int pdu_len) -{ - int bytes_sent = 0; - - if (snet == 0) { - debug_printf("BVLC/BVLC6 Send to DNET %u\n", (unsigned)dest->net); - bytes_sent = bvlc_send_pdu(dest, npdu_data, pdu, pdu_len); - bytes_sent = bip6_send_pdu(dest, npdu_data, pdu, pdu_len); - } else if (snet == BIP_Net) { - debug_printf("BVLC Send to DNET %u\n", (unsigned)dest->net); - bytes_sent = bvlc_send_pdu(dest, npdu_data, pdu, pdu_len); - } else if (snet == BIP6_Net) { - debug_printf("BVLC6 Send to DNET %u\n",(unsigned)dest->net); - bytes_sent = bip6_send_pdu(dest, npdu_data, pdu, pdu_len); - } - - return bytes_sent; -} - -/** Initialize an npdu_data structure with given parameters and good defaults, - * and add the Network Layer Message fields. - * The name is a misnomer, as it doesn't do any actual encoding here. - * @see npdu_encode_npdu_data for a simpler version to use when sending an - * APDU instead of a Network Layer Message. - * - * @param npdu_data [out] Returns a filled-out structure with information - * provided by the other arguments and good defaults. - * @param network_message_type [in] The type of Network Layer Message. - * @param data_expecting_reply [in] True if message should have a reply. - * @param priority [in] One of the 4 priorities defined in section 6.2.2, - * like B'11' = Life Safety message - */ -static void npdu_encode_npdu_network( - BACNET_NPDU_DATA * npdu_data, - BACNET_NETWORK_MESSAGE_TYPE network_message_type, - bool data_expecting_reply, - BACNET_MESSAGE_PRIORITY priority) -{ - if (npdu_data) { - npdu_data->data_expecting_reply = data_expecting_reply; - npdu_data->protocol_version = BACNET_PROTOCOL_VERSION; - npdu_data->network_layer_message = true; /* false if APDU */ - npdu_data->network_message_type = network_message_type; /* optional */ - npdu_data->vendor_id = 0; /* optional, if net message type is > 0x80 */ - npdu_data->priority = priority; - npdu_data->hop_count = HOP_COUNT_DEFAULT; - } -} - -/** - * Broadcast an I-am-router-to-network message - * - * @param snet - the directly connected port network number - * @param dnet - the network number we are saying we are a router to. - * If the dnet is 0, send a broadcast out each port with an - * I-Am-Router-To-Network message containing the network - * numbers of each accessible network except the networks - * reachable via the network on which the broadcast is being made. - */ -static void send_i_am_router_to_network(uint16_t snet, uint16_t net) -{ - BACNET_ADDRESS dest; - bool data_expecting_reply = false; - BACNET_NPDU_DATA npdu_data; - int pdu_len = 0; - int len = 0; - DNET *port = NULL; - DNET *dnet = NULL; - - datalink_get_broadcast_address(&dest); - npdu_encode_npdu_network(&npdu_data, - NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, - data_expecting_reply, MESSAGE_PRIORITY_NORMAL); - /* We don't need src information, since a message can't originate from - our downstream BACnet network. */ - pdu_len = - npdu_encode_pdu(&Tx_Buffer[0], &dest, NULL, &npdu_data); - if (net) { - len = encode_unsigned16(&Tx_Buffer[pdu_len], net); - pdu_len += len; - } else { - debug_printf("I-Am-Router-To-Network "); - /* Each router shall broadcast out each port - an I-Am-Router-To-Network message containing the network - numbers of each accessible network except the networks - reachable via the network on which the broadcast is being made. - This enables routers to build or update their routing table - entries for each of the network numbers contained in the message. - */ - port = Router_Table_Head; - while (port != NULL) { - if (port->net != snet) { - debug_printf("%u,", port->net); - len = encode_unsigned16(&Tx_Buffer[pdu_len], - port->net); - pdu_len += len; - dnet = port->dnets; - while (dnet != NULL) { - debug_printf("%u,", dnet->net); - len = encode_unsigned16(&Tx_Buffer[pdu_len], - dnet->net); - pdu_len += len; - dnet = dnet->next; - } - } - port = port->next; - } - debug_printf("from %u\n", snet); - } - datalink_send_pdu(snet, &dest, &npdu_data, &Tx_Buffer[0], pdu_len); -} - -/** Sends our Routing Table, built from our DNET[] array, as an ACK. - * There are two cases here: - * 1) We are responding to a NETWORK_MESSAGE_INIT_RT_TABLE requesting our table. - * We will normally broadcast that response. - * 2) We are ACKing the receipt of a NETWORK_MESSAGE_INIT_RT_TABLE containing a - * routing table, and then we will want to respond to that dst router. - * In that case, DNET[] should just have one entry of -1 (no routing table - * is sent). - * - * @param dest [in] If NULL, Ack will be broadcast to the local BACnet network. - * Optionally may designate a particular router destination, - * especially when ACKing receipt of this message type. - */ -void send_initialize_routing_table_ack( - uint8_t snet, - BACNET_ADDRESS * dst) -{ - BACNET_ADDRESS dest; - bool data_expecting_reply = false; - BACNET_NPDU_DATA npdu_data; - int pdu_len = 0; - int len = 0; - uint8_t count = 0; - uint8_t port_id = 1; - DNET *port = NULL; - - if (dst) { - bacnet_address_copy(&dest, dst); - } else { - datalink_get_broadcast_address(&dest); - } - npdu_encode_npdu_network(&npdu_data, - NETWORK_MESSAGE_INIT_RT_TABLE_ACK, - data_expecting_reply, MESSAGE_PRIORITY_NORMAL); - /* We don't need src information, since a message can't originate from - our downstream BACnet network. */ - pdu_len = - npdu_encode_pdu(&Tx_Buffer[0], &dest, NULL, &npdu_data); - /* First, count the number of Ports we will encode */ - port = Router_Table_Head; - while (port != NULL) { - count++; - port = port->next; - } - Tx_Buffer[pdu_len] = count; - pdu_len++; - if (count > 0) { - /* Now encode each BACNET_ROUTER_PORT. - * We will simply use a positive index for PortID, - * and have no PortInfo. - */ - port = Router_Table_Head; - while (port != NULL) { - len = encode_unsigned16(&Tx_Buffer[pdu_len], port->net); - pdu_len += len; - Tx_Buffer[pdu_len] = port_id; - pdu_len++; - port_id++; - Tx_Buffer[pdu_len] = 0; - pdu_len++; - port = port->next; - } - } - /* Now send the message */ - datalink_send_pdu(snet, &dest, &npdu_data, &Tx_Buffer[0], pdu_len); -} - -/** - * Sends a reject network message - * - * @param snet [in] Which BACnet network orginated the message. - * @param dst [in] If NULL, request will be broadcast to the local BACnet - * network. Otherwise, designates a particular router - * destination. - * @param reject_reason [in] One of the BACNET_NETWORK_REJECT_REASONS codes. - */ -void send_reject_message_to_network( - uint16_t snet, - BACNET_ADDRESS * dst, - uint8_t reject_reason, - uint16_t dnet) -{ - BACNET_ADDRESS dest; - bool data_expecting_reply = false; - BACNET_NPDU_DATA npdu_data; - int pdu_len = 0; - int len = 0; - - if (dst) { - bacnet_address_copy(&dest, dst); - } else { - datalink_get_broadcast_address(&dest); - } - npdu_encode_npdu_network(&npdu_data, - NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK, - data_expecting_reply, MESSAGE_PRIORITY_NORMAL); - /* We don't need src information, since a message can't originate from - our downstream BACnet network. */ - pdu_len = - npdu_encode_pdu(&Tx_Buffer[0], &dest, NULL, &npdu_data); - /* encode the reject reason */ - Tx_Buffer[pdu_len] = reject_reason; - pdu_len++; - if (dnet) { - len = encode_unsigned16(&Tx_Buffer[pdu_len], dnet); - pdu_len += len; - } - /* Now send the message */ - datalink_send_pdu(snet, &dest, &npdu_data, &Tx_Buffer[0], pdu_len); -} - -/** - * Sends a who-is-router-to-network message - * - * @param dnet [in] Which BACnet network we are seeking - */ -static void send_who_is_router_to_network( - uint16_t snet, - uint16_t dnet) -{ - BACNET_ADDRESS dest; - bool data_expecting_reply = false; - BACNET_NPDU_DATA npdu_data; - int pdu_len = 0; - int len = 0; - - datalink_get_broadcast_address(&dest); - npdu_encode_npdu_network(&npdu_data, - NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, - data_expecting_reply, MESSAGE_PRIORITY_NORMAL); - pdu_len = - npdu_encode_pdu(&Tx_Buffer[0], &dest, NULL, &npdu_data); - if (dnet) { - len = encode_unsigned16(&Tx_Buffer[pdu_len], dnet); - pdu_len += len; - } - /* Now send the message to port */ - datalink_send_pdu(snet, &dest, &npdu_data, &Tx_Buffer[0], pdu_len); -} - -/** - * Handler to manage the Who-Is-Router-To-Network Message - * - * 6.6.3.2 Who-Is-Router-To-Network - * - * When a router receives a Who-Is-Router-To-Network - * message specifying a particular network number, - * it shall search its routing table for the network number - * contained in the message. If the specified network number - * is found in its table and the port through which it is - * reachable is not the port from which the - * Who-Is-Router-To-Network message was received, the - * router shall construct an I-Am-Router-To-Network message - * containing the specified network number and send it to the node - * that generated the request using a broadcast MAC address, - * thus allowing other nodes on this network to take - * advantage of the routing information. - * - * If the network number is not found in the routing table, the router - * shall attempt to discover the next router on the path to the - * indicated destination network by generating a Who-Is-Router-To-Network - * message containing the specified destination - * network number and broadcasting it out all its ports other - * than the one from which the Who-Is-Router-To-Network message - * arrived. Two cases are possible. In case one the received - * Who-Is-Router-To-Network message was from the originating - * device. For this case, the router shall add SNET and SADR - * fields before broadcasting the subsequent Who-Is-Router-To- - * Network. This permits an I-Could-Be-Router-To-Network message - * to be directed to the originating device. The second case - * is that the received Who-Is-Router-To-Network message came - * from another router and it already contains SNET and SADR - * fields. For this case, the SNET and SADR shall be retained - * in the newly generated Who-Is-Router-To-Network message. - * - * If the Who-Is-Router-To-Network message does not specify a - * particular destination network number, the router shall - * construct an I-Am-Router-To-Network message containing a - * list of all the networks it is able to reach through other than the - * port from which the Who-Is-Router-To-Network message was - * received and transmit it in the same manner as described - * above. The message shall list all networks not flagged as - * permanently unreachable, including those that are temporarily - * unreachable due to the imposition of congestion control restrictions. - * Networks that may be reachable through a PTP - * connection shall be listed only if the connection is currently established. - * - * @param snet [in] source network port number - * @param src [in] The routing source information, if any. - * If src->net and src->len are 0, there is no routing source information. - * @param npdu_data [in] Contains a filled-out structure with information - * decoded from the NCPI and other NPDU bytes. - * @param npdu [in] Buffer containing the rest of the NPDU, following the - * bytes that have already been decoded. - * @param npdu_len [in] The length of the remaining NPDU message in npdu[]. - */ -static void who_is_router_to_network_handler( - uint16_t snet, - BACNET_ADDRESS * src, - BACNET_NPDU_DATA * npdu_data, - uint8_t * npdu, - uint16_t npdu_len) -{ - DNET *port = NULL; - uint16_t network = 0; - uint16_t len = 0; - - if (npdu) { - if (npdu_len >= 2) { - len += decode_unsigned16(&npdu[len], &network); - port = dnet_find(network, NULL); - if (port) { - /* found in my list! */ - if (port->net != snet) { - /* reachable not through the port this message received */ - send_i_am_router_to_network(snet, network); - } - } else { - /* discover the next router on the path to the network */ - port = Router_Table_Head; - while (port) { - if (port->net != snet) { - send_who_is_router_to_network(port->net, network); - } - port = port->next; - } - } - } else { - send_i_am_router_to_network(snet, 0); - } - } -} - -/** - * Handler to manage the Network Layer Control Messages received in a packet. - * This handler is called if the NCPI bit 7 indicates that this packet is a - * network layer message and there is no further DNET to pass it to. - * The NCPI has already been decoded into the npdu_data structure. - * - * @param snet [in] source network port number - * @param src [in] The routing source information, if any. - * If src->net and src->len are 0, there is no routing source information. - * @param npdu_data [in] Contains a filled-out structure with information - * decoded from the NCPI and other NPDU bytes. - * @param npdu [in] Buffer containing the rest of the NPDU, following the - * bytes that have already been decoded. - * @param npdu_len [in] The length of the remaining NPDU message in npdu[]. - */ -static void network_control_handler( - uint16_t snet, - BACNET_ADDRESS * src, - BACNET_NPDU_DATA * npdu_data, - uint8_t * npdu, - uint16_t npdu_len) -{ - uint16_t npdu_offset = 0; - uint16_t dnet = 0; - uint16_t len = 0; - const char *msg_name = NULL; - - msg_name = bactext_network_layer_msg_name(npdu_data->network_message_type); - fprintf(stderr, "Received %s\n", msg_name); - switch (npdu_data->network_message_type) { - case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: - who_is_router_to_network_handler( - snet, src, npdu_data, npdu, npdu_len); - break; - case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: - /* add its DNETs to our routing table */ - fprintf(stderr, "for Networks: "); - while (npdu_len) { - len = decode_unsigned16(&npdu[npdu_offset], &dnet); - fprintf(stderr, "%hu", dnet); - dnet_add(snet, dnet, src); - npdu_len -= len; - npdu_offset += len; - if (npdu_len) { - fprintf(stderr, ", "); - } - } - fprintf(stderr, ".\n"); - break; - case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK: - /* Do nothing, same as previous case. */ - break; - case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: - if (npdu_len >= 3) { - decode_unsigned16(&npdu[1], &dnet); - fprintf(stderr, "for Network:%hu\n", dnet); - switch (npdu[0]) { - case 0: - fprintf(stderr, "Reason: Other Error.\n"); - break; - case 1: - fprintf(stderr, "Reason: Network unreachable.\n"); - break; - case 2: - fprintf(stderr, "Reason: Network is busy.\n"); - break; - case 3: - fprintf(stderr, "Reason: Unknown network message type.\n"); - break; - case 4: - fprintf(stderr, "Reason: Message too long.\n"); - break; - case 5: - fprintf(stderr, "Reason: Security Error.\n"); - break; - case 6: - fprintf(stderr, "Reason: Invalid address length.\n"); - break; - default: - fprintf(stderr, "Reason: %u\n", (unsigned int)npdu[0]); - break; - } - } - break; - case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: - case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: - /* Do nothing - don't support upstream traffic congestion control */ - break; - case NETWORK_MESSAGE_INIT_RT_TABLE: - /* If sent with Number of Ports == 0, we respond with - * NETWORK_MESSAGE_INIT_RT_TABLE_ACK and a list of all our - * reachable networks. - */ - if (npdu_len > 0) { - /* If Number of Ports is 0, broadcast our "full" table */ - if (npdu[0] == 0) { - send_initialize_routing_table_ack(snet, NULL); - } else { - /* they sent us a list */ - int net_count = npdu[0]; - while (net_count--) { - int i = 1; - /* DNET */ - decode_unsigned16(&npdu[i], &dnet); - /* update routing table */ - dnet_add(snet, dnet, src); - if (npdu[i + 3] > 0) { - /* find next NET value */ - i = npdu[i + 3] + 4; - } else { - i += 4; - } - } - send_initialize_routing_table_ack(snet, NULL); - } - break; - } - break; - case NETWORK_MESSAGE_INIT_RT_TABLE_ACK: - /* Do nothing with the routing table info, since don't support - * upstream traffic congestion control */ - break; - case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK: - case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK: - /* Do nothing - don't support PTP half-router control */ - break; - default: - /* An unrecognized message is bad; send an error response. */ - send_reject_message_to_network(snet, src, - NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE, 0); - break; - } -} - -/** - * Fill the router src address with this port router, router network number, - * and the original src address. - * - * @param router_src [in] The src BACNET_ADDRESS for this routed message. - * @param snet [in] The source network port where the message came from - * @param src [in] The BACNET_ADDRESS of the message's original src. - */ -static void routed_src_address( - BACNET_ADDRESS * router_src, - uint16_t snet, - BACNET_ADDRESS * src) -{ - unsigned int i = 0; - - if (router_src && src) { - /* copy our directly connected port address */ - if (port_find(snet, router_src)) { - if (src->net) { - /* from a router - add router our table */ - dnet_add(snet, src->net, src); - /* the routed address stays the same */ - router_src->net = src->net; - router_src->len = src->len; - for (i = 0; i < MAX_MAC_LEN; i++) { - router_src->adr[i] = src->adr[i]; - } - } else { - /* from our directly connected port */ - router_src->net = snet; - router_src->len = src->mac_len; - for (i = 0; i < MAX_MAC_LEN; i++) { - router_src->adr[i] = src->mac[i]; - } - } - } - } -} - -/** - * If a BACnet NPDU is received with NPCI indicating that the message - * should be relayed by virtue of the presence of a non-broadcast - * DNET, the router shall search its routing table for the indicated - * network number. Normal routing procedures are described in 6.5. - * If, however, the network number cannot be found in the routing - * table or through the use of the Who-Is-Router-To-Network message, - * the router shall generate a Reject-Message-To-Network message and - * send it to the node that originated the BACnet NPDU. - * If the NPCI indicates either a remote or global broadcast, - * the message shall be processed as described in 6.3.2. - * - * @param src [in] The BACNET_ADDRESS of the message's source. - * @param dest [in] The BACNET_ADDRESS of the message's destination. - * @param DNET_list [in] List of our reachable downstream BACnet Network numbers. - * Normally just one valid entry; terminated with a -1 value. - * @param apdu [in] The apdu portion of the request, to be processed. - * @param apdu_len [in] The total (remaining) length of the apdu. - */ -static void routed_apdu_handler( - uint16_t snet, - BACNET_NPDU_DATA * npdu, - BACNET_ADDRESS * src, - BACNET_ADDRESS * dest, - uint8_t * apdu, - uint16_t apdu_len) -{ - DNET *port = NULL; - BACNET_ADDRESS local_dest; - BACNET_ADDRESS remote_dest; - BACNET_ADDRESS router_src; - int npdu_len = 0; - - /* for broadcast messages no search is needed */ - if (dest->net == BACNET_BROADCAST_NETWORK) { - /* A global broadcast, indicated by a DNET of X'FFFF', is sent - to all networks through all routers. Upon receipt of a message - with the global broadcast DNET network number, a router shall - decrement the Hop Count. If the Hop Count is still greater - than zero, then the router shall broadcast the message on all - directly connected networks except the network of origin, using - the broadcast MAC address appropriate for each destination network. - If the Hop Count is zero, then the router shall discard - the message. In order for the message to be disseminated globally, - the originating device shall use a broadcast MAC address - on the originating network so that all attached routers may - receive the message and propagate it further. */ - datalink_get_broadcast_address(&local_dest); - npdu->hop_count--; - routed_src_address(&router_src, snet, src); - /* encode both source and destination for broadcast */ - npdu_len = - npdu_encode_pdu(&Tx_Buffer[0], &local_dest, &router_src, npdu); - memmove(&Tx_Buffer[npdu_len], apdu, apdu_len); - /* send to my other ports */ - debug_printf("Routing a BROADCAST from %u\n", (unsigned)snet); - port = Router_Table_Head; - while (port != NULL) { - if (port->net != snet) { - datalink_send_pdu(port->net, &local_dest, npdu, - &Tx_Buffer[0], npdu_len+apdu_len); - } - port = port->next; - } - return; - } - port = dnet_find(dest->net, &remote_dest); - if (port) { - if (port->net == dest->net) { - debug_printf("Routing to Port %u\n", (unsigned)dest->net); - /* Case 1: the router is directly - connected to the network referred to by DNET. */ - /* In the first case, DNET, DADR, and Hop - Count shall be removed from the NPCI and the message shall be - sent directly to the destination device with DA set equal to - DADR. The control octet shall be adjusted accordingly to - indicate only the presence of SNET and SADR. */ - memmove(&local_dest.mac, dest->adr, MAX_MAC_LEN); - local_dest.mac_len = dest->len; - local_dest.net = 0; - npdu->hop_count--; - routed_src_address(&router_src, snet, src); - npdu_len = - npdu_encode_pdu(&Tx_Buffer[0], &local_dest, &router_src, npdu); - memmove(&Tx_Buffer[npdu_len], apdu, apdu_len); - datalink_send_pdu(port->net, &local_dest, npdu, - &Tx_Buffer[0], npdu_len+apdu_len); - } else { - debug_printf("Routing to another Router %u\n", - (unsigned)remote_dest.net); - /* Case 2: the message must be - relayed to another router for further transmission */ - /* In the second case, if the Hop Count is greater than zero, - the message shall be sent to the next router on the - path to the destination network. - If the Hop Count is zero, then the message shall be - discarded. */ - npdu->hop_count--; - routed_src_address(&router_src, snet, src); - npdu_len = - npdu_encode_pdu(&Tx_Buffer[0], &remote_dest, &router_src, npdu); - memmove(&Tx_Buffer[npdu_len], apdu, apdu_len); - datalink_send_pdu(port->net, &remote_dest, npdu, - &Tx_Buffer[0], npdu_len+apdu_len); - } - } else if (dest->net) { - debug_printf("Routing to Unknown Route %u\n", - (unsigned)dest->net); - /* Case 3: a global broadcast is required. */ - dest->mac_len = 0; - npdu->hop_count--; - /* encode both source and destination */ - routed_src_address(&router_src, snet, src); - npdu_len = - npdu_encode_pdu(&Tx_Buffer[0], dest, &router_src, npdu); - memmove(&Tx_Buffer[npdu_len], apdu, apdu_len); - datalink_send_pdu(port->net, dest, npdu, - &Tx_Buffer[0], npdu_len+apdu_len); - /* If the next router is unknown, an attempt shall be made to - identify it using a Who-Is-Router-To-Network message. */ - send_who_is_router_to_network(0, dest->net); - } -} - -/** - * Handler for the routing packets only - * - * @param src [out] Returned with routing source information if the NPDU - * has any and if this points to non-null storage for it. - * If src->net and src->len are 0 on return, there is no - * routing source information. - * This src describes the original source of the message when - * it had to be routed to reach this BACnet Device, and this - * is passed down into the apdu_handler. - * @param DNET_list [in] List of our reachable downstream BACnet Network - * numbers terminated with a -1 value. - * @param pdu [in] Buffer containing the NPDU and APDU of the received packet. - * @param pdu_len [in] The size of the received message in the pdu[] buffer. - */ -static void my_routing_npdu_handler( - uint16_t snet, - BACNET_ADDRESS * src, - uint8_t * pdu, - uint16_t pdu_len) -{ - int apdu_offset = 0; - BACNET_ADDRESS dest = { 0 }; - BACNET_NPDU_DATA npdu_data = { 0 }; - - if (!pdu) { - /* no packet */ - } else if (pdu[0] == BACNET_PROTOCOL_VERSION) { - apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data); - if (apdu_offset <= 0) { - fprintf(stderr, "NPDU: Decoding failed; Discarded!\n"); - } else if (npdu_data.network_layer_message) { - if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) { - network_control_handler(snet, src, &npdu_data, - &pdu[apdu_offset], (uint16_t) (pdu_len - apdu_offset)); - } else { - /* The DNET is set, but we don't support downstream routers, - * so we just silently drop this network layer message, - * since only routers can handle it (even if for our DNET) */ - } - } else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) { - if ((dest.net == 0) || - (dest.net == BACNET_BROADCAST_NETWORK) || - (npdu_data.hop_count > 1)) { - /* only handle the version that we know how to handle */ - /* and we are not a router, so ignore messages with - routing information cause they are not for us */ - if ((dest.net == BACNET_BROADCAST_NETWORK) && - ((pdu[apdu_offset] & 0xF0) == - PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) { - /* hack for 5.4.5.1 - IDLE */ - /* ConfirmedBroadcastReceived */ - /* then enter IDLE - ignore the PDU */ - } else { - routed_apdu_handler(snet, &npdu_data, src, &dest, - &pdu[apdu_offset], - (uint16_t) (pdu_len - apdu_offset)); - /* add a Device object and application layer */ - if ((dest.net == 0) || - (dest.net == BACNET_BROADCAST_NETWORK)) { - apdu_handler(src, &pdu[apdu_offset], - (uint16_t) (pdu_len - apdu_offset)); - } - } - } else { - fprintf(stderr, "NPDU: DNET=%u. Discarded!\n", - (unsigned) dest.net); - } - } - } else { - /* unsupported protocol version */ - } - - return; -} - -/** - * Initialize the BACnet/IPv6 and BACnet/IP data links - */ -static void datalink_init(void) -{ - char *pEnv = NULL; - BACNET_ADDRESS my_address = {0}; - extern bool BIP_Debug; - - /* BACnet/IP Initialization */ - BIP_Debug = true; - pEnv = getenv("BACNET_IP_PORT"); - if (pEnv) { - bip_set_port(htons((uint16_t) strtol(pEnv, NULL, 0))); - } else { - /* BIP_Port is statically initialized to 0xBAC0, - * so if it is different, then it was programmatically altered, - * and we shouldn't just stomp on it here. - * Unless it is set below 1024, since: - * "The range for well-known ports managed by the IANA is 0-1023." - */ - if (ntohs(bip_get_port()) < 1024) { - bip_set_port(htons(0xBAC0)); - } - } - if (!bip_init(getenv("BACNET_IFACE"))) { - exit(1); - } - atexit(bip_cleanup); - /* BACnet/IPv6 Initialization */ - pEnv = getenv("BACNET_BIP6_PORT"); - if (pEnv) { - bip6_set_port((uint16_t) strtol(pEnv, NULL, 0)); - } - pEnv = getenv("BACNET_BIP6_BROADCAST"); - if (pEnv) { - BACNET_IP6_ADDRESS addr; - bvlc6_address_set(&addr, - (uint16_t) strtol(pEnv, NULL, 0), 0, 0, 0, 0, 0, 0, - BIP6_MULTICAST_GROUP_ID); - bip6_set_broadcast_addr(&addr); - } - if (!bip6_init(getenv("BACNET_BIP6_IFACE"))) { - exit(1); - } - atexit(bip6_cleanup); - /* router network numbers */ - pEnv = getenv("BACNET_IP_NET"); - if (pEnv) { - BIP_Net = strtol(pEnv, NULL, 0); - } else { - BIP_Net = 1; - } - /* configure the first entry in the table - home port */ - bip_get_my_address(&my_address); - port_add(BIP_Net, &my_address); - /* BACnet/IPv6 network */ - pEnv = getenv("BACNET_IP6_NET"); - if (pEnv) { - BIP6_Net = strtol(pEnv, NULL, 0); - } else { - BIP6_Net = 2; - } - /* configure the next entry in the table */ - bip6_get_my_address(&my_address); - port_add(BIP6_Net, &my_address); -} - -/** - * Cleanup memory - * - */ -static void cleanup(void) -{ - DNET *port = NULL; - - fprintf(stderr, "Cleaning up...\n"); - /* clean up the remote networks */ - port = Router_Table_Head; - while (port != NULL) { - dnet_cleanup(port->dnets); - port = port->next; - } - /* clean up the directly connected networks */ - dnet_cleanup(Router_Table_Head); -} - -#if defined(_WIN32) -static BOOL WINAPI CtrlCHandler( - DWORD dwCtrlType) -{ - dwCtrlType = dwCtrlType; - - /* signal to main loop to exit */ - Exit_Requested = true; - while (Exit_Requested) { - Sleep(100); - } - exit(0); - - return TRUE; -} - -void control_c_hooks(void) -{ - SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT); - SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlCHandler, TRUE); -} -#else -static void sig_int( - int signo) -{ - (void) signo; - Exit_Requested = true; - exit(0); -} - -void signal_init( - void) -{ - signal(SIGINT, sig_int); - signal(SIGHUP, sig_int); - signal(SIGTERM, sig_int); -} - -void control_c_hooks(void) -{ - signal_init(); -} -#endif - -/** - * Main function of simple router demo. - * - * @param argc [in] Arg count. - * @param argv [in] Takes one argument: the Device Instance #. - * @return 0 on success. - */ -int main( - int argc, - char *argv[]) -{ - BACNET_ADDRESS src = { - 0 - }; /* address where message came from */ - uint16_t pdu_len = 0; - time_t last_seconds = 0; - time_t current_seconds = 0; - uint32_t elapsed_seconds = 0; - - printf("BACnet Simple IP Router Demo\n"); - printf("BACnet Stack Version %s\n", BACnet_Version); - datalink_init(); - atexit(cleanup); - control_c_hooks(); - /* configure the timeout values */ - last_seconds = time(NULL); - /* broadcast an I-Am on startup */ - printf("BACnet/IP Network: %u\n", (unsigned)BIP_Net); - send_i_am_router_to_network(BIP_Net, 0); - printf("BACnet/IPv6 Network: %u\n", (unsigned)BIP6_Net); - send_i_am_router_to_network(BIP6_Net, 0); - /* loop forever */ - for (;;) { - /* input */ - current_seconds = time(NULL); - /* returns 0 bytes on timeout */ - pdu_len = bvlc_receive(&src, &BIP_Rx_Buffer[0], MAX_MPDU, 5); - /* process */ - if (pdu_len) { - debug_printf("BACnet/IP Received packet\n"); - my_routing_npdu_handler(BIP_Net, &src, &BIP_Rx_Buffer[0], pdu_len); - } - /* returns 0 bytes on timeout */ - pdu_len = bip6_receive(&src, &BIP6_Rx_Buffer[0], MAX_MPDU, 5); - /* process */ - if (pdu_len) { - debug_printf("BACnet/IPv6 Received packet\n"); - my_routing_npdu_handler(BIP6_Net, &src, &BIP6_Rx_Buffer[0], pdu_len); - } - /* at least one second has passed */ - elapsed_seconds = (uint32_t) (current_seconds - last_seconds); - if (elapsed_seconds) { - last_seconds = current_seconds; - bvlc_maintenance_timer(elapsed_seconds); - } - if (Exit_Requested) { - break; - } - } - /* tell signal interrupts we are done */ - Exit_Requested = false; - - return 0; -} +/** +* @file +* @author Steve Karg +* @date 2016 +* @brief Simple BACnet/IP to BACnet/IPv6 router +* +* @section LICENSE +* +* Copyright (C) 2016 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*/ +#include +#include +#include +#include +#include +#include +#include + +#include "bacdef.h" +#include "config.h" +#include "debug.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "client.h" +#include "net.h" +#include "version.h" + +/* our datalink layers */ +#include "bvlc6.h" +#include "bip6.h" +#undef MAX_HEADER +#undef MAX_MPDU +#include "bip.h" +#include "bvlc.h" + +/** +* 6.6.1 Routing Tables +* +* By definition, a router is a device that is connected to at least +* two BACnet networks. Each attachment is through a "port." A +* "routing table" consists of the following information for each port: +* (a) the MAC address of the port's connection to its network; +* (b) the 2-octet network number of the directly connected network; +* (c) a list of network numbers reachable through the port along +* with the MAC address of the next router on the path to each +* network number and the reachability status of each such network. +* +* The "reachability status" is an implementation-dependent value +* that indicates whether the associated network is able to +* receive traffic. The reachability status shall be able to +* distinguish, at a minimum, between "permanent" failures of a route, +* such as might result from the failure of a router, and "temporary" +* unreachability due to the imposition of a congestion control +* restriction. +*/ +typedef struct _dnet { + uint8_t mac[MAX_MAC_LEN]; + uint8_t mac_len; + uint16_t net; + bool enabled; + struct _dnet *dnets; + struct _dnet *next; +} DNET; +/* The list of DNETs that our router can reach. */ +static DNET *Router_Table_Head; +/* track our directly connected ports network number */ +static uint16_t BIP_Net; +static uint16_t BIP6_Net; +/* buffer for receiving packets from the directly connected ports */ +static uint8_t BIP_Rx_Buffer[MAX_MPDU]; +static uint8_t BIP6_Rx_Buffer[MAX_MPDU]; +/* buffer for transmitting from any port */ +static uint8_t Tx_Buffer[MAX_MPDU]; +/* main loop exit control */ +static bool Exit_Requested; + +/** + * Search the router table to find a matching DNET entry + * + * @param net - network number to find a match + * @param addr - address to be filled with remote router address + * + * @return NULL if not found, or a pointer to the directly connected port. + * If addr is not NULL, the DNET entry address is copied to addr + * The caller will need to compare the sought after net with the + * returned port->net to determine if the addr is filled. + */ +static DNET *dnet_find( + uint16_t net, + BACNET_ADDRESS * addr) +{ + + DNET *port = Router_Table_Head; + DNET *dnet = NULL; + unsigned int i = 0; + + while (port != NULL) { + if (net == port->net) { + /* DNET is directly connected to the router */ + return port; + } else if (port->dnets) { + /* search router ports DNET list */ + dnet = port->dnets; + while (dnet != NULL) { + if (net == dnet->net) { + if (addr) { + addr->mac_len = dnet->mac_len; + for (i = 0; i < MAX_MAC_LEN; i++) { + addr->mac[i] = dnet->mac[i]; + } + } + return port; + } + dnet = dnet->next; + } + } + port = port->next; + } + + return NULL; +} + +static bool port_find( + uint16_t snet, + BACNET_ADDRESS *addr) +{ + DNET *port = NULL; + bool found = false; + unsigned int i = 0; + + port = Router_Table_Head; + while (port) { + if (port->net == snet) { + if (addr) { + addr->mac_len = port->mac_len; + for (i = 0; i < MAX_MAC_LEN; i++) { + addr->mac[i] = port->mac[i]; + } + } + found = true; + break; + } + port = port->next; + } + + return found; +} + +/** + * Add a directly connected port to the router table + * + * @param snet - router port SNET + * @param addr - address of port at the net to be added + */ +static void port_add( + uint16_t snet, + BACNET_ADDRESS *addr) +{ + DNET *port = NULL; + DNET *dnet = NULL; + unsigned int i = 0; + + port = dnet_find(snet, NULL); + if (!port) { + port = Router_Table_Head; + if (!port) { + /* create first port */ + port = (DNET *) calloc(1, sizeof(DNET)); + assert(port); + Router_Table_Head = port; + } else { + while (port) { + if (port->next) { + port = port->next; + } else { + /* create next port */ + dnet = (DNET *) calloc(1, sizeof(DNET)); + assert(dnet); + port->next = dnet; + port = port->next; + break; + } + } + } + port->net = snet; + if (addr) { + port->mac_len = addr->mac_len; + for (i = 0; i < MAX_MAC_LEN; i++) { + port->mac[i] = addr->mac[i]; + } + } else { + port->mac_len = 0; + } + port->enabled = true; + } +} + +/** + * Add a route to the router table + * + * @param snet - router port SNET + * @param net - net to be added + * @param addr - address of router at the net to be added + */ +static void dnet_add( + uint16_t snet, + uint16_t net, + BACNET_ADDRESS *addr) +{ + + DNET *dnet = NULL; + DNET *port = NULL; + DNET *prior_dnet = NULL; + unsigned int i = 0; + + /* make sure NETs are not repeated */ + dnet = dnet_find(net, NULL); + if (dnet) { + return; + } + /* start with the source network number table */ + port = dnet_find(snet, NULL); + if (!port) { + return; + } + dnet = port->dnets; + if (dnet == NULL) { + /* first DNET to add */ + dnet = (DNET *) calloc(1, sizeof(DNET)); + assert(dnet); + port->dnets = dnet; + if (addr) { + dnet->mac_len = addr->mac_len; + for (i = 0; i < MAX_MAC_LEN; i++) { + dnet->mac[i] = addr->mac[i]; + } + } + dnet->net = net; + dnet->enabled = true; + dnet->next = NULL; + } else { + while (dnet != NULL) { + if (dnet->net == net) { + /* make sure NETs are not repeated */ + return; + } + prior_dnet = dnet; + dnet = dnet->next; + } + /* next DNET to add */ + dnet = (DNET *) calloc(1, sizeof(DNET)); + if (addr) { + dnet->mac_len = addr->mac_len; + for (i = 0; i < MAX_MAC_LEN; i++) { + dnet->mac[i] = addr->mac[i]; + } + } + dnet->net = net; + dnet->enabled = true; + dnet->next = NULL; + prior_dnet->next = dnet; + } +} + +/** + * Free the DNET data of a route + * + * @param dnets - router info to be freed + */ +static void dnet_cleanup( + DNET * dnets) +{ + DNET *dnet = dnets; + while (dnet != NULL) { + debug_printf("DNET %u removed\n", (unsigned)dnet->net); + dnet = dnet->next; + free(dnets); + dnets = dnet; + } +} + +/** + * Initialize the a data link broadcast address + * + * @param dest - address to be filled with broadcast designator + */ +static void datalink_get_broadcast_address( + BACNET_ADDRESS * dest) +{ + if (dest) { + dest->mac_len = 0; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; + } + + return; +} + +/** + * function to send a packet out the BACnet/IP and BACnet/IPv6 ports + * + * @param snet - network number of the directly connected port to send + * @param dest - address to where packet is sent + * @param npdu_data - NPCI data to control network destination + * @param pdu - protocol data unit to be sent + * @param pdu_len - number of bytes to send + * + * @return number of bytes sent + */ +static int datalink_send_pdu( + uint16_t snet, + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data, + uint8_t * pdu, + unsigned int pdu_len) +{ + int bytes_sent = 0; + + if (snet == 0) { + debug_printf("BVLC/BVLC6 Send to DNET %u\n", (unsigned)dest->net); + bytes_sent = bvlc_send_pdu(dest, npdu_data, pdu, pdu_len); + bytes_sent = bip6_send_pdu(dest, npdu_data, pdu, pdu_len); + } else if (snet == BIP_Net) { + debug_printf("BVLC Send to DNET %u\n", (unsigned)dest->net); + bytes_sent = bvlc_send_pdu(dest, npdu_data, pdu, pdu_len); + } else if (snet == BIP6_Net) { + debug_printf("BVLC6 Send to DNET %u\n",(unsigned)dest->net); + bytes_sent = bip6_send_pdu(dest, npdu_data, pdu, pdu_len); + } + + return bytes_sent; +} + +/** Initialize an npdu_data structure with given parameters and good defaults, + * and add the Network Layer Message fields. + * The name is a misnomer, as it doesn't do any actual encoding here. + * @see npdu_encode_npdu_data for a simpler version to use when sending an + * APDU instead of a Network Layer Message. + * + * @param npdu_data [out] Returns a filled-out structure with information + * provided by the other arguments and good defaults. + * @param network_message_type [in] The type of Network Layer Message. + * @param data_expecting_reply [in] True if message should have a reply. + * @param priority [in] One of the 4 priorities defined in section 6.2.2, + * like B'11' = Life Safety message + */ +static void npdu_encode_npdu_network( + BACNET_NPDU_DATA * npdu_data, + BACNET_NETWORK_MESSAGE_TYPE network_message_type, + bool data_expecting_reply, + BACNET_MESSAGE_PRIORITY priority) +{ + if (npdu_data) { + npdu_data->data_expecting_reply = data_expecting_reply; + npdu_data->protocol_version = BACNET_PROTOCOL_VERSION; + npdu_data->network_layer_message = true; /* false if APDU */ + npdu_data->network_message_type = network_message_type; /* optional */ + npdu_data->vendor_id = 0; /* optional, if net message type is > 0x80 */ + npdu_data->priority = priority; + npdu_data->hop_count = HOP_COUNT_DEFAULT; + } +} + +/** + * Broadcast an I-am-router-to-network message + * + * @param snet - the directly connected port network number + * @param dnet - the network number we are saying we are a router to. + * If the dnet is 0, send a broadcast out each port with an + * I-Am-Router-To-Network message containing the network + * numbers of each accessible network except the networks + * reachable via the network on which the broadcast is being made. + */ +static void send_i_am_router_to_network(uint16_t snet, uint16_t net) +{ + BACNET_ADDRESS dest; + bool data_expecting_reply = false; + BACNET_NPDU_DATA npdu_data; + int pdu_len = 0; + int len = 0; + DNET *port = NULL; + DNET *dnet = NULL; + + datalink_get_broadcast_address(&dest); + npdu_encode_npdu_network(&npdu_data, + NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, + data_expecting_reply, MESSAGE_PRIORITY_NORMAL); + /* We don't need src information, since a message can't originate from + our downstream BACnet network. */ + pdu_len = + npdu_encode_pdu(&Tx_Buffer[0], &dest, NULL, &npdu_data); + if (net) { + len = encode_unsigned16(&Tx_Buffer[pdu_len], net); + pdu_len += len; + } else { + debug_printf("I-Am-Router-To-Network "); + /* Each router shall broadcast out each port + an I-Am-Router-To-Network message containing the network + numbers of each accessible network except the networks + reachable via the network on which the broadcast is being made. + This enables routers to build or update their routing table + entries for each of the network numbers contained in the message. + */ + port = Router_Table_Head; + while (port != NULL) { + if (port->net != snet) { + debug_printf("%u,", port->net); + len = encode_unsigned16(&Tx_Buffer[pdu_len], + port->net); + pdu_len += len; + dnet = port->dnets; + while (dnet != NULL) { + debug_printf("%u,", dnet->net); + len = encode_unsigned16(&Tx_Buffer[pdu_len], + dnet->net); + pdu_len += len; + dnet = dnet->next; + } + } + port = port->next; + } + debug_printf("from %u\n", snet); + } + datalink_send_pdu(snet, &dest, &npdu_data, &Tx_Buffer[0], pdu_len); +} + +/** Sends our Routing Table, built from our DNET[] array, as an ACK. + * There are two cases here: + * 1) We are responding to a NETWORK_MESSAGE_INIT_RT_TABLE requesting our table. + * We will normally broadcast that response. + * 2) We are ACKing the receipt of a NETWORK_MESSAGE_INIT_RT_TABLE containing a + * routing table, and then we will want to respond to that dst router. + * In that case, DNET[] should just have one entry of -1 (no routing table + * is sent). + * + * @param dest [in] If NULL, Ack will be broadcast to the local BACnet network. + * Optionally may designate a particular router destination, + * especially when ACKing receipt of this message type. + */ +void send_initialize_routing_table_ack( + uint8_t snet, + BACNET_ADDRESS * dst) +{ + BACNET_ADDRESS dest; + bool data_expecting_reply = false; + BACNET_NPDU_DATA npdu_data; + int pdu_len = 0; + int len = 0; + uint8_t count = 0; + uint8_t port_id = 1; + DNET *port = NULL; + + if (dst) { + bacnet_address_copy(&dest, dst); + } else { + datalink_get_broadcast_address(&dest); + } + npdu_encode_npdu_network(&npdu_data, + NETWORK_MESSAGE_INIT_RT_TABLE_ACK, + data_expecting_reply, MESSAGE_PRIORITY_NORMAL); + /* We don't need src information, since a message can't originate from + our downstream BACnet network. */ + pdu_len = + npdu_encode_pdu(&Tx_Buffer[0], &dest, NULL, &npdu_data); + /* First, count the number of Ports we will encode */ + port = Router_Table_Head; + while (port != NULL) { + count++; + port = port->next; + } + Tx_Buffer[pdu_len] = count; + pdu_len++; + if (count > 0) { + /* Now encode each BACNET_ROUTER_PORT. + * We will simply use a positive index for PortID, + * and have no PortInfo. + */ + port = Router_Table_Head; + while (port != NULL) { + len = encode_unsigned16(&Tx_Buffer[pdu_len], port->net); + pdu_len += len; + Tx_Buffer[pdu_len] = port_id; + pdu_len++; + port_id++; + Tx_Buffer[pdu_len] = 0; + pdu_len++; + port = port->next; + } + } + /* Now send the message */ + datalink_send_pdu(snet, &dest, &npdu_data, &Tx_Buffer[0], pdu_len); +} + +/** + * Sends a reject network message + * + * @param snet [in] Which BACnet network orginated the message. + * @param dst [in] If NULL, request will be broadcast to the local BACnet + * network. Otherwise, designates a particular router + * destination. + * @param reject_reason [in] One of the BACNET_NETWORK_REJECT_REASONS codes. + */ +void send_reject_message_to_network( + uint16_t snet, + BACNET_ADDRESS * dst, + uint8_t reject_reason, + uint16_t dnet) +{ + BACNET_ADDRESS dest; + bool data_expecting_reply = false; + BACNET_NPDU_DATA npdu_data; + int pdu_len = 0; + int len = 0; + + if (dst) { + bacnet_address_copy(&dest, dst); + } else { + datalink_get_broadcast_address(&dest); + } + npdu_encode_npdu_network(&npdu_data, + NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK, + data_expecting_reply, MESSAGE_PRIORITY_NORMAL); + /* We don't need src information, since a message can't originate from + our downstream BACnet network. */ + pdu_len = + npdu_encode_pdu(&Tx_Buffer[0], &dest, NULL, &npdu_data); + /* encode the reject reason */ + Tx_Buffer[pdu_len] = reject_reason; + pdu_len++; + if (dnet) { + len = encode_unsigned16(&Tx_Buffer[pdu_len], dnet); + pdu_len += len; + } + /* Now send the message */ + datalink_send_pdu(snet, &dest, &npdu_data, &Tx_Buffer[0], pdu_len); +} + +/** + * Sends a who-is-router-to-network message + * + * @param dnet [in] Which BACnet network we are seeking + */ +static void send_who_is_router_to_network( + uint16_t snet, + uint16_t dnet) +{ + BACNET_ADDRESS dest; + bool data_expecting_reply = false; + BACNET_NPDU_DATA npdu_data; + int pdu_len = 0; + int len = 0; + + datalink_get_broadcast_address(&dest); + npdu_encode_npdu_network(&npdu_data, + NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, + data_expecting_reply, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Tx_Buffer[0], &dest, NULL, &npdu_data); + if (dnet) { + len = encode_unsigned16(&Tx_Buffer[pdu_len], dnet); + pdu_len += len; + } + /* Now send the message to port */ + datalink_send_pdu(snet, &dest, &npdu_data, &Tx_Buffer[0], pdu_len); +} + +/** + * Handler to manage the Who-Is-Router-To-Network Message + * + * 6.6.3.2 Who-Is-Router-To-Network + * + * When a router receives a Who-Is-Router-To-Network + * message specifying a particular network number, + * it shall search its routing table for the network number + * contained in the message. If the specified network number + * is found in its table and the port through which it is + * reachable is not the port from which the + * Who-Is-Router-To-Network message was received, the + * router shall construct an I-Am-Router-To-Network message + * containing the specified network number and send it to the node + * that generated the request using a broadcast MAC address, + * thus allowing other nodes on this network to take + * advantage of the routing information. + * + * If the network number is not found in the routing table, the router + * shall attempt to discover the next router on the path to the + * indicated destination network by generating a Who-Is-Router-To-Network + * message containing the specified destination + * network number and broadcasting it out all its ports other + * than the one from which the Who-Is-Router-To-Network message + * arrived. Two cases are possible. In case one the received + * Who-Is-Router-To-Network message was from the originating + * device. For this case, the router shall add SNET and SADR + * fields before broadcasting the subsequent Who-Is-Router-To- + * Network. This permits an I-Could-Be-Router-To-Network message + * to be directed to the originating device. The second case + * is that the received Who-Is-Router-To-Network message came + * from another router and it already contains SNET and SADR + * fields. For this case, the SNET and SADR shall be retained + * in the newly generated Who-Is-Router-To-Network message. + * + * If the Who-Is-Router-To-Network message does not specify a + * particular destination network number, the router shall + * construct an I-Am-Router-To-Network message containing a + * list of all the networks it is able to reach through other than the + * port from which the Who-Is-Router-To-Network message was + * received and transmit it in the same manner as described + * above. The message shall list all networks not flagged as + * permanently unreachable, including those that are temporarily + * unreachable due to the imposition of congestion control restrictions. + * Networks that may be reachable through a PTP + * connection shall be listed only if the connection is currently established. + * + * @param snet [in] source network port number + * @param src [in] The routing source information, if any. + * If src->net and src->len are 0, there is no routing source information. + * @param npdu_data [in] Contains a filled-out structure with information + * decoded from the NCPI and other NPDU bytes. + * @param npdu [in] Buffer containing the rest of the NPDU, following the + * bytes that have already been decoded. + * @param npdu_len [in] The length of the remaining NPDU message in npdu[]. + */ +static void who_is_router_to_network_handler( + uint16_t snet, + BACNET_ADDRESS * src, + BACNET_NPDU_DATA * npdu_data, + uint8_t * npdu, + uint16_t npdu_len) +{ + DNET *port = NULL; + uint16_t network = 0; + uint16_t len = 0; + + if (npdu) { + if (npdu_len >= 2) { + len += decode_unsigned16(&npdu[len], &network); + port = dnet_find(network, NULL); + if (port) { + /* found in my list! */ + if (port->net != snet) { + /* reachable not through the port this message received */ + send_i_am_router_to_network(snet, network); + } + } else { + /* discover the next router on the path to the network */ + port = Router_Table_Head; + while (port) { + if (port->net != snet) { + send_who_is_router_to_network(port->net, network); + } + port = port->next; + } + } + } else { + send_i_am_router_to_network(snet, 0); + } + } +} + +/** + * Handler to manage the Network Layer Control Messages received in a packet. + * This handler is called if the NCPI bit 7 indicates that this packet is a + * network layer message and there is no further DNET to pass it to. + * The NCPI has already been decoded into the npdu_data structure. + * + * @param snet [in] source network port number + * @param src [in] The routing source information, if any. + * If src->net and src->len are 0, there is no routing source information. + * @param npdu_data [in] Contains a filled-out structure with information + * decoded from the NCPI and other NPDU bytes. + * @param npdu [in] Buffer containing the rest of the NPDU, following the + * bytes that have already been decoded. + * @param npdu_len [in] The length of the remaining NPDU message in npdu[]. + */ +static void network_control_handler( + uint16_t snet, + BACNET_ADDRESS * src, + BACNET_NPDU_DATA * npdu_data, + uint8_t * npdu, + uint16_t npdu_len) +{ + uint16_t npdu_offset = 0; + uint16_t dnet = 0; + uint16_t len = 0; + const char *msg_name = NULL; + + msg_name = bactext_network_layer_msg_name(npdu_data->network_message_type); + fprintf(stderr, "Received %s\n", msg_name); + switch (npdu_data->network_message_type) { + case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: + who_is_router_to_network_handler( + snet, src, npdu_data, npdu, npdu_len); + break; + case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: + /* add its DNETs to our routing table */ + fprintf(stderr, "for Networks: "); + while (npdu_len) { + len = decode_unsigned16(&npdu[npdu_offset], &dnet); + fprintf(stderr, "%hu", dnet); + dnet_add(snet, dnet, src); + npdu_len -= len; + npdu_offset += len; + if (npdu_len) { + fprintf(stderr, ", "); + } + } + fprintf(stderr, ".\n"); + break; + case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK: + /* Do nothing, same as previous case. */ + break; + case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: + if (npdu_len >= 3) { + decode_unsigned16(&npdu[1], &dnet); + fprintf(stderr, "for Network:%hu\n", dnet); + switch (npdu[0]) { + case 0: + fprintf(stderr, "Reason: Other Error.\n"); + break; + case 1: + fprintf(stderr, "Reason: Network unreachable.\n"); + break; + case 2: + fprintf(stderr, "Reason: Network is busy.\n"); + break; + case 3: + fprintf(stderr, "Reason: Unknown network message type.\n"); + break; + case 4: + fprintf(stderr, "Reason: Message too long.\n"); + break; + case 5: + fprintf(stderr, "Reason: Security Error.\n"); + break; + case 6: + fprintf(stderr, "Reason: Invalid address length.\n"); + break; + default: + fprintf(stderr, "Reason: %u\n", (unsigned int)npdu[0]); + break; + } + } + break; + case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: + case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: + /* Do nothing - don't support upstream traffic congestion control */ + break; + case NETWORK_MESSAGE_INIT_RT_TABLE: + /* If sent with Number of Ports == 0, we respond with + * NETWORK_MESSAGE_INIT_RT_TABLE_ACK and a list of all our + * reachable networks. + */ + if (npdu_len > 0) { + /* If Number of Ports is 0, broadcast our "full" table */ + if (npdu[0] == 0) { + send_initialize_routing_table_ack(snet, NULL); + } else { + /* they sent us a list */ + int net_count = npdu[0]; + while (net_count--) { + int i = 1; + /* DNET */ + decode_unsigned16(&npdu[i], &dnet); + /* update routing table */ + dnet_add(snet, dnet, src); + if (npdu[i + 3] > 0) { + /* find next NET value */ + i = npdu[i + 3] + 4; + } else { + i += 4; + } + } + send_initialize_routing_table_ack(snet, NULL); + } + break; + } + break; + case NETWORK_MESSAGE_INIT_RT_TABLE_ACK: + /* Do nothing with the routing table info, since don't support + * upstream traffic congestion control */ + break; + case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK: + case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK: + /* Do nothing - don't support PTP half-router control */ + break; + default: + /* An unrecognized message is bad; send an error response. */ + send_reject_message_to_network(snet, src, + NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE, 0); + break; + } +} + +/** + * Fill the router src address with this port router, router network number, + * and the original src address. + * + * @param router_src [in] The src BACNET_ADDRESS for this routed message. + * @param snet [in] The source network port where the message came from + * @param src [in] The BACNET_ADDRESS of the message's original src. + */ +static void routed_src_address( + BACNET_ADDRESS * router_src, + uint16_t snet, + BACNET_ADDRESS * src) +{ + unsigned int i = 0; + + if (router_src && src) { + /* copy our directly connected port address */ + if (port_find(snet, router_src)) { + if (src->net) { + /* from a router - add router our table */ + dnet_add(snet, src->net, src); + /* the routed address stays the same */ + router_src->net = src->net; + router_src->len = src->len; + for (i = 0; i < MAX_MAC_LEN; i++) { + router_src->adr[i] = src->adr[i]; + } + } else { + /* from our directly connected port */ + router_src->net = snet; + router_src->len = src->mac_len; + for (i = 0; i < MAX_MAC_LEN; i++) { + router_src->adr[i] = src->mac[i]; + } + } + } + } +} + +/** + * If a BACnet NPDU is received with NPCI indicating that the message + * should be relayed by virtue of the presence of a non-broadcast + * DNET, the router shall search its routing table for the indicated + * network number. Normal routing procedures are described in 6.5. + * If, however, the network number cannot be found in the routing + * table or through the use of the Who-Is-Router-To-Network message, + * the router shall generate a Reject-Message-To-Network message and + * send it to the node that originated the BACnet NPDU. + * If the NPCI indicates either a remote or global broadcast, + * the message shall be processed as described in 6.3.2. + * + * @param src [in] The BACNET_ADDRESS of the message's source. + * @param dest [in] The BACNET_ADDRESS of the message's destination. + * @param DNET_list [in] List of our reachable downstream BACnet Network numbers. + * Normally just one valid entry; terminated with a -1 value. + * @param apdu [in] The apdu portion of the request, to be processed. + * @param apdu_len [in] The total (remaining) length of the apdu. + */ +static void routed_apdu_handler( + uint16_t snet, + BACNET_NPDU_DATA * npdu, + BACNET_ADDRESS * src, + BACNET_ADDRESS * dest, + uint8_t * apdu, + uint16_t apdu_len) +{ + DNET *port = NULL; + BACNET_ADDRESS local_dest; + BACNET_ADDRESS remote_dest; + BACNET_ADDRESS router_src; + int npdu_len = 0; + + /* for broadcast messages no search is needed */ + if (dest->net == BACNET_BROADCAST_NETWORK) { + /* A global broadcast, indicated by a DNET of X'FFFF', is sent + to all networks through all routers. Upon receipt of a message + with the global broadcast DNET network number, a router shall + decrement the Hop Count. If the Hop Count is still greater + than zero, then the router shall broadcast the message on all + directly connected networks except the network of origin, using + the broadcast MAC address appropriate for each destination network. + If the Hop Count is zero, then the router shall discard + the message. In order for the message to be disseminated globally, + the originating device shall use a broadcast MAC address + on the originating network so that all attached routers may + receive the message and propagate it further. */ + datalink_get_broadcast_address(&local_dest); + npdu->hop_count--; + routed_src_address(&router_src, snet, src); + /* encode both source and destination for broadcast */ + npdu_len = + npdu_encode_pdu(&Tx_Buffer[0], &local_dest, &router_src, npdu); + memmove(&Tx_Buffer[npdu_len], apdu, apdu_len); + /* send to my other ports */ + debug_printf("Routing a BROADCAST from %u\n", (unsigned)snet); + port = Router_Table_Head; + while (port != NULL) { + if (port->net != snet) { + datalink_send_pdu(port->net, &local_dest, npdu, + &Tx_Buffer[0], npdu_len+apdu_len); + } + port = port->next; + } + return; + } + port = dnet_find(dest->net, &remote_dest); + if (port) { + if (port->net == dest->net) { + debug_printf("Routing to Port %u\n", (unsigned)dest->net); + /* Case 1: the router is directly + connected to the network referred to by DNET. */ + /* In the first case, DNET, DADR, and Hop + Count shall be removed from the NPCI and the message shall be + sent directly to the destination device with DA set equal to + DADR. The control octet shall be adjusted accordingly to + indicate only the presence of SNET and SADR. */ + memmove(&local_dest.mac, dest->adr, MAX_MAC_LEN); + local_dest.mac_len = dest->len; + local_dest.net = 0; + npdu->hop_count--; + routed_src_address(&router_src, snet, src); + npdu_len = + npdu_encode_pdu(&Tx_Buffer[0], &local_dest, &router_src, npdu); + memmove(&Tx_Buffer[npdu_len], apdu, apdu_len); + datalink_send_pdu(port->net, &local_dest, npdu, + &Tx_Buffer[0], npdu_len+apdu_len); + } else { + debug_printf("Routing to another Router %u\n", + (unsigned)remote_dest.net); + /* Case 2: the message must be + relayed to another router for further transmission */ + /* In the second case, if the Hop Count is greater than zero, + the message shall be sent to the next router on the + path to the destination network. + If the Hop Count is zero, then the message shall be + discarded. */ + npdu->hop_count--; + routed_src_address(&router_src, snet, src); + npdu_len = + npdu_encode_pdu(&Tx_Buffer[0], &remote_dest, &router_src, npdu); + memmove(&Tx_Buffer[npdu_len], apdu, apdu_len); + datalink_send_pdu(port->net, &remote_dest, npdu, + &Tx_Buffer[0], npdu_len+apdu_len); + } + } else if (dest->net) { + debug_printf("Routing to Unknown Route %u\n", + (unsigned)dest->net); + /* Case 3: a global broadcast is required. */ + dest->mac_len = 0; + npdu->hop_count--; + /* encode both source and destination */ + routed_src_address(&router_src, snet, src); + npdu_len = + npdu_encode_pdu(&Tx_Buffer[0], dest, &router_src, npdu); + memmove(&Tx_Buffer[npdu_len], apdu, apdu_len); + datalink_send_pdu(port->net, dest, npdu, + &Tx_Buffer[0], npdu_len+apdu_len); + /* If the next router is unknown, an attempt shall be made to + identify it using a Who-Is-Router-To-Network message. */ + send_who_is_router_to_network(0, dest->net); + } +} + +/** + * Handler for the routing packets only + * + * @param src [out] Returned with routing source information if the NPDU + * has any and if this points to non-null storage for it. + * If src->net and src->len are 0 on return, there is no + * routing source information. + * This src describes the original source of the message when + * it had to be routed to reach this BACnet Device, and this + * is passed down into the apdu_handler. + * @param DNET_list [in] List of our reachable downstream BACnet Network + * numbers terminated with a -1 value. + * @param pdu [in] Buffer containing the NPDU and APDU of the received packet. + * @param pdu_len [in] The size of the received message in the pdu[] buffer. + */ +static void my_routing_npdu_handler( + uint16_t snet, + BACNET_ADDRESS * src, + uint8_t * pdu, + uint16_t pdu_len) +{ + int apdu_offset = 0; + BACNET_ADDRESS dest = { 0 }; + BACNET_NPDU_DATA npdu_data = { 0 }; + + if (!pdu) { + /* no packet */ + } else if (pdu[0] == BACNET_PROTOCOL_VERSION) { + apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data); + if (apdu_offset <= 0) { + fprintf(stderr, "NPDU: Decoding failed; Discarded!\n"); + } else if (npdu_data.network_layer_message) { + if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) { + network_control_handler(snet, src, &npdu_data, + &pdu[apdu_offset], (uint16_t) (pdu_len - apdu_offset)); + } else { + /* The DNET is set, but we don't support downstream routers, + * so we just silently drop this network layer message, + * since only routers can handle it (even if for our DNET) */ + } + } else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) { + if ((dest.net == 0) || + (dest.net == BACNET_BROADCAST_NETWORK) || + (npdu_data.hop_count > 1)) { + /* only handle the version that we know how to handle */ + /* and we are not a router, so ignore messages with + routing information cause they are not for us */ + if ((dest.net == BACNET_BROADCAST_NETWORK) && + ((pdu[apdu_offset] & 0xF0) == + PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) { + /* hack for 5.4.5.1 - IDLE */ + /* ConfirmedBroadcastReceived */ + /* then enter IDLE - ignore the PDU */ + } else { + routed_apdu_handler(snet, &npdu_data, src, &dest, + &pdu[apdu_offset], + (uint16_t) (pdu_len - apdu_offset)); + /* add a Device object and application layer */ + if ((dest.net == 0) || + (dest.net == BACNET_BROADCAST_NETWORK)) { + apdu_handler(src, &pdu[apdu_offset], + (uint16_t) (pdu_len - apdu_offset)); + } + } + } else { + fprintf(stderr, "NPDU: DNET=%u. Discarded!\n", + (unsigned) dest.net); + } + } + } else { + /* unsupported protocol version */ + } + + return; +} + +/** + * Initialize the BACnet/IPv6 and BACnet/IP data links + */ +static void datalink_init(void) +{ + char *pEnv = NULL; + BACNET_ADDRESS my_address = {0}; + extern bool BIP_Debug; + + /* BACnet/IP Initialization */ + BIP_Debug = true; + pEnv = getenv("BACNET_IP_PORT"); + if (pEnv) { + bip_set_port(htons((uint16_t) strtol(pEnv, NULL, 0))); + } else { + /* BIP_Port is statically initialized to 0xBAC0, + * so if it is different, then it was programmatically altered, + * and we shouldn't just stomp on it here. + * Unless it is set below 1024, since: + * "The range for well-known ports managed by the IANA is 0-1023." + */ + if (ntohs(bip_get_port()) < 1024) { + bip_set_port(htons(0xBAC0)); + } + } + if (!bip_init(getenv("BACNET_IFACE"))) { + exit(1); + } + atexit(bip_cleanup); + /* BACnet/IPv6 Initialization */ + pEnv = getenv("BACNET_BIP6_PORT"); + if (pEnv) { + bip6_set_port((uint16_t) strtol(pEnv, NULL, 0)); + } + pEnv = getenv("BACNET_BIP6_BROADCAST"); + if (pEnv) { + BACNET_IP6_ADDRESS addr; + bvlc6_address_set(&addr, + (uint16_t) strtol(pEnv, NULL, 0), 0, 0, 0, 0, 0, 0, + BIP6_MULTICAST_GROUP_ID); + bip6_set_broadcast_addr(&addr); + } + if (!bip6_init(getenv("BACNET_BIP6_IFACE"))) { + exit(1); + } + atexit(bip6_cleanup); + /* router network numbers */ + pEnv = getenv("BACNET_IP_NET"); + if (pEnv) { + BIP_Net = strtol(pEnv, NULL, 0); + } else { + BIP_Net = 1; + } + /* configure the first entry in the table - home port */ + bip_get_my_address(&my_address); + port_add(BIP_Net, &my_address); + /* BACnet/IPv6 network */ + pEnv = getenv("BACNET_IP6_NET"); + if (pEnv) { + BIP6_Net = strtol(pEnv, NULL, 0); + } else { + BIP6_Net = 2; + } + /* configure the next entry in the table */ + bip6_get_my_address(&my_address); + port_add(BIP6_Net, &my_address); +} + +/** + * Cleanup memory + * + */ +static void cleanup(void) +{ + DNET *port = NULL; + + fprintf(stderr, "Cleaning up...\n"); + /* clean up the remote networks */ + port = Router_Table_Head; + while (port != NULL) { + dnet_cleanup(port->dnets); + port = port->next; + } + /* clean up the directly connected networks */ + dnet_cleanup(Router_Table_Head); +} + +#if defined(_WIN32) +static BOOL WINAPI CtrlCHandler( + DWORD dwCtrlType) +{ + dwCtrlType = dwCtrlType; + + /* signal to main loop to exit */ + Exit_Requested = true; + while (Exit_Requested) { + Sleep(100); + } + exit(0); + + return TRUE; +} + +void control_c_hooks(void) +{ + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT); + SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlCHandler, TRUE); +} +#else +static void sig_int( + int signo) +{ + (void) signo; + Exit_Requested = true; + exit(0); +} + +void signal_init( + void) +{ + signal(SIGINT, sig_int); + signal(SIGHUP, sig_int); + signal(SIGTERM, sig_int); +} + +void control_c_hooks(void) +{ + signal_init(); +} +#endif + +/** + * Main function of simple router demo. + * + * @param argc [in] Arg count. + * @param argv [in] Takes one argument: the Device Instance #. + * @return 0 on success. + */ +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + uint32_t elapsed_seconds = 0; + + printf("BACnet Simple IP Router Demo\n"); + printf("BACnet Stack Version %s\n", BACnet_Version); + datalink_init(); + atexit(cleanup); + control_c_hooks(); + /* configure the timeout values */ + last_seconds = time(NULL); + /* broadcast an I-Am on startup */ + printf("BACnet/IP Network: %u\n", (unsigned)BIP_Net); + send_i_am_router_to_network(BIP_Net, 0); + printf("BACnet/IPv6 Network: %u\n", (unsigned)BIP6_Net); + send_i_am_router_to_network(BIP6_Net, 0); + /* loop forever */ + for (;;) { + /* input */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = bvlc_receive(&src, &BIP_Rx_Buffer[0], MAX_MPDU, 5); + /* process */ + if (pdu_len) { + debug_printf("BACnet/IP Received packet\n"); + my_routing_npdu_handler(BIP_Net, &src, &BIP_Rx_Buffer[0], pdu_len); + } + /* returns 0 bytes on timeout */ + pdu_len = bip6_receive(&src, &BIP6_Rx_Buffer[0], MAX_MPDU, 5); + /* process */ + if (pdu_len) { + debug_printf("BACnet/IPv6 Received packet\n"); + my_routing_npdu_handler(BIP6_Net, &src, &BIP6_Rx_Buffer[0], pdu_len); + } + /* at least one second has passed */ + elapsed_seconds = (uint32_t) (current_seconds - last_seconds); + if (elapsed_seconds) { + last_seconds = current_seconds; + bvlc_maintenance_timer(elapsed_seconds); + } + if (Exit_Requested) { + break; + } + } + /* tell signal interrupts we are done */ + Exit_Requested = false; + + return 0; +} diff --git a/demo/writepropm/Makefile b/demo/writepropm/Makefile index 069152e1..1b6739d4 100644 --- a/demo/writepropm/Makefile +++ b/demo/writepropm/Makefile @@ -1,40 +1,40 @@ -#Makefile to build BACnet Application for the Linux Port - -# tools - only if you need them. -# Most platforms have this already defined -# CC = gcc - -# Executable file name -TARGET = bacwpm - -TARGET_BIN = ${TARGET}$(TARGET_EXT) - -SRCS = main.c \ - ../object/netport.c \ - ../object/device-client.c - -OBJS = ${SRCS:.c=.o} - -all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} - -${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} - ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ - size $@ - cp $@ ../../bin - -lib: ${BACNET_LIB_TARGET} - -${BACNET_LIB_TARGET}: - ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) - -.c.o: - ${CC} -c ${CFLAGS} $*.c -o $@ - -depend: - rm -f .depend - ${CC} -MM ${CFLAGS} *.c >> .depend - -clean: - rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map - -include: .depend +#Makefile to build BACnet Application for the Linux Port + +# tools - only if you need them. +# Most platforms have this already defined +# CC = gcc + +# Executable file name +TARGET = bacwpm + +TARGET_BIN = ${TARGET}$(TARGET_EXT) + +SRCS = main.c \ + ../object/netport.c \ + ../object/device-client.c + +OBJS = ${SRCS:.c=.o} + +all: ${BACNET_LIB_TARGET} Makefile ${TARGET_BIN} + +${TARGET_BIN}: ${OBJS} Makefile ${BACNET_LIB_TARGET} + ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@ + size $@ + cp $@ ../../bin + +lib: ${BACNET_LIB_TARGET} + +${BACNET_LIB_TARGET}: + ( cd ${BACNET_LIB_DIR} ; $(MAKE) clean ; $(MAKE) ) + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -f core ${TARGET_BIN} ${OBJS} ${BACNET_LIB_TARGET} $(TARGET).map + +include: .depend diff --git a/demo/writepropm/main.c b/demo/writepropm/main.c index e7e3a9be..cb93fa8e 100644 --- a/demo/writepropm/main.c +++ b/demo/writepropm/main.c @@ -1,522 +1,522 @@ -/************************************************************************* -* Copyright (C) 2017 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ - -/* command line tool that sends a BACnet service, and displays the reply */ -#include -#include -#include -#include -#include -#include -#include /* for time */ - -#define PRINT_ENABLED 1 - -#include "bacdef.h" -#include "config.h" -#include "bactext.h" -#include "bacerror.h" -#include "iam.h" -#include "arf.h" -#include "tsm.h" -#include "address.h" -#include "npdu.h" -#include "apdu.h" -#include "device.h" -#include "net.h" -#include "datalink.h" -#include "whois.h" -#include "version.h" -/* some demo stuff needed */ -#include "rpm.h" -#include "filename.h" -#include "handlers.h" -#include "client.h" -#include "txbuf.h" -#include "dlenv.h" - -/* buffer used for receive */ -static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; - -/* global variables used in this file */ -static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; -static BACNET_WRITE_ACCESS_DATA *Write_Access_Data; -/* needed to filter incoming messages */ -static uint8_t Request_Invoke_ID = 0; -static BACNET_ADDRESS Target_Address; -/* needed for return value of main application */ -static bool Error_Detected = false; -/* Used for verbose */ -static bool Verbose = false; - -static void MyErrorHandler( - BACNET_ADDRESS * src, - uint8_t invoke_id, - BACNET_ERROR_CLASS error_class, - BACNET_ERROR_CODE error_code) -{ - if (address_match(&Target_Address, src) && - (invoke_id == Request_Invoke_ID)) { - printf("BACnet Error: %s: %s\n", - bactext_error_class_name((int) error_class), - bactext_error_code_name((int) error_code)); - Error_Detected = true; - /* FIXME: WPM error has extra data for first failed write. */ - } -} - -void MyAbortHandler( - BACNET_ADDRESS * src, - uint8_t invoke_id, - uint8_t abort_reason, - bool server) -{ - (void) server; - if (address_match(&Target_Address, src) && - (invoke_id == Request_Invoke_ID)) { - printf("BACnet Abort: %s\n", - bactext_abort_reason_name((int) abort_reason)); - Error_Detected = true; - } -} - -void MyRejectHandler( - BACNET_ADDRESS * src, - uint8_t invoke_id, - uint8_t reject_reason) -{ - /* FIXME: verify src and invoke id */ - if (address_match(&Target_Address, src) && - (invoke_id == Request_Invoke_ID)) { - printf("BACnet Reject: %s\n", - bactext_reject_reason_name((int) reject_reason)); - Error_Detected = true; - } -} - -void MyWritePropertyMultipleSimpleAckHandler( - BACNET_ADDRESS * src, - uint8_t invoke_id) -{ - if (address_match(&Target_Address, src) && - (invoke_id == Request_Invoke_ID)) { - printf("\nWriteProperty Acknowledged!\n"); - } -} - -static void Init_Service_Handlers( - void) -{ - Device_Init(NULL); - /* we need to handle who-is - to support dynamic device binding to us */ - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); - /* handle i-am to support binding to other devices */ - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); - /* set the handler for all the services we don't implement - It is required to send the proper reject message... */ - apdu_set_unrecognized_service_handler_handler - (handler_unrecognized_service); - /* we must implement read property - it's required! */ - apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, - handler_read_property); - /* handle the data coming back from confirmed requests */ - apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, - MyWritePropertyMultipleSimpleAckHandler); - /* handle any errors coming back */ - apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); - apdu_set_abort_handler(MyAbortHandler); - apdu_set_reject_handler(MyRejectHandler); -} - -void cleanup( - void) -{ - BACNET_WRITE_ACCESS_DATA *wpm_object; - BACNET_WRITE_ACCESS_DATA *old_wpm_object; - BACNET_PROPERTY_VALUE *wpm_property; - BACNET_PROPERTY_VALUE *old_wpm_property; - - wpm_object = Write_Access_Data; - old_wpm_object = wpm_object; - while (wpm_object) { - wpm_property = wpm_object->listOfProperties; - while (wpm_property) { - old_wpm_property = wpm_property; - wpm_property = wpm_property->next; - free(old_wpm_property); - } - old_wpm_object = wpm_object; - wpm_object = wpm_object->next; - free(old_wpm_object); - } -} - -static void print_usage(char *filename) -{ - printf("Usage: %s device-instance object-type object-instance " - "property[index] priority tag value [property[index] priority tag value]\n", - filename); - printf(" [--version][--help]\n"); -} - -static void print_help(char *filename) -{ - printf("Write one or more properties to one or more objects\n" - "in a BACnet device.\n" - "device-instance:\n" - "BACnet Device Object Instance number that you are\n" - "trying to communicate to. This number will be used\n" - "to try and bind with the device using Who-Is and\n" - "I-Am services. For example, if you were writing\n" - "Device Object 123, the device-instance would be 123.\n" - "\nobject-type:\n" - "The object type is the integer value of the enumeration\n" - "BACNET_OBJECT_TYPE in bacenum.h. It is the object\n" - "that you are writing. For example if you were\n" - "writing Analog Output 2, the object-type would be 1.\n" - "\nobject-instance:\n" - "This is the object instance number of the object that\n" - "you are writing. For example, if you were writing\n" - "Analog Output 2, the object-instance would be 2.\n" - "\nproperty:\n" - "The property is an integer value of the enumeration\n" - "BACNET_PROPERTY_ID in bacenum.h. It is the property\n" - "you are writing. For example, if you were writing the\n" - "Present Value property, use 85 as the property.\n" - "priority:\n" - "This parameter is used for setting the priority of the\n" - "write. If Priority 0 is given, no priority is sent. The BACnet \n" - "standard states that the value is written at the lowest \n" - "priority (16) if the object property supports priorities\n" - "when no priority is sent.\n" "\n" - "index\n" - "This integer parameter is the index number of an array.\n" - "If the property is an array, individual elements can be written\n" - "to if supported. If this parameter is -1, the index is ignored.\n" - "\n" "tag:\n" - "Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \n" - "in bacenum.h. It is the data type of the value that you are\n" - "writing. For example, if you were writing a REAL value, you would \n" - "use a tag of 4.\n" - "Context tags are created using two tags in a row. The context tag\n" - "is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL.\n" - "\n" "value:\n" - "The value is an ASCII representation of some type of data that you\n" - "are writing. It is encoded using the tag information provided. For\n" - "example, if you were writing a REAL value of 100.0, you would use \n" - "100.0 as the value.\n" "\n" - "Here is a brief overview of BACnet property and tags:\n" - "Certain properties are expected to be written with certain \n" - "application tags, so you probably need to know which ones to use\n" - "with each property of each object. It is almost safe to say that\n" - "given a property and an object and a table, the tag could be looked\n" - "up automatically. There may be a few exceptions to this, such as\n" - "the Any property type in the schedule object and the Present Value\n" - "accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\n" - "the demo to use this kind of table - but I also wanted to be able\n" - "to do negative testing by passing the wrong tag and have the server\n" - "return a reject message.\n\n" ); - printf("Example:\n" - "If you want send a value of 100 to the Present-Value in\n" - "Analog Output 44 and 45 of Device 123 at priority 16,\n" - "send the following command:\n" - "%s 123 1 44 85 16 4 100 1 45 85 16 4 100\n", - filename); -} - -int main( - int argc, - char *argv[]) -{ - BACNET_ADDRESS src = { - 0 - }; /* address where message came from */ - uint16_t pdu_len = 0; - unsigned timeout = 100; /* milliseconds */ - unsigned max_apdu = 0; - int args_remaining = 0, tag_value_arg = 0, arg_sets = 0; - time_t elapsed_seconds = 0; - time_t last_seconds = 0; - time_t current_seconds = 0; - time_t timeout_seconds = 0; - bool found = false; - uint8_t buffer[MAX_PDU] = { - 0 - }; - BACNET_WRITE_ACCESS_DATA *wpm_object; - BACNET_PROPERTY_VALUE *wpm_property; - char *value_string = NULL; - bool status = false; - BACNET_APPLICATION_TAG property_tag; - uint8_t context_tag = 0; - unsigned property_id = 0; - unsigned property_array_index = 0; - int scan_count = 0; - int argi = 0; - char *filename = NULL; - - filename = filename_remove_path(argv[0]); - for (argi = 1; argi < argc; argi++) { - if (strcmp(argv[argi], "--help") == 0) { - print_usage(filename); - print_help(filename); - return 0; - } - if (strcmp(argv[argi], "--version") == 0) { - printf("%s %s\n", filename, BACNET_VERSION_TEXT); - printf("Copyright (C) 2017 by Steve Karg and others.\n" - "This is free software; see the source for copying conditions.\n" - "There is NO warranty; not even for MERCHANTABILITY or\n" - "FITNESS FOR A PARTICULAR PURPOSE.\n"); - return 0; - } - } - if (argc < 9) { - print_usage(filename); - return 0; - } - /* decode the command line parameters */ - Target_Device_Object_Instance = strtol(argv[1], NULL, 0); - if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { - fprintf(stderr, "device-instance=%u - it must be less than %u\n", - Target_Device_Object_Instance, BACNET_MAX_INSTANCE); - return 1; - } - atexit(cleanup); - Write_Access_Data = calloc(1, sizeof(BACNET_WRITE_ACCESS_DATA)); - wpm_object = Write_Access_Data; - args_remaining = (argc - 2); - arg_sets = 0; - while (wpm_object) { - tag_value_arg = 2 + (arg_sets * 6); - wpm_object->object_type = strtol(argv[tag_value_arg], NULL, 0); - tag_value_arg++; - args_remaining--; - if (Verbose) { - printf("object-type=%u\n", wpm_object->object_type); - } - if (args_remaining <= 0) { - fprintf(stderr, "Error: not enough object property quadruples.\n"); - return 1; - } - if (wpm_object->object_type >= MAX_BACNET_OBJECT_TYPE) { - fprintf(stderr, "object-type=%u - it must be less than %u\n", - wpm_object->object_type, MAX_BACNET_OBJECT_TYPE); - return 1; - } - wpm_object->object_instance = strtol(argv[tag_value_arg], NULL, 0); - tag_value_arg++; - args_remaining--; - if (Verbose) { - printf("object-instance=%u\n", wpm_object->object_instance); - } - if (args_remaining <= 0) { - fprintf(stderr, "Error: not enough object property quadruples.\n"); - return 1; - } - if (wpm_object->object_instance > BACNET_MAX_INSTANCE) { - fprintf(stderr, "object-instance=%u - it must be less than %u\n", - wpm_object->object_instance, BACNET_MAX_INSTANCE + 1); - return 1; - } - do { - wpm_property = calloc(1, sizeof(BACNET_PROPERTY_VALUE)); - wpm_object->listOfProperties = wpm_property; - if (wpm_property) { - /* Property[index] */ - scan_count = - sscanf(argv[tag_value_arg], "%u[%u]", &property_id, - &property_array_index); - tag_value_arg++; - args_remaining--; - if (scan_count > 0) { - wpm_property->propertyIdentifier = property_id; - if (Verbose) { - printf("property-identifier=%u, array-index=%u\n", - property_id, property_array_index); - } - if (wpm_property->propertyIdentifier > MAX_BACNET_PROPERTY_ID) { - fprintf(stderr, - "property=%u - it must be less than %u\n", - wpm_property->propertyIdentifier, - MAX_BACNET_PROPERTY_ID + 1); - return 1; - } - } - if (scan_count > 1) { - wpm_property->propertyArrayIndex = property_array_index; - } else { - wpm_property->propertyArrayIndex = BACNET_ARRAY_ALL; - } - if (args_remaining <= 0) { - fprintf(stderr, - "Error: missing priority and tag value pair.\n"); - return 1; - } - /* Priority */ - wpm_property->priority = (uint8_t) - strtol(argv[tag_value_arg], NULL, 0); - tag_value_arg++; - args_remaining--; - if (Verbose) { - printf("priority=%u\n", wpm_property->priority); - } - if (args_remaining <= 0) { - fprintf(stderr, - "Error: missing tag value pair.\n"); - return 1; - } - /* Tag + Value */ - /* special case for context tagged values */ - if (toupper(argv[tag_value_arg][0]) == 'C') { - context_tag = (uint8_t) strtol(&argv[tag_value_arg][1], NULL, 0); - tag_value_arg++; - args_remaining--; - wpm_property->value.context_tag = context_tag; - wpm_property->value.context_specific = true; - } else { - wpm_property->value.context_specific = false; - } - property_tag = strtol(argv[tag_value_arg], NULL, 0); - tag_value_arg++; - args_remaining--; - if (args_remaining <= 0) { - fprintf(stderr, "Error: missing value for tag-value pair\n"); - return 1; - } - value_string = argv[tag_value_arg]; - tag_value_arg++; - args_remaining--; - if (Verbose) { - printf("tag=%u value=%s\n", property_tag, value_string); - } - if (property_tag >= MAX_BACNET_APPLICATION_TAG) { - fprintf(stderr, "Error: tag=%u - it must be less than %u\n", - property_tag, MAX_BACNET_APPLICATION_TAG); - return 1; - } - status = - bacapp_parse_application_data(property_tag, value_string, - &wpm_property->value); - if (!status) { - /* FIXME: show the expected entry format for the tag */ - fprintf(stderr, "Error: unable to parse the tag value\n"); - return 1; - } - wpm_property->value.next = NULL; - /* we only support a single property value */ - break; - } - } while (wpm_property); - if (args_remaining) { - arg_sets++; - wpm_object->next = calloc(1, sizeof(BACNET_WRITE_ACCESS_DATA)); - wpm_object = wpm_object->next; - } else { - break; - } - } - /* setup my info */ - Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); - address_init(); - Init_Service_Handlers(); - dlenv_init(); - atexit(datalink_cleanup); - /* configure the timeout values */ - last_seconds = time(NULL); - timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); - /* try to bind with the device */ - found = - address_bind_request(Target_Device_Object_Instance, &max_apdu, - &Target_Address); - if (found) { - if (Verbose) { - printf("Found Device %u in address_cache.\n", - Target_Device_Object_Instance); - } - } else { - Send_WhoIs(Target_Device_Object_Instance, - Target_Device_Object_Instance); - } - /* loop forever */ - for (;;) { - /* increment timer - exit if timed out */ - current_seconds = time(NULL); - - /* at least one second has passed */ - if (current_seconds != last_seconds) - tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); - if (Error_Detected) - break; - /* wait until the device is bound, or timeout and quit */ - if (!found) { - found = - address_bind_request(Target_Device_Object_Instance, &max_apdu, - &Target_Address); - } - if (found) { - if (Request_Invoke_ID == 0) { - if (Verbose) { - printf("Sending WritePropertyMultiple to Device %u.\n", - Target_Device_Object_Instance); - } - Request_Invoke_ID = - Send_Write_Property_Multiple_Request(&buffer[0], - sizeof(buffer), Target_Device_Object_Instance, - Write_Access_Data); - } else if (tsm_invoke_id_free(Request_Invoke_ID)) { - break; - } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { - fprintf(stderr, "\rError: TSM Timeout!\n"); - tsm_free_invoke_id(Request_Invoke_ID); - Error_Detected = true; - /* try again or abort? */ - break; - } - } else { - /* increment timer - exit if timed out */ - elapsed_seconds += (current_seconds - last_seconds); - if (elapsed_seconds > timeout_seconds) { - printf("\rError: APDU Timeout!\n"); - Error_Detected = true; - break; - } - } - /* returns 0 bytes on timeout */ - pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); - /* process */ - if (pdu_len) { - npdu_handler(&src, &Rx_Buf[0], pdu_len); - } - - /* keep track of time for next check */ - last_seconds = current_seconds; - } - if (Error_Detected) { - return 1; - } - - return 0; -} +/************************************************************************* +* Copyright (C) 2017 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* command line tool that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include +#include +#include /* for time */ + +#define PRINT_ENABLED 1 + +#include "bacdef.h" +#include "config.h" +#include "bactext.h" +#include "bacerror.h" +#include "iam.h" +#include "arf.h" +#include "tsm.h" +#include "address.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "whois.h" +#include "version.h" +/* some demo stuff needed */ +#include "rpm.h" +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" +#include "dlenv.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* global variables used in this file */ +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_WRITE_ACCESS_DATA *Write_Access_Data; +/* needed to filter incoming messages */ +static uint8_t Request_Invoke_ID = 0; +static BACNET_ADDRESS Target_Address; +/* needed for return value of main application */ +static bool Error_Detected = false; +/* Used for verbose */ +static bool Verbose = false; + +static void MyErrorHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + BACNET_ERROR_CLASS error_class, + BACNET_ERROR_CODE error_code) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Error: %s: %s\n", + bactext_error_class_name((int) error_class), + bactext_error_code_name((int) error_code)); + Error_Detected = true; + /* FIXME: WPM error has extra data for first failed write. */ + } +} + +void MyAbortHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t abort_reason, + bool server) +{ + (void) server; + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Abort: %s\n", + bactext_abort_reason_name((int) abort_reason)); + Error_Detected = true; + } +} + +void MyRejectHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id, + uint8_t reject_reason) +{ + /* FIXME: verify src and invoke id */ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("BACnet Reject: %s\n", + bactext_reject_reason_name((int) reject_reason)); + Error_Detected = true; + } +} + +void MyWritePropertyMultipleSimpleAckHandler( + BACNET_ADDRESS * src, + uint8_t invoke_id) +{ + if (address_match(&Target_Address, src) && + (invoke_id == Request_Invoke_ID)) { + printf("\nWriteProperty Acknowledged!\n"); + } +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); + /* set the handler for all the services we don't implement + It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* we must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, + MyWritePropertyMultipleSimpleAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +void cleanup( + void) +{ + BACNET_WRITE_ACCESS_DATA *wpm_object; + BACNET_WRITE_ACCESS_DATA *old_wpm_object; + BACNET_PROPERTY_VALUE *wpm_property; + BACNET_PROPERTY_VALUE *old_wpm_property; + + wpm_object = Write_Access_Data; + old_wpm_object = wpm_object; + while (wpm_object) { + wpm_property = wpm_object->listOfProperties; + while (wpm_property) { + old_wpm_property = wpm_property; + wpm_property = wpm_property->next; + free(old_wpm_property); + } + old_wpm_object = wpm_object; + wpm_object = wpm_object->next; + free(old_wpm_object); + } +} + +static void print_usage(char *filename) +{ + printf("Usage: %s device-instance object-type object-instance " + "property[index] priority tag value [property[index] priority tag value]\n", + filename); + printf(" [--version][--help]\n"); +} + +static void print_help(char *filename) +{ + printf("Write one or more properties to one or more objects\n" + "in a BACnet device.\n" + "device-instance:\n" + "BACnet Device Object Instance number that you are\n" + "trying to communicate to. This number will be used\n" + "to try and bind with the device using Who-Is and\n" + "I-Am services. For example, if you were writing\n" + "Device Object 123, the device-instance would be 123.\n" + "\nobject-type:\n" + "The object type is the integer value of the enumeration\n" + "BACNET_OBJECT_TYPE in bacenum.h. It is the object\n" + "that you are writing. For example if you were\n" + "writing Analog Output 2, the object-type would be 1.\n" + "\nobject-instance:\n" + "This is the object instance number of the object that\n" + "you are writing. For example, if you were writing\n" + "Analog Output 2, the object-instance would be 2.\n" + "\nproperty:\n" + "The property is an integer value of the enumeration\n" + "BACNET_PROPERTY_ID in bacenum.h. It is the property\n" + "you are writing. For example, if you were writing the\n" + "Present Value property, use 85 as the property.\n" + "priority:\n" + "This parameter is used for setting the priority of the\n" + "write. If Priority 0 is given, no priority is sent. The BACnet \n" + "standard states that the value is written at the lowest \n" + "priority (16) if the object property supports priorities\n" + "when no priority is sent.\n" "\n" + "index\n" + "This integer parameter is the index number of an array.\n" + "If the property is an array, individual elements can be written\n" + "to if supported. If this parameter is -1, the index is ignored.\n" + "\n" "tag:\n" + "Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \n" + "in bacenum.h. It is the data type of the value that you are\n" + "writing. For example, if you were writing a REAL value, you would \n" + "use a tag of 4.\n" + "Context tags are created using two tags in a row. The context tag\n" + "is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL.\n" + "\n" "value:\n" + "The value is an ASCII representation of some type of data that you\n" + "are writing. It is encoded using the tag information provided. For\n" + "example, if you were writing a REAL value of 100.0, you would use \n" + "100.0 as the value.\n" "\n" + "Here is a brief overview of BACnet property and tags:\n" + "Certain properties are expected to be written with certain \n" + "application tags, so you probably need to know which ones to use\n" + "with each property of each object. It is almost safe to say that\n" + "given a property and an object and a table, the tag could be looked\n" + "up automatically. There may be a few exceptions to this, such as\n" + "the Any property type in the schedule object and the Present Value\n" + "accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\n" + "the demo to use this kind of table - but I also wanted to be able\n" + "to do negative testing by passing the wrong tag and have the server\n" + "return a reject message.\n\n" ); + printf("Example:\n" + "If you want send a value of 100 to the Present-Value in\n" + "Analog Output 44 and 45 of Device 123 at priority 16,\n" + "send the following command:\n" + "%s 123 1 44 85 16 4 100 1 45 85 16 4 100\n", + filename); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { + 0 + }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned max_apdu = 0; + int args_remaining = 0, tag_value_arg = 0, arg_sets = 0; + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + bool found = false; + uint8_t buffer[MAX_PDU] = { + 0 + }; + BACNET_WRITE_ACCESS_DATA *wpm_object; + BACNET_PROPERTY_VALUE *wpm_property; + char *value_string = NULL; + bool status = false; + BACNET_APPLICATION_TAG property_tag; + uint8_t context_tag = 0; + unsigned property_id = 0; + unsigned property_array_index = 0; + int scan_count = 0; + int argi = 0; + char *filename = NULL; + + filename = filename_remove_path(argv[0]); + for (argi = 1; argi < argc; argi++) { + if (strcmp(argv[argi], "--help") == 0) { + print_usage(filename); + print_help(filename); + return 0; + } + if (strcmp(argv[argi], "--version") == 0) { + printf("%s %s\n", filename, BACNET_VERSION_TEXT); + printf("Copyright (C) 2017 by Steve Karg and others.\n" + "This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or\n" + "FITNESS FOR A PARTICULAR PURPOSE.\n"); + return 0; + } + } + if (argc < 9) { + print_usage(filename); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\n", + Target_Device_Object_Instance, BACNET_MAX_INSTANCE); + return 1; + } + atexit(cleanup); + Write_Access_Data = calloc(1, sizeof(BACNET_WRITE_ACCESS_DATA)); + wpm_object = Write_Access_Data; + args_remaining = (argc - 2); + arg_sets = 0; + while (wpm_object) { + tag_value_arg = 2 + (arg_sets * 6); + wpm_object->object_type = strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (Verbose) { + printf("object-type=%u\n", wpm_object->object_type); + } + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough object property quadruples.\n"); + return 1; + } + if (wpm_object->object_type >= MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\n", + wpm_object->object_type, MAX_BACNET_OBJECT_TYPE); + return 1; + } + wpm_object->object_instance = strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (Verbose) { + printf("object-instance=%u\n", wpm_object->object_instance); + } + if (args_remaining <= 0) { + fprintf(stderr, "Error: not enough object property quadruples.\n"); + return 1; + } + if (wpm_object->object_instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\n", + wpm_object->object_instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + do { + wpm_property = calloc(1, sizeof(BACNET_PROPERTY_VALUE)); + wpm_object->listOfProperties = wpm_property; + if (wpm_property) { + /* Property[index] */ + scan_count = + sscanf(argv[tag_value_arg], "%u[%u]", &property_id, + &property_array_index); + tag_value_arg++; + args_remaining--; + if (scan_count > 0) { + wpm_property->propertyIdentifier = property_id; + if (Verbose) { + printf("property-identifier=%u, array-index=%u\n", + property_id, property_array_index); + } + if (wpm_property->propertyIdentifier > MAX_BACNET_PROPERTY_ID) { + fprintf(stderr, + "property=%u - it must be less than %u\n", + wpm_property->propertyIdentifier, + MAX_BACNET_PROPERTY_ID + 1); + return 1; + } + } + if (scan_count > 1) { + wpm_property->propertyArrayIndex = property_array_index; + } else { + wpm_property->propertyArrayIndex = BACNET_ARRAY_ALL; + } + if (args_remaining <= 0) { + fprintf(stderr, + "Error: missing priority and tag value pair.\n"); + return 1; + } + /* Priority */ + wpm_property->priority = (uint8_t) + strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (Verbose) { + printf("priority=%u\n", wpm_property->priority); + } + if (args_remaining <= 0) { + fprintf(stderr, + "Error: missing tag value pair.\n"); + return 1; + } + /* Tag + Value */ + /* special case for context tagged values */ + if (toupper(argv[tag_value_arg][0]) == 'C') { + context_tag = (uint8_t) strtol(&argv[tag_value_arg][1], NULL, 0); + tag_value_arg++; + args_remaining--; + wpm_property->value.context_tag = context_tag; + wpm_property->value.context_specific = true; + } else { + wpm_property->value.context_specific = false; + } + property_tag = strtol(argv[tag_value_arg], NULL, 0); + tag_value_arg++; + args_remaining--; + if (args_remaining <= 0) { + fprintf(stderr, "Error: missing value for tag-value pair\n"); + return 1; + } + value_string = argv[tag_value_arg]; + tag_value_arg++; + args_remaining--; + if (Verbose) { + printf("tag=%u value=%s\n", property_tag, value_string); + } + if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + fprintf(stderr, "Error: tag=%u - it must be less than %u\n", + property_tag, MAX_BACNET_APPLICATION_TAG); + return 1; + } + status = + bacapp_parse_application_data(property_tag, value_string, + &wpm_property->value); + if (!status) { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, "Error: unable to parse the tag value\n"); + return 1; + } + wpm_property->value.next = NULL; + /* we only support a single property value */ + break; + } + } while (wpm_property); + if (args_remaining) { + arg_sets++; + wpm_object->next = calloc(1, sizeof(BACNET_WRITE_ACCESS_DATA)); + wpm_object = wpm_object->next; + } else { + break; + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + dlenv_init(); + atexit(datalink_cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); + /* try to bind with the device */ + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + if (found) { + if (Verbose) { + printf("Found Device %u in address_cache.\n", + Target_Device_Object_Instance); + } + } else { + Send_WhoIs(Target_Device_Object_Instance, + Target_Device_Object_Instance); + } + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + + /* at least one second has passed */ + if (current_seconds != last_seconds) + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); + if (Error_Detected) + break; + /* wait until the device is bound, or timeout and quit */ + if (!found) { + found = + address_bind_request(Target_Device_Object_Instance, &max_apdu, + &Target_Address); + } + if (found) { + if (Request_Invoke_ID == 0) { + if (Verbose) { + printf("Sending WritePropertyMultiple to Device %u.\n", + Target_Device_Object_Instance); + } + Request_Invoke_ID = + Send_Write_Property_Multiple_Request(&buffer[0], + sizeof(buffer), Target_Device_Object_Instance, + Write_Access_Data); + } else if (tsm_invoke_id_free(Request_Invoke_ID)) { + break; + } else if (tsm_invoke_id_failed(Request_Invoke_ID)) { + fprintf(stderr, "\rError: TSM Timeout!\n"); + tsm_free_invoke_id(Request_Invoke_ID); + Error_Detected = true; + /* try again or abort? */ + break; + } + } else { + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) { + printf("\rError: APDU Timeout!\n"); + Error_Detected = true; + break; + } + } + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + + /* keep track of time for next check */ + last_seconds = current_seconds; + } + if (Error_Detected) { + return 1; + } + + return 0; +} diff --git a/include/bip6.h b/include/bip6.h index c8f55446..3c12f2ff 100644 --- a/include/bip6.h +++ b/include/bip6.h @@ -1,87 +1,87 @@ -/** -* @file -* @author Steve Karg -* @date 2015 -* @defgroup DLBIP6 BACnet/IPv6 DataLink Network Layer -* @ingroup DataLink -* -* Implementation of the Network Layer using BACnet/IPv6 as the transport, as -* described in Annex J. -* The functions described here fulfill the roles defined generically at the -* DataLink level by serving as the implementation of the function templates. -*/ -#ifndef BIP6_H -#define BIP6_H - -#include -#include -#include -#include "bacdef.h" -#include "npdu.h" -#include "bvlc6.h" - -/* specific defines for BACnet/IP over Ethernet */ -#define BIP6_HEADER_MAX (1 + 1 + 2) -#define BIP6_MPDU_MAX (BIP6_HEADER_MAX+MAX_PDU) -/* for legacy demo applications */ -#define MAX_MPDU BIP6_MPDU_MAX - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - /* 6 datalink functions used by demo handlers and applications: - init, send, receive, cleanup, unicast/broadcast address. - Note: the addresses used here are VMAC addresses. */ - bool bip6_init( - char *ifname); - void bip6_cleanup( - void); - void bip6_get_broadcast_address( - BACNET_ADDRESS * my_address); - void bip6_get_my_address( - BACNET_ADDRESS * my_address); - int bip6_send_pdu( - BACNET_ADDRESS * dest, - BACNET_NPDU_DATA * npdu_data, - uint8_t * pdu, - unsigned pdu_len); - uint16_t bip6_receive( - BACNET_ADDRESS * src, - uint8_t * pdu, - uint16_t max_pdu, - unsigned timeout); - - /* functions that are custom per port */ - void bip6_set_interface( - char *ifname); - - bool bip6_address_match_self( - BACNET_IP6_ADDRESS *addr); - - bool bip6_set_addr( - BACNET_IP6_ADDRESS *addr); - bool bip6_get_addr( - BACNET_IP6_ADDRESS *addr); - - void bip6_set_port( - uint16_t port); - uint16_t bip6_get_port( - void); - - bool bip6_set_broadcast_addr( - BACNET_IP6_ADDRESS *addr); - /* returns network byte order */ - bool bip6_get_broadcast_addr( - BACNET_IP6_ADDRESS *addr); - - int bip6_send_mpdu( - BACNET_IP6_ADDRESS *addr, - uint8_t * mtu, - uint16_t mtu_len); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +/** +* @file +* @author Steve Karg +* @date 2015 +* @defgroup DLBIP6 BACnet/IPv6 DataLink Network Layer +* @ingroup DataLink +* +* Implementation of the Network Layer using BACnet/IPv6 as the transport, as +* described in Annex J. +* The functions described here fulfill the roles defined generically at the +* DataLink level by serving as the implementation of the function templates. +*/ +#ifndef BIP6_H +#define BIP6_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" +#include "bvlc6.h" + +/* specific defines for BACnet/IP over Ethernet */ +#define BIP6_HEADER_MAX (1 + 1 + 2) +#define BIP6_MPDU_MAX (BIP6_HEADER_MAX+MAX_PDU) +/* for legacy demo applications */ +#define MAX_MPDU BIP6_MPDU_MAX + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* 6 datalink functions used by demo handlers and applications: + init, send, receive, cleanup, unicast/broadcast address. + Note: the addresses used here are VMAC addresses. */ + bool bip6_init( + char *ifname); + void bip6_cleanup( + void); + void bip6_get_broadcast_address( + BACNET_ADDRESS * my_address); + void bip6_get_my_address( + BACNET_ADDRESS * my_address); + int bip6_send_pdu( + BACNET_ADDRESS * dest, + BACNET_NPDU_DATA * npdu_data, + uint8_t * pdu, + unsigned pdu_len); + uint16_t bip6_receive( + BACNET_ADDRESS * src, + uint8_t * pdu, + uint16_t max_pdu, + unsigned timeout); + + /* functions that are custom per port */ + void bip6_set_interface( + char *ifname); + + bool bip6_address_match_self( + BACNET_IP6_ADDRESS *addr); + + bool bip6_set_addr( + BACNET_IP6_ADDRESS *addr); + bool bip6_get_addr( + BACNET_IP6_ADDRESS *addr); + + void bip6_set_port( + uint16_t port); + uint16_t bip6_get_port( + void); + + bool bip6_set_broadcast_addr( + BACNET_IP6_ADDRESS *addr); + /* returns network byte order */ + bool bip6_get_broadcast_addr( + BACNET_IP6_ADDRESS *addr); + + int bip6_send_mpdu( + BACNET_IP6_ADDRESS *addr, + uint8_t * mtu, + uint16_t mtu_len); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/include/bvlc6.h b/include/bvlc6.h index 630b3d60..52badfdb 100644 --- a/include/bvlc6.h +++ b/include/bvlc6.h @@ -1,403 +1,403 @@ -/** -* @file -* @author Steve Karg -* @date 2015 -* -* Implementation of the BACnet Virtual Link Layer using IPv6, -* as described in Annex J. -*/ -#ifndef BVLC6_H -#define BVLC6_H - -#include -#include -#include -#include -#include "bacdef.h" -#include "npdu.h" - -/** -* BVLL for BACnet/IPv6 -* @{ -*/ -#define BVLL_TYPE_BACNET_IP6 (0x82) -/** @} */ - -/** -* B/IPv6 BVLL Messages -* @{ -*/ -#define BVLC6_RESULT 0x00 -#define BVLC6_ORIGINAL_UNICAST_NPDU 0x01 -#define BVLC6_ORIGINAL_BROADCAST_NPDU 0x02 -#define BVLC6_ADDRESS_RESOLUTION 0x03 -#define BVLC6_FORWARDED_ADDRESS_RESOLUTION 0x04 -#define BVLC6_ADDRESS_RESOLUTION_ACK 0x05 -#define BVLC6_VIRTUAL_ADDRESS_RESOLUTION 0x06 -#define BVLC6_VIRTUAL_ADDRESS_RESOLUTION_ACK 0x07 -#define BVLC6_FORWARDED_NPDU 0x08 -#define BVLC6_REGISTER_FOREIGN_DEVICE 0x09 -#define BVLC6_DELETE_FOREIGN_DEVICE 0x0A -#define BVLC6_SECURE_BVLL 0x0B -#define BVLC6_DISTRIBUTE_BROADCAST_TO_NETWORK 0x0C -/** @} */ - -/** -* BVLC Result Code -* @{ -*/ -#define BVLC6_RESULT_SUCCESSFUL_COMPLETION 0x0000 -#define BVLC6_RESULT_ADDRESS_RESOLUTION_NAK 0x0030 -#define BVLC6_RESULT_VIRTUAL_ADDRESS_RESOLUTION_NAK 0x0060 -#define BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK 0x0090 -#define BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK 0x00A0 -#define BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK 0x00C0 -/** @} */ - -/** -* BACnet IPv6 Multicast Group ID -* BACnet broadcast messages shall be delivered by IPv6 multicasts -* as opposed to using IP broadcasting. Broadcasting in -* IPv6 is subsumed by multicasting to the all-nodes link -* group FF02::1; however, the use of the all-nodes group is not -* recommended, and BACnet/IPv6 uses an IANA permanently assigned -* multicast group identifier to avoid disturbing -* every interface in the network. -* -* The IANA assigned BACnet/IPv6 variable scope multicast address -* is FF0X:0:0:0:0:0:0:BAC0 (FF0X::BAC0) which indicates the multicast -* group identifier X'BAC0'. The following multicast scopes are -* defined for B/IPv6. -* @{ -*/ -#define BIP6_MULTICAST_GROUP_ID 0xBAC0 -/** @} */ - -/** -* IANA prefixes -* @{ -*/ -#define BIP6_MULTICAST_reserved_0 0xFF00 -#define BIP6_MULTICAST_NODE_LOCAL 0xFF01 -#define BIP6_MULTICAST_LINK_LOCAL 0xFF02 -#define BIP6_MULTICAST_reserved_3 0xFF03 -#define BIP6_MULTICAST_ADMIN_LOCAL 0xFF04 -#define BIP6_MULTICAST_SITE_LOCAL 0xFF05 -#define BIP6_MULTICAST_ORG_LOCAL 0xFF08 -#define BIP6_MULTICAST_GLOBAL 0xFF0E -/** @} */ - -/* number of bytes in the IPv6 address */ -#define IP6_ADDRESS_MAX 16 -/* number of bytes in the B/IPv6 address */ -#define BIP6_ADDRESS_MAX 18 - -/** -* BACnet IPv6 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. -* @{ -*/ -typedef struct BACnet_IP6_Address { - uint8_t address[IP6_ADDRESS_MAX]; - uint16_t port; -} BACNET_IP6_ADDRESS; -/** @} */ - -/** -* BACnet /IPv6 Broadcast Distribution Table Format -* -* The BDT shall consist of either the eighteen-octet B/IPv6 address -* of the peer BBMD or the combination of the fully qualified -* domain name service (DNS) entry and UDP port that resolves to -* the B/IPv6 address of the peer BBMD. The Broadcast -* Distribution Table shall not contain an entry for the BBMD in -* which the BDT resides. -* @{ -*/ -struct BACnet_IP6_Broadcast_Distribution_Table_Entry; -typedef struct BACnet_IP6_Broadcast_Distribution_Table_Entry { - /* true if valid entry - false if not */ - bool valid; - /* BACnet/IPv6 address */ - BACNET_IP6_ADDRESS bip6_address; - struct BACnet_IP6_Broadcast_Distribution_Table_Entry *next; -} BACNET_IP6_BROADCAST_DISTRIBUTION_TABLE_ENTRY; -/** @} */ - -/** -* Foreign Device Table (FDT) -* -* Each entry shall contain the B/IPv6 address and the TTL of the -* registered foreign device. -* -* Each entry shall consist of the eighteen-octet B/IPv6 address of the -* registrant; the 2-octet Time-to-Live value supplied at the time of -* registration; and a 2-octet value representing the number of seconds -* remaining before the BBMD will purge the registrant's FDT entry if no -* re-registration occurs. The number of seconds remaining shall be -* initialized to the 2-octet Time-to-Live value supplied at the time -* of registration plus 30 seconds (see U.4.5.2), with a maximum of 65535. -* @{ -*/ -struct BACnet_IP6_Foreign_Device_Table_Entry; -typedef struct BACnet_IP6_Foreign_Device_Table_Entry { - /* true if valid entry - false if not */ - bool valid; - /* BACnet/IPv6 address */ - BACNET_IP6_ADDRESS bip6_address; - /* requested time-to-live value */ - uint16_t ttl_seconds; - /* number of seconds remaining */ - uint16_t ttl_seconds_remaining; - struct BACnet_IP6_Foreign_Device_Table_Entry *next; -} BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY; -/** @} */ - -#ifdef __cplusplus -extern "C" { - -#endif /* __cplusplus */ - int bvlc6_encode_address( - uint8_t * pdu, - uint16_t pdu_size, - BACNET_IP6_ADDRESS * ip6_address); - int bvlc6_decode_address( - uint8_t * pdu, - uint16_t pdu_len, - BACNET_IP6_ADDRESS * ip6_address); - bool bvlc6_address_copy( - BACNET_IP6_ADDRESS * dst, - BACNET_IP6_ADDRESS * src); - bool bvlc6_address_different( - BACNET_IP6_ADDRESS * dst, - BACNET_IP6_ADDRESS * src); - - 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 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 bvlc6_vmac_address_set( - BACNET_ADDRESS * addr, - uint32_t device_id); - bool bvlc6_vmac_address_get( - BACNET_ADDRESS * addr, - uint32_t *device_id); - - int bvlc6_encode_header( - uint8_t * pdu, - uint16_t pdu_size, - uint8_t message_type, - uint16_t length); - int bvlc6_decode_header( - uint8_t * pdu, - uint16_t pdu_len, - uint8_t * message_type, - uint16_t * length); - - int bvlc6_encode_result( - uint8_t * pdu, - uint16_t pdu_size, - uint32_t vmac, - uint16_t result_code); - int bvlc6_decode_result( - uint8_t * pdu, - uint16_t pdu_len, - uint32_t * vmac, - uint16_t * result_code); - - 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 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 bvlc6_encode_original_broadcast( - uint8_t * pdu, - uint16_t pdu_size, - uint32_t vmac, - uint8_t * npdu, - uint16_t npdu_len); - 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 bvlc6_encode_address_resolution( - uint8_t * pdu, - uint16_t pdu_size, - uint32_t vmac_src, - uint32_t vmac_target); - int bvlc6_decode_address_resolution( - uint8_t * pdu, - uint16_t pdu_len, - uint32_t * vmac_src, - uint32_t * vmac_target); - - 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 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 bvlc6_encode_address_resolution_ack( - uint8_t * pdu, - uint16_t pdu_size, - uint32_t vmac_src, - uint32_t vmac_dst); - int bvlc6_decode_address_resolution_ack( - uint8_t * pdu, - uint16_t pdu_len, - uint32_t * vmac_src, - uint32_t * vmac_dst); - - int bvlc6_encode_virtual_address_resolution( - uint8_t * pdu, - uint16_t pdu_size, - uint32_t vmac_src); - int bvlc6_decode_virtual_address_resolution( - uint8_t * pdu, - uint16_t pdu_len, - uint32_t * vmac_src); - - int bvlc6_encode_virtual_address_resolution_ack( - uint8_t * pdu, - uint16_t pdu_size, - uint32_t vmac_src, - uint32_t vmac_dst); - int bvlc6_decode_virtual_address_resolution_ack( - uint8_t * pdu, - uint16_t pdu_len, - uint32_t * vmac_src, - uint32_t * vmac_dst); - - int bvlc6_encode_forwarded_npdu( - uint8_t * pdu, - uint16_t pdu_size, - uint32_t vmac_src, - BACNET_IP6_ADDRESS * address, - uint8_t * npdu, - uint16_t npdu_len); - int bvlc6_decode_forwarded_npdu( - uint8_t * pdu, - uint16_t pdu_len, - uint32_t * vmac_src, - BACNET_IP6_ADDRESS * address, - uint8_t * npdu, - uint16_t npdu_size, - uint16_t * npdu_len); - - int bvlc6_encode_register_foreign_device( - uint8_t * pdu, - uint16_t pdu_size, - uint32_t vmac_src, - uint16_t ttl_seconds); - int bvlc6_decode_register_foreign_device( - uint8_t * pdu, - uint16_t pdu_len, - uint32_t * vmac_src, - uint16_t * ttl_seconds); - - int bvlc6_encode_delete_foreign_device( - uint8_t * pdu, - uint16_t pdu_size, - uint32_t vmac_src, - BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY * fdt_entry); - int bvlc6_decode_delete_foreign_device( - uint8_t * pdu, - uint16_t pdu_len, - uint32_t * vmac_src, - BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY * fdt_entry); - - int bvlc6_encode_secure_bvll( - uint8_t * pdu, - uint16_t pdu_size, - uint8_t * sbuf, - uint16_t sbuf_len); - int bvlc6_decode_secure_bvll( - uint8_t * pdu, - uint16_t pdu_len, - uint8_t * sbuf, - uint16_t sbuf_size, - uint16_t * sbuf_len); - - 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 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); - - /* user application function prototypes */ - int bvlc6_handler( - BACNET_IP6_ADDRESS *addr, - BACNET_ADDRESS * src, - uint8_t * npdu, - uint16_t npdu_len); - int bvlc6_register_with_bbmd( - BACNET_IP6_ADDRESS *bbmd_addr, - uint32_t vmac_src, - uint16_t time_to_live_seconds); - uint16_t bvlc6_get_last_result( - void); - uint8_t bvlc6_get_function_code( - void); - void bvlc6_init(void); - -#ifdef TEST -#include "ctest.h" - void test_BVLC6( - Test * pTest); -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* */ +/** +* @file +* @author Steve Karg +* @date 2015 +* +* Implementation of the BACnet Virtual Link Layer using IPv6, +* as described in Annex J. +*/ +#ifndef BVLC6_H +#define BVLC6_H + +#include +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/** +* BVLL for BACnet/IPv6 +* @{ +*/ +#define BVLL_TYPE_BACNET_IP6 (0x82) +/** @} */ + +/** +* B/IPv6 BVLL Messages +* @{ +*/ +#define BVLC6_RESULT 0x00 +#define BVLC6_ORIGINAL_UNICAST_NPDU 0x01 +#define BVLC6_ORIGINAL_BROADCAST_NPDU 0x02 +#define BVLC6_ADDRESS_RESOLUTION 0x03 +#define BVLC6_FORWARDED_ADDRESS_RESOLUTION 0x04 +#define BVLC6_ADDRESS_RESOLUTION_ACK 0x05 +#define BVLC6_VIRTUAL_ADDRESS_RESOLUTION 0x06 +#define BVLC6_VIRTUAL_ADDRESS_RESOLUTION_ACK 0x07 +#define BVLC6_FORWARDED_NPDU 0x08 +#define BVLC6_REGISTER_FOREIGN_DEVICE 0x09 +#define BVLC6_DELETE_FOREIGN_DEVICE 0x0A +#define BVLC6_SECURE_BVLL 0x0B +#define BVLC6_DISTRIBUTE_BROADCAST_TO_NETWORK 0x0C +/** @} */ + +/** +* BVLC Result Code +* @{ +*/ +#define BVLC6_RESULT_SUCCESSFUL_COMPLETION 0x0000 +#define BVLC6_RESULT_ADDRESS_RESOLUTION_NAK 0x0030 +#define BVLC6_RESULT_VIRTUAL_ADDRESS_RESOLUTION_NAK 0x0060 +#define BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK 0x0090 +#define BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK 0x00A0 +#define BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK 0x00C0 +/** @} */ + +/** +* BACnet IPv6 Multicast Group ID +* BACnet broadcast messages shall be delivered by IPv6 multicasts +* as opposed to using IP broadcasting. Broadcasting in +* IPv6 is subsumed by multicasting to the all-nodes link +* group FF02::1; however, the use of the all-nodes group is not +* recommended, and BACnet/IPv6 uses an IANA permanently assigned +* multicast group identifier to avoid disturbing +* every interface in the network. +* +* The IANA assigned BACnet/IPv6 variable scope multicast address +* is FF0X:0:0:0:0:0:0:BAC0 (FF0X::BAC0) which indicates the multicast +* group identifier X'BAC0'. The following multicast scopes are +* defined for B/IPv6. +* @{ +*/ +#define BIP6_MULTICAST_GROUP_ID 0xBAC0 +/** @} */ + +/** +* IANA prefixes +* @{ +*/ +#define BIP6_MULTICAST_reserved_0 0xFF00 +#define BIP6_MULTICAST_NODE_LOCAL 0xFF01 +#define BIP6_MULTICAST_LINK_LOCAL 0xFF02 +#define BIP6_MULTICAST_reserved_3 0xFF03 +#define BIP6_MULTICAST_ADMIN_LOCAL 0xFF04 +#define BIP6_MULTICAST_SITE_LOCAL 0xFF05 +#define BIP6_MULTICAST_ORG_LOCAL 0xFF08 +#define BIP6_MULTICAST_GLOBAL 0xFF0E +/** @} */ + +/* number of bytes in the IPv6 address */ +#define IP6_ADDRESS_MAX 16 +/* number of bytes in the B/IPv6 address */ +#define BIP6_ADDRESS_MAX 18 + +/** +* BACnet IPv6 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. +* @{ +*/ +typedef struct BACnet_IP6_Address { + uint8_t address[IP6_ADDRESS_MAX]; + uint16_t port; +} BACNET_IP6_ADDRESS; +/** @} */ + +/** +* BACnet /IPv6 Broadcast Distribution Table Format +* +* The BDT shall consist of either the eighteen-octet B/IPv6 address +* of the peer BBMD or the combination of the fully qualified +* domain name service (DNS) entry and UDP port that resolves to +* the B/IPv6 address of the peer BBMD. The Broadcast +* Distribution Table shall not contain an entry for the BBMD in +* which the BDT resides. +* @{ +*/ +struct BACnet_IP6_Broadcast_Distribution_Table_Entry; +typedef struct BACnet_IP6_Broadcast_Distribution_Table_Entry { + /* true if valid entry - false if not */ + bool valid; + /* BACnet/IPv6 address */ + BACNET_IP6_ADDRESS bip6_address; + struct BACnet_IP6_Broadcast_Distribution_Table_Entry *next; +} BACNET_IP6_BROADCAST_DISTRIBUTION_TABLE_ENTRY; +/** @} */ + +/** +* Foreign Device Table (FDT) +* +* Each entry shall contain the B/IPv6 address and the TTL of the +* registered foreign device. +* +* Each entry shall consist of the eighteen-octet B/IPv6 address of the +* registrant; the 2-octet Time-to-Live value supplied at the time of +* registration; and a 2-octet value representing the number of seconds +* remaining before the BBMD will purge the registrant's FDT entry if no +* re-registration occurs. The number of seconds remaining shall be +* initialized to the 2-octet Time-to-Live value supplied at the time +* of registration plus 30 seconds (see U.4.5.2), with a maximum of 65535. +* @{ +*/ +struct BACnet_IP6_Foreign_Device_Table_Entry; +typedef struct BACnet_IP6_Foreign_Device_Table_Entry { + /* true if valid entry - false if not */ + bool valid; + /* BACnet/IPv6 address */ + BACNET_IP6_ADDRESS bip6_address; + /* requested time-to-live value */ + uint16_t ttl_seconds; + /* number of seconds remaining */ + uint16_t ttl_seconds_remaining; + struct BACnet_IP6_Foreign_Device_Table_Entry *next; +} BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY; +/** @} */ + +#ifdef __cplusplus +extern "C" { + +#endif /* __cplusplus */ + int bvlc6_encode_address( + uint8_t * pdu, + uint16_t pdu_size, + BACNET_IP6_ADDRESS * ip6_address); + int bvlc6_decode_address( + uint8_t * pdu, + uint16_t pdu_len, + BACNET_IP6_ADDRESS * ip6_address); + bool bvlc6_address_copy( + BACNET_IP6_ADDRESS * dst, + BACNET_IP6_ADDRESS * src); + bool bvlc6_address_different( + BACNET_IP6_ADDRESS * dst, + BACNET_IP6_ADDRESS * src); + + 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 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 bvlc6_vmac_address_set( + BACNET_ADDRESS * addr, + uint32_t device_id); + bool bvlc6_vmac_address_get( + BACNET_ADDRESS * addr, + uint32_t *device_id); + + int bvlc6_encode_header( + uint8_t * pdu, + uint16_t pdu_size, + uint8_t message_type, + uint16_t length); + int bvlc6_decode_header( + uint8_t * pdu, + uint16_t pdu_len, + uint8_t * message_type, + uint16_t * length); + + int bvlc6_encode_result( + uint8_t * pdu, + uint16_t pdu_size, + uint32_t vmac, + uint16_t result_code); + int bvlc6_decode_result( + uint8_t * pdu, + uint16_t pdu_len, + uint32_t * vmac, + uint16_t * result_code); + + 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 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 bvlc6_encode_original_broadcast( + uint8_t * pdu, + uint16_t pdu_size, + uint32_t vmac, + uint8_t * npdu, + uint16_t npdu_len); + 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 bvlc6_encode_address_resolution( + uint8_t * pdu, + uint16_t pdu_size, + uint32_t vmac_src, + uint32_t vmac_target); + int bvlc6_decode_address_resolution( + uint8_t * pdu, + uint16_t pdu_len, + uint32_t * vmac_src, + uint32_t * vmac_target); + + 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 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 bvlc6_encode_address_resolution_ack( + uint8_t * pdu, + uint16_t pdu_size, + uint32_t vmac_src, + uint32_t vmac_dst); + int bvlc6_decode_address_resolution_ack( + uint8_t * pdu, + uint16_t pdu_len, + uint32_t * vmac_src, + uint32_t * vmac_dst); + + int bvlc6_encode_virtual_address_resolution( + uint8_t * pdu, + uint16_t pdu_size, + uint32_t vmac_src); + int bvlc6_decode_virtual_address_resolution( + uint8_t * pdu, + uint16_t pdu_len, + uint32_t * vmac_src); + + int bvlc6_encode_virtual_address_resolution_ack( + uint8_t * pdu, + uint16_t pdu_size, + uint32_t vmac_src, + uint32_t vmac_dst); + int bvlc6_decode_virtual_address_resolution_ack( + uint8_t * pdu, + uint16_t pdu_len, + uint32_t * vmac_src, + uint32_t * vmac_dst); + + int bvlc6_encode_forwarded_npdu( + uint8_t * pdu, + uint16_t pdu_size, + uint32_t vmac_src, + BACNET_IP6_ADDRESS * address, + uint8_t * npdu, + uint16_t npdu_len); + int bvlc6_decode_forwarded_npdu( + uint8_t * pdu, + uint16_t pdu_len, + uint32_t * vmac_src, + BACNET_IP6_ADDRESS * address, + uint8_t * npdu, + uint16_t npdu_size, + uint16_t * npdu_len); + + int bvlc6_encode_register_foreign_device( + uint8_t * pdu, + uint16_t pdu_size, + uint32_t vmac_src, + uint16_t ttl_seconds); + int bvlc6_decode_register_foreign_device( + uint8_t * pdu, + uint16_t pdu_len, + uint32_t * vmac_src, + uint16_t * ttl_seconds); + + int bvlc6_encode_delete_foreign_device( + uint8_t * pdu, + uint16_t pdu_size, + uint32_t vmac_src, + BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY * fdt_entry); + int bvlc6_decode_delete_foreign_device( + uint8_t * pdu, + uint16_t pdu_len, + uint32_t * vmac_src, + BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY * fdt_entry); + + int bvlc6_encode_secure_bvll( + uint8_t * pdu, + uint16_t pdu_size, + uint8_t * sbuf, + uint16_t sbuf_len); + int bvlc6_decode_secure_bvll( + uint8_t * pdu, + uint16_t pdu_len, + uint8_t * sbuf, + uint16_t sbuf_size, + uint16_t * sbuf_len); + + 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 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); + + /* user application function prototypes */ + int bvlc6_handler( + BACNET_IP6_ADDRESS *addr, + BACNET_ADDRESS * src, + uint8_t * npdu, + uint16_t npdu_len); + int bvlc6_register_with_bbmd( + BACNET_IP6_ADDRESS *bbmd_addr, + uint32_t vmac_src, + uint16_t time_to_live_seconds); + uint16_t bvlc6_get_last_result( + void); + uint8_t bvlc6_get_function_code( + void); + void bvlc6_init(void); + +#ifdef TEST +#include "ctest.h" + void test_BVLC6( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* */ diff --git a/include/vmac.h b/include/vmac.h index a499d628..fa5ee3ef 100644 --- a/include/vmac.h +++ b/include/vmac.h @@ -1,52 +1,52 @@ -/** -* @file -* @author Steve Karg -* @date 2016 -*/ -#ifndef VMAC_H -#define VMAC_H - -#include -#include - -/* define the max MAC as big as IPv6 + port number */ -#define VMAC_MAC_MAX 18 -/** -* VMAC data structure -* -* @{ -*/ -struct vmac_data { - uint8_t mac[18]; - uint8_t mac_len; -}; -/** @} */ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - unsigned int VMAC_Count(void); - struct vmac_data *VMAC_Find_By_Key(uint32_t device_id); - bool VMAC_Find_By_Data(struct vmac_data *vmac, uint32_t *device_id); - bool VMAC_Add(uint32_t device_id, struct vmac_data *pVMAC); - bool VMAC_Delete(uint32_t device_id); - bool VMAC_Different( - struct vmac_data *vmac1, - struct vmac_data *vmac2); - bool VMAC_Match( - struct vmac_data *vmac1, - struct vmac_data *vmac2); - void VMAC_Cleanup(void); - void VMAC_Init(void); - -#ifdef TEST -#include "ctest.h" - void testVMAC( - Test * pTest); -#endif - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +/** +* @file +* @author Steve Karg +* @date 2016 +*/ +#ifndef VMAC_H +#define VMAC_H + +#include +#include + +/* define the max MAC as big as IPv6 + port number */ +#define VMAC_MAC_MAX 18 +/** +* VMAC data structure +* +* @{ +*/ +struct vmac_data { + uint8_t mac[18]; + uint8_t mac_len; +}; +/** @} */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + unsigned int VMAC_Count(void); + struct vmac_data *VMAC_Find_By_Key(uint32_t device_id); + bool VMAC_Find_By_Data(struct vmac_data *vmac, uint32_t *device_id); + bool VMAC_Add(uint32_t device_id, struct vmac_data *pVMAC); + bool VMAC_Delete(uint32_t device_id); + bool VMAC_Different( + struct vmac_data *vmac1, + struct vmac_data *vmac2); + bool VMAC_Match( + struct vmac_data *vmac1, + struct vmac_data *vmac2); + void VMAC_Cleanup(void); + void VMAC_Init(void); + +#ifdef TEST +#include "ctest.h" + void testVMAC( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/arduino_uno/bip.h b/ports/arduino_uno/bip.h index 1bb3284d..34b70700 100644 --- a/ports/arduino_uno/bip.h +++ b/ports/arduino_uno/bip.h @@ -1,117 +1,117 @@ -/************************************************************************** -* -* Copyright (C) 2012 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*********************************************************************/ -#ifndef BIP_H -#define BIP_H - -#include -#include -#include -#include "bacdef.h" -#include "npdu.h" - -/* specific defines for BACnet/IP over Ethernet */ -#define MAX_HEADER (1 + 1 + 2) -#define MAX_MPDU (MAX_HEADER+MAX_PDU) - -#define BVLL_TYPE_BACNET_IP (0x81) - -extern bool BIP_Debug; - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - /* note: define init, set_interface, and cleanup in your port */ - /* on Linux, ifname is eth0, ath0, arc0, and others. - on Windows, ifname is the dotted ip address of the interface */ - bool bip_init(char *ifname); - void bip_set_interface(char *ifname); - void bip_cleanup(void); - - /* Convert uint8_t IPv4 to uint32 */ - uint32_t convertBIP_Address2uint32(uint8_t * bip_address); - void convertUint32Address_2_uint8Address(uint32_t ip, - uint8_t * address); - /* common BACnet/IP functions */ - void bip_set_socket(uint8_t sock_fd); - uint8_t bip_socket(void); - bool bip_valid(void); - void bip_get_broadcast_address(BACNET_ADDRESS * dest); /* destination address */ - void bip_get_my_address(BACNET_ADDRESS * my_address); - - /* function to send a packet out the BACnet/IP socket */ - /* returns zero on success, non-zero on failure */ - int bip_send_pdu(BACNET_ADDRESS * dest, /* destination address */ - - BACNET_NPDU_DATA * npdu_data, /* network information */ - - uint8_t * pdu, /* any data to be sent - may be null */ - - unsigned pdu_len); /* number of bytes of data */ - - /* receives a BACnet/IP packet */ - /* returns the number of octets in the PDU, or zero on failure */ - uint16_t bip_receive(BACNET_ADDRESS * src, /* source address */ - - uint8_t * pdu, /* PDU data */ - - uint16_t max_pdu, /* amount of space available in the PDU */ - - unsigned timeout); /* milliseconds to wait for a packet */ - - /* use network byte order for setting */ - void bip_set_port(uint16_t port); - /* returns network byte order */ - uint16_t bip_get_port(void); - - /* use network byte order for setting */ - void bip_set_addr(uint8_t * net_address); - /* returns network byte order */ - uint8_t *bip_get_addr(void); - - /* use network byte order for setting */ - void bip_set_broadcast_addr(uint8_t * net_address); - /* returns network byte order */ - uint8_t *bip_get_broadcast_addr(void); - - /* gets an IP address by name, where name can be a - string that is an IP address in dotted form, or - a name that is a domain name - returns 0 if not found, or - an IP address in network byte order */ - long bip_getaddrbyname(const char *host_name); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -/** @defgroup DLBIP BACnet/IP DataLink Network Layer - * @ingroup DataLink - * Implementation of the Network Layer using BACnet/IP as the transport, as - * described in Annex J. - * The functions described here fulfill the roles defined generically at the - * DataLink level by serving as the implementation of the function templates. - * - */ -#endif +/************************************************************************** +* +* Copyright (C) 2012 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ +#ifndef BIP_H +#define BIP_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* specific defines for BACnet/IP over Ethernet */ +#define MAX_HEADER (1 + 1 + 2) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +#define BVLL_TYPE_BACNET_IP (0x81) + +extern bool BIP_Debug; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* note: define init, set_interface, and cleanup in your port */ + /* on Linux, ifname is eth0, ath0, arc0, and others. + on Windows, ifname is the dotted ip address of the interface */ + bool bip_init(char *ifname); + void bip_set_interface(char *ifname); + void bip_cleanup(void); + + /* Convert uint8_t IPv4 to uint32 */ + uint32_t convertBIP_Address2uint32(uint8_t * bip_address); + void convertUint32Address_2_uint8Address(uint32_t ip, + uint8_t * address); + /* common BACnet/IP functions */ + void bip_set_socket(uint8_t sock_fd); + uint8_t bip_socket(void); + bool bip_valid(void); + void bip_get_broadcast_address(BACNET_ADDRESS * dest); /* destination address */ + void bip_get_my_address(BACNET_ADDRESS * my_address); + + /* function to send a packet out the BACnet/IP socket */ + /* returns zero on success, non-zero on failure */ + int bip_send_pdu(BACNET_ADDRESS * dest, /* destination address */ + + BACNET_NPDU_DATA * npdu_data, /* network information */ + + uint8_t * pdu, /* any data to be sent - may be null */ + + unsigned pdu_len); /* number of bytes of data */ + + /* receives a BACnet/IP packet */ + /* returns the number of octets in the PDU, or zero on failure */ + uint16_t bip_receive(BACNET_ADDRESS * src, /* source address */ + + uint8_t * pdu, /* PDU data */ + + uint16_t max_pdu, /* amount of space available in the PDU */ + + unsigned timeout); /* milliseconds to wait for a packet */ + + /* use network byte order for setting */ + void bip_set_port(uint16_t port); + /* returns network byte order */ + uint16_t bip_get_port(void); + + /* use network byte order for setting */ + void bip_set_addr(uint8_t * net_address); + /* returns network byte order */ + uint8_t *bip_get_addr(void); + + /* use network byte order for setting */ + void bip_set_broadcast_addr(uint8_t * net_address); + /* returns network byte order */ + uint8_t *bip_get_broadcast_addr(void); + + /* gets an IP address by name, where name can be a + string that is an IP address in dotted form, or + a name that is a domain name + returns 0 if not found, or + an IP address in network byte order */ + long bip_getaddrbyname(const char *host_name); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup DLBIP BACnet/IP DataLink Network Layer + * @ingroup DataLink + * Implementation of the Network Layer using BACnet/IP as the transport, as + * described in Annex J. + * The functions described here fulfill the roles defined generically at the + * DataLink level by serving as the implementation of the function templates. + * + */ +#endif diff --git a/ports/arduino_uno/device.c b/ports/arduino_uno/device.c index 60cf0a7a..815edc46 100644 --- a/ports/arduino_uno/device.c +++ b/ports/arduino_uno/device.c @@ -1,460 +1,460 @@ -/************************************************************************** -* -* Copyright (C) 2007 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ - -#include -#include -#include "bacdef.h" -#include "bacdcode.h" -#include "bacstr.h" -#include "bacenum.h" -#include "apdu.h" -#include "dcc.h" -#include "dlmstp.h" -#include "version.h" - -/* objects */ -#include "device.h" -#include "av.h" -#include "bv.h" -#include "wp.h" - -/* note: you really only need to define variables for - properties that are writable or that may change. - The properties that are constant can be hard coded - into the read-property encoding. */ -static uint32_t Object_Instance_Number = 260001; -static char Object_Name[20] = "My Device"; -static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; - -void Device_Init(void) -{ - /* Reinitialize_State = BACNET_REINIT_IDLE; */ - /* dcc_set_status_duration(COMMUNICATION_ENABLE, 0); */ - /* FIXME: Get the data from the eeprom */ - /* I2C_Read_Block(EEPROM_DEVICE_ADDRESS, - (char *)&Object_Instance_Number, - sizeof(Object_Instance_Number), - EEPROM_BACNET_ID_ADDR); */ -} - -/* methods to manipulate the data */ -uint32_t Device_Object_Instance_Number(void) -{ - return Object_Instance_Number; -} - -bool Device_Set_Object_Instance_Number(uint32_t object_id) -{ - bool status = true; /* return value */ - - if (object_id <= BACNET_MAX_INSTANCE) { - Object_Instance_Number = object_id; - /* FIXME: Write the data to the eeprom */ - /* I2C_Write_Block( - EEPROM_DEVICE_ADDRESS, - (char *)&Object_Instance_Number, - sizeof(Object_Instance_Number), - EEPROM_BACNET_ID_ADDR); */ - } else - status = false; - - return status; -} - -bool Device_Valid_Object_Instance_Number(uint32_t object_id) -{ - /* BACnet allows for a wildcard instance number */ - return ((Object_Instance_Number == object_id) || - (object_id == BACNET_MAX_INSTANCE)); -} - -uint16_t Device_Vendor_Identifier(void) -{ - return BACNET_VENDOR_ID; -} - -unsigned Device_Object_List_Count(void) -{ - unsigned count = 1; /* at least 1 for device object */ - - /* FIXME: add objects as needed */ - count += Analog_Value_Count(); - count += Binary_Value_Count(); - - return count; -} - -bool Device_Object_List_Identifier(uint32_t array_index, - int *object_type, - uint32_t * instance) -{ - bool status = false; - uint32_t object_index = 0; - uint32_t object_count = 0; - - /* device object */ - if (array_index == 1) { - *object_type = OBJECT_DEVICE; - *instance = Object_Instance_Number; - status = true; - } - /* normalize the index since - we know it is not the previous objects */ - /* array index starts at 1 */ - object_index = array_index - 1; - /* 1 for the device object */ - object_count = 1; - /* FIXME: add objects as needed */ - /* analog value objects */ - if (!status) { - /* array index starts at 1, and 1 for the device object */ - object_index -= object_count; - object_count = Analog_Value_Count(); - if (object_index < object_count) { - *object_type = OBJECT_ANALOG_VALUE; - *instance = Analog_Value_Index_To_Instance(object_index); - status = true; - } - } - /* binary value objects */ - if (!status) { - object_index -= object_count; - object_count = Binary_Value_Count(); - /* is it a valid index for this object? */ - if (object_index < object_count) { - *object_type = OBJECT_BINARY_VALUE; - *instance = Binary_Value_Index_To_Instance(object_index); - status = true; - } - } - - return status; -} - -/* return the length of the apdu encoded or -1 for error */ -int Device_Encode_Property_APDU(uint8_t * apdu, - uint32_t object_instance, - BACNET_PROPERTY_ID property, - uint32_t array_index, - BACNET_ERROR_CLASS * error_class, - BACNET_ERROR_CODE * error_code) -{ - int apdu_len = 0; /* return value */ - int len = 0; /* apdu len intermediate value */ - BACNET_BIT_STRING bit_string; - BACNET_CHARACTER_STRING char_string; - uint32_t i = 0; - int object_type = 0; - uint32_t instance = 0; - uint32_t count = 0; - - //object_instance = object_instance; - /* FIXME: change the hardcoded names to suit your application */ - switch (property) { - case PROP_OBJECT_IDENTIFIER: - apdu_len = - encode_application_object_id(&apdu[0], OBJECT_DEVICE, - Object_Instance_Number); - break; - case PROP_OBJECT_NAME: - characterstring_init_ansi(&char_string, Object_Name); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_OBJECT_TYPE: - apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); - break; - case PROP_SYSTEM_STATUS: - apdu_len = encode_application_enumerated(&apdu[0], System_Status); - break; - case PROP_VENDOR_NAME: - characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_VENDOR_IDENTIFIER: - apdu_len = - encode_application_unsigned(&apdu[0], - Device_Vendor_Identifier()); - break; - case PROP_MODEL_NAME: - characterstring_init_ansi(&char_string, "GNU Demo"); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_FIRMWARE_REVISION: - characterstring_init_ansi(&char_string, BACNET_VERSION_TEXT); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_APPLICATION_SOFTWARE_VERSION: - characterstring_init_ansi(&char_string, "1.0"); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_PROTOCOL_VERSION: - apdu_len = - encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); - break; - case PROP_PROTOCOL_REVISION: - apdu_len = - encode_application_unsigned(&apdu[0], - BACNET_PROTOCOL_REVISION); - break; - case PROP_PROTOCOL_SERVICES_SUPPORTED: - /* Note: list of services that are executed, not initiated. */ - bitstring_init(&bit_string); - for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { - /* automatic lookup based on handlers set */ - bitstring_set_bit(&bit_string, (uint8_t) i, - apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); - } - apdu_len = encode_application_bitstring(&apdu[0], &bit_string); - break; - case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: - /* Note: this is the list of objects that can be in this device, - not a list of objects that this device can access */ - bitstring_init(&bit_string); - /* must have the bit string as big as it can be */ - for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { - /* initialize all the object types to not-supported */ - bitstring_set_bit(&bit_string, (uint8_t) i, false); - } - /* FIXME: indicate the objects that YOU support */ - bitstring_set_bit(&bit_string, OBJECT_DEVICE, true); - bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true); - bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true); - apdu_len = encode_application_bitstring(&apdu[0], &bit_string); - break; - case PROP_OBJECT_LIST: - count = Device_Object_List_Count(); - /* Array element zero is the number of objects in the list */ - if (array_index == 0) - apdu_len = encode_application_unsigned(&apdu[0], count); - /* if no index was specified, then try to encode the entire list */ - /* into one packet. Note that more than likely you will have */ - /* to return an error if the number of encoded objects exceeds */ - /* your maximum APDU size. */ - else if (array_index == BACNET_ARRAY_ALL) { - for (i = 1; i <= count; i++) { - Device_Object_List_Identifier(i, &object_type, &instance); - len = - encode_application_object_id(&apdu[apdu_len], - object_type, instance); - apdu_len += len; - /* assume next one is the same size as this one */ - /* can we all fit into the APDU? */ - if ((apdu_len + len) >= MAX_APDU) { - /* Abort response */ - *error_code = - ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; - apdu_len = BACNET_STATUS_ABORT; - break; - } - } - } else { - if (Device_Object_List_Identifier(array_index, &object_type, - &instance)) - apdu_len = - encode_application_object_id(&apdu[0], object_type, - instance); - else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - apdu_len = BACNET_STATUS_ERROR; - } - } - break; - case PROP_MAX_APDU_LENGTH_ACCEPTED: - apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); - break; - case PROP_SEGMENTATION_SUPPORTED: - apdu_len = - encode_application_enumerated(&apdu[0], SEGMENTATION_NONE); - break; - case PROP_APDU_TIMEOUT: - apdu_len = encode_application_unsigned(&apdu[0], 60000); - break; - case PROP_NUMBER_OF_APDU_RETRIES: - apdu_len = encode_application_unsigned(&apdu[0], 0); - break; - case PROP_DEVICE_ADDRESS_BINDING: - /* FIXME: encode the list here, if it exists */ - break; - case PROP_DATABASE_REVISION: - apdu_len = encode_application_unsigned(&apdu[0], 0); - break; -// case PROP_MAX_INFO_FRAMES: -// apdu_len = -// encode_application_unsigned(&apdu[0], -// dlmstp_max_info_frames()); -// break; -// case PROP_MAX_MASTER: -// apdu_len = -// encode_application_unsigned(&apdu[0], dlmstp_max_master()); -// break; -// case 9600: -// apdu_len = -// encode_application_unsigned(&apdu[0], RS485_Get_Baud_Rate()); -// break; -// case 512: -// apdu_len = encode_application_unsigned(&apdu[0], stack_size()); -// break; -// case 513: -// apdu_len = encode_application_unsigned(&apdu[0], stack_unused()); -// break; - default: - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_UNKNOWN_PROPERTY; - apdu_len = -1; - break; - } - /* only array properties can have array options */ - if ((apdu_len >= 0) && (property != PROP_OBJECT_LIST) && - (array_index != BACNET_ARRAY_ALL)) { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; - apdu_len = BACNET_STATUS_ERROR; - } - - return apdu_len; -} - -bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, - BACNET_ERROR_CLASS * error_class, - BACNET_ERROR_CODE * error_code) -{ - bool status = false; /* return value */ - int len = 0; - BACNET_APPLICATION_DATA_VALUE value; - - if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) { - *error_class = ERROR_CLASS_OBJECT; - *error_code = ERROR_CODE_UNKNOWN_OBJECT; - return false; - } - /* decode the some of the request */ - len = - bacapp_decode_application_data(wp_data->application_data, - wp_data->application_data_len, &value); - /* FIXME: len < application_data_len: more data? */ - if (len < 0) { - /* error while decoding - a value larger than we can handle */ - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - return false; - } - switch (wp_data->object_property) { - case PROP_OBJECT_IDENTIFIER: - if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { - if ((value.type.Object_Id.type == OBJECT_DEVICE) && - (Device_Set_Object_Instance_Number(value.type.Object_Id. - instance))) { - /* we could send an I-Am broadcast to let the world know */ - status = true; - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_INVALID_DATA_TYPE; - } - break; -// case PROP_MAX_INFO_FRAMES: -// if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { -// if (value.type.Unsigned_Int <= 255) { -// dlmstp_set_max_info_frames(value.type.Unsigned_Int); -// status = true; -// } else { -// *error_class = ERROR_CLASS_PROPERTY; -// *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; -// } -// } else { -// *error_class = ERROR_CLASS_PROPERTY; -// *error_code = ERROR_CODE_INVALID_DATA_TYPE; -// } -// break; -// case PROP_MAX_MASTER: -// if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { -// if ((value.type.Unsigned_Int > 0) && -// (value.type.Unsigned_Int <= 127)) { -// dlmstp_set_max_master(value.type.Unsigned_Int); -// status = true; -// } else { -// *error_class = ERROR_CLASS_PROPERTY; -// *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; -// } -// } else { -// *error_class = ERROR_CLASS_PROPERTY; -// *error_code = ERROR_CODE_INVALID_DATA_TYPE; -// } -// break; - case PROP_OBJECT_NAME: - if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { - uint8_t encoding; - - encoding = - characterstring_encoding(&value.type.Character_String); - if (encoding == CHARACTER_ANSI_X34) { - if (characterstring_ansi_copy(&Object_Name[0], - sizeof(Object_Name), - &value.type.Character_String)) { - status = true; - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_INVALID_DATA_TYPE; - } - break; -// case 9600: -// if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { -// if (value.type.Unsigned_Int > 115200) { -// RS485_Set_Baud_Rate(value.type.Unsigned_Int); -// status = true; -// } else { -// *error_class = ERROR_CLASS_PROPERTY; -// *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; -// } -// } else { -// *error_class = ERROR_CLASS_PROPERTY; -// *error_code = ERROR_CODE_INVALID_DATA_TYPE; -// } -// break; - default: - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - break; - } - - return status; -} +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "apdu.h" +#include "dcc.h" +#include "dlmstp.h" +#include "version.h" + +/* objects */ +#include "device.h" +#include "av.h" +#include "bv.h" +#include "wp.h" + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number = 260001; +static char Object_Name[20] = "My Device"; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; + +void Device_Init(void) +{ + /* Reinitialize_State = BACNET_REINIT_IDLE; */ + /* dcc_set_status_duration(COMMUNICATION_ENABLE, 0); */ + /* FIXME: Get the data from the eeprom */ + /* I2C_Read_Block(EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number(void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number(uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + Object_Instance_Number = object_id; + /* FIXME: Write the data to the eeprom */ + /* I2C_Write_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number(uint32_t object_id) +{ + /* BACnet allows for a wildcard instance number */ + return ((Object_Instance_Number == object_id) || + (object_id == BACNET_MAX_INSTANCE)); +} + +uint16_t Device_Vendor_Identifier(void) +{ + return BACNET_VENDOR_ID; +} + +unsigned Device_Object_List_Count(void) +{ + unsigned count = 1; /* at least 1 for device object */ + + /* FIXME: add objects as needed */ + count += Analog_Value_Count(); + count += Binary_Value_Count(); + + return count; +} + +bool Device_Object_List_Identifier(uint32_t array_index, + int *object_type, + uint32_t * instance) +{ + bool status = false; + uint32_t object_index = 0; + uint32_t object_count = 0; + + /* device object */ + if (array_index == 1) { + *object_type = OBJECT_DEVICE; + *instance = Object_Instance_Number; + status = true; + } + /* normalize the index since + we know it is not the previous objects */ + /* array index starts at 1 */ + object_index = array_index - 1; + /* 1 for the device object */ + object_count = 1; + /* FIXME: add objects as needed */ + /* analog value objects */ + if (!status) { + /* array index starts at 1, and 1 for the device object */ + object_index -= object_count; + object_count = Analog_Value_Count(); + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_VALUE; + *instance = Analog_Value_Index_To_Instance(object_index); + status = true; + } + } + /* binary value objects */ + if (!status) { + object_index -= object_count; + object_count = Binary_Value_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_VALUE; + *instance = Binary_Value_Index_To_Instance(object_index); + status = true; + } + } + + return status; +} + +/* return the length of the apdu encoded or -1 for error */ +int Device_Encode_Property_APDU(uint8_t * apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + uint32_t array_index, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + uint32_t i = 0; + int object_type = 0; + uint32_t instance = 0; + uint32_t count = 0; + + //object_instance = object_instance; + /* FIXME: change the hardcoded names to suit your application */ + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, Object_Name); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_SYSTEM_STATUS: + apdu_len = encode_application_enumerated(&apdu[0], System_Status); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, BACNET_VENDOR_NAME); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], + Device_Vendor_Identifier()); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, "GNU Demo"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, BACNET_VERSION_TEXT); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, "1.0"); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_PROTOCOL_VERSION); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_application_unsigned(&apdu[0], + BACNET_PROTOCOL_REVISION); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported((BACNET_SERVICES_SUPPORTED) i)); + } + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + /* must have the bit string as big as it can be */ + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* FIXME: indicate the objects that YOU support */ + bitstring_set_bit(&bit_string, OBJECT_DEVICE, true); + bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true); + bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + Device_Object_List_Identifier(i, &object_type, &instance); + len = + encode_application_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + /* Abort response */ + *error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } + } else { + if (Device_Object_List_Identifier(array_index, &object_type, + &instance)) + apdu_len = + encode_application_object_id(&apdu[0], object_type, + instance); + else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = + encode_application_enumerated(&apdu[0], SEGMENTATION_NONE); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_application_unsigned(&apdu[0], 60000); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = encode_application_unsigned(&apdu[0], 0); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = encode_application_unsigned(&apdu[0], 0); + break; +// case PROP_MAX_INFO_FRAMES: +// apdu_len = +// encode_application_unsigned(&apdu[0], +// dlmstp_max_info_frames()); +// break; +// case PROP_MAX_MASTER: +// apdu_len = +// encode_application_unsigned(&apdu[0], dlmstp_max_master()); +// break; +// case 9600: +// apdu_len = +// encode_application_unsigned(&apdu[0], RS485_Get_Baud_Rate()); +// break; +// case 512: +// apdu_len = encode_application_unsigned(&apdu[0], stack_size()); +// break; +// case 513: +// apdu_len = encode_application_unsigned(&apdu[0], stack_unused()); +// break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (property != PROP_OBJECT_LIST) && + (array_index != BACNET_ARRAY_ALL)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type.Object_Id. + instance))) { + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +// case PROP_MAX_INFO_FRAMES: +// if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { +// if (value.type.Unsigned_Int <= 255) { +// dlmstp_set_max_info_frames(value.type.Unsigned_Int); +// status = true; +// } else { +// *error_class = ERROR_CLASS_PROPERTY; +// *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; +// } +// } else { +// *error_class = ERROR_CLASS_PROPERTY; +// *error_code = ERROR_CODE_INVALID_DATA_TYPE; +// } +// break; +// case PROP_MAX_MASTER: +// if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { +// if ((value.type.Unsigned_Int > 0) && +// (value.type.Unsigned_Int <= 127)) { +// dlmstp_set_max_master(value.type.Unsigned_Int); +// status = true; +// } else { +// *error_class = ERROR_CLASS_PROPERTY; +// *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; +// } +// } else { +// *error_class = ERROR_CLASS_PROPERTY; +// *error_code = ERROR_CODE_INVALID_DATA_TYPE; +// } +// break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + uint8_t encoding; + + encoding = + characterstring_encoding(&value.type.Character_String); + if (encoding == CHARACTER_ANSI_X34) { + if (characterstring_ansi_copy(&Object_Name[0], + sizeof(Object_Name), + &value.type.Character_String)) { + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +// case 9600: +// if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { +// if (value.type.Unsigned_Int > 115200) { +// RS485_Set_Baud_Rate(value.type.Unsigned_Int); +// status = true; +// } else { +// *error_class = ERROR_CLASS_PROPERTY; +// *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; +// } +// } else { +// *error_class = ERROR_CLASS_PROPERTY; +// *error_code = ERROR_CODE_INVALID_DATA_TYPE; +// } +// break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} diff --git a/ports/arduino_uno/h_rp.c b/ports/arduino_uno/h_rp.c index 5a1ffe2b..cc3a60f0 100644 --- a/ports/arduino_uno/h_rp.c +++ b/ports/arduino_uno/h_rp.c @@ -1,161 +1,161 @@ -/************************************************************************** -* -* Copyright (C) 2005 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ -#include -#include -#include -#include -#include -#include "config.h" -#include "txbuf.h" -#include "bacdef.h" -#include "bacdcode.h" -#include "bacerror.h" -#include "apdu.h" -#include "npdu.h" -#include "abort.h" -#include "rp.h" -/* demo objects */ -#include "device.h" -#include "av.h" -#include "bv.h" - -/* Encodes the property APDU and returns the length, - or sets the error, and returns -1 */ -int Encode_Property_APDU(uint8_t * apdu, - BACNET_READ_PROPERTY_DATA * rp_data, - BACNET_ERROR_CLASS * error_class, - BACNET_ERROR_CODE * error_code) -{ - int apdu_len = -1; - - /* handle each object type */ - switch (rp_data->object_type) { - case OBJECT_DEVICE: - if (Device_Valid_Object_Instance_Number(rp_data->object_instance)) { - apdu_len = - Device_Encode_Property_APDU(&apdu[0], - rp_data->object_instance, rp_data->object_property, - rp_data->array_index, error_class, error_code); - } - break; - case OBJECT_ANALOG_VALUE: - if (Analog_Value_Valid_Instance(rp_data->object_instance)) { - apdu_len = - Analog_Value_Encode_Property_APDU(&apdu[0], - rp_data->object_instance, rp_data->object_property, - rp_data->array_index, error_class, error_code); - } - break; - case OBJECT_BINARY_VALUE: - if (Binary_Value_Valid_Instance(rp_data->object_instance)) { - apdu_len = - Binary_Value_Encode_Property_APDU(&apdu[0], - rp_data->object_instance, rp_data->object_property, - rp_data->array_index, error_class, error_code); - } - break; - default: - *error_class = ERROR_CLASS_OBJECT; - *error_code = ERROR_CODE_UNKNOWN_OBJECT; - break; - } - - return apdu_len; -} - -void handler_read_property(uint8_t * service_request, - uint16_t service_len, - BACNET_ADDRESS * src, - BACNET_CONFIRMED_SERVICE_DATA * service_data) -{ - BACNET_READ_PROPERTY_DATA data; - int len = 0; - int ack_len = 0; - int property_len = 0; - int pdu_len = 0; - BACNET_NPDU_DATA npdu_data; - BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; - BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; - BACNET_ADDRESS my_address; - - /* encode the NPDU portion of the packet */ - datalink_get_my_address(&my_address); - npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); - pdu_len = - npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, - &npdu_data); - if (service_data->segmented_message) { - /* we don't support segmentation - send an abort */ - len = - abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], - service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, - true); - goto RP_ABORT; - } - len = rp_decode_service_request(service_request, service_len, &data); - if (len < 0) { - /* bad decoding - send an abort */ - len = - abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], - service_data->invoke_id, ABORT_REASON_OTHER, true); - goto RP_ABORT; - } - /* most cases will be error */ - ack_len = - rp_ack_encode_apdu_init(&Handler_Transmit_Buffer[pdu_len], - service_data->invoke_id, &data); - /* FIXME: add buffer len as passed into function or use smart buffer */ - property_len = - Encode_Property_APDU(&Handler_Transmit_Buffer[pdu_len + ack_len], - &data, &error_class, &error_code); - if (property_len >= 0) { - len = - rp_ack_encode_apdu_object_property_end(&Handler_Transmit_Buffer - [pdu_len + property_len + ack_len]); - len += ack_len + property_len; - } else { - switch (property_len) { - /* BACnet APDU too small to fit data, so proper response is Abort */ - case BACNET_STATUS_ABORT: - len = - abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], - service_data->invoke_id, - ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); - break; - default: - len = - bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], - service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, - error_class, error_code); - break; - } - } - RP_ABORT: - pdu_len += len; - - datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); - - return; -} +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "rp.h" +/* demo objects */ +#include "device.h" +#include "av.h" +#include "bv.h" + +/* Encodes the property APDU and returns the length, + or sets the error, and returns -1 */ +int Encode_Property_APDU(uint8_t * apdu, + BACNET_READ_PROPERTY_DATA * rp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + int apdu_len = -1; + + /* handle each object type */ + switch (rp_data->object_type) { + case OBJECT_DEVICE: + if (Device_Valid_Object_Instance_Number(rp_data->object_instance)) { + apdu_len = + Device_Encode_Property_APDU(&apdu[0], + rp_data->object_instance, rp_data->object_property, + rp_data->array_index, error_class, error_code); + } + break; + case OBJECT_ANALOG_VALUE: + if (Analog_Value_Valid_Instance(rp_data->object_instance)) { + apdu_len = + Analog_Value_Encode_Property_APDU(&apdu[0], + rp_data->object_instance, rp_data->object_property, + rp_data->array_index, error_class, error_code); + } + break; + case OBJECT_BINARY_VALUE: + if (Binary_Value_Valid_Instance(rp_data->object_instance)) { + apdu_len = + Binary_Value_Encode_Property_APDU(&apdu[0], + rp_data->object_instance, rp_data->object_property, + rp_data->array_index, error_class, error_code); + } + break; + default: + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + break; + } + + return apdu_len; +} + +void handler_read_property(uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_READ_PROPERTY_DATA data; + int len = 0; + int ack_len = 0; + int property_len = 0; + int pdu_len = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + BACNET_ADDRESS my_address; + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); + goto RP_ABORT; + } + len = rp_decode_service_request(service_request, service_len, &data); + if (len < 0) { + /* bad decoding - send an abort */ + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); + goto RP_ABORT; + } + /* most cases will be error */ + ack_len = + rp_ack_encode_apdu_init(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, &data); + /* FIXME: add buffer len as passed into function or use smart buffer */ + property_len = + Encode_Property_APDU(&Handler_Transmit_Buffer[pdu_len + ack_len], + &data, &error_class, &error_code); + if (property_len >= 0) { + len = + rp_ack_encode_apdu_object_property_end(&Handler_Transmit_Buffer + [pdu_len + property_len + ack_len]); + len += ack_len + property_len; + } else { + switch (property_len) { + /* BACnet APDU too small to fit data, so proper response is Abort */ + case BACNET_STATUS_ABORT: + len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); + break; + default: + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, + error_class, error_code); + break; + } + } + RP_ABORT: + pdu_len += len; + + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); + + return; +} diff --git a/ports/arduino_uno/uart.c b/ports/arduino_uno/uart.c index 83b5674a..c2c6b47c 100644 --- a/ports/arduino_uno/uart.c +++ b/ports/arduino_uno/uart.c @@ -1,44 +1,44 @@ -/** - * @file - * @author Miguel Fernandes - * @date 6 de Jun de 2013 - * @brief For redirecting stdout, stdin and stderr - * see http://www.appelsiini.net/2011/simple-usart-with-avr-libc - */ -#include -#include -#include -#include -#include "hardware.h" -#include "uart.h" - -void uart_init(void) -{ - UBRR0H = UBRRH_VALUE; - UBRR0L = UBRRL_VALUE; - -#if USE_2X - UCSR0A |= _BV(U2X0); -#else - UCSR0A &= ~(_BV(U2X0)); -#endif - - UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */ - UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */ -} - -void uart_putchar(char c, - FILE * stream) -{ - if (c == '\n') { - uart_putchar('\r', stream); - } - loop_until_bit_is_set(UCSR0A, UDRE0); - UDR0 = c; -} - -char uart_getchar(FILE * stream) -{ - loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */ - return UDR0; -} +/** + * @file + * @author Miguel Fernandes + * @date 6 de Jun de 2013 + * @brief For redirecting stdout, stdin and stderr + * see http://www.appelsiini.net/2011/simple-usart-with-avr-libc + */ +#include +#include +#include +#include +#include "hardware.h" +#include "uart.h" + +void uart_init(void) +{ + UBRR0H = UBRRH_VALUE; + UBRR0L = UBRRL_VALUE; + +#if USE_2X + UCSR0A |= _BV(U2X0); +#else + UCSR0A &= ~(_BV(U2X0)); +#endif + + UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */ + UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */ +} + +void uart_putchar(char c, + FILE * stream) +{ + if (c == '\n') { + uart_putchar('\r', stream); + } + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; +} + +char uart_getchar(FILE * stream) +{ + loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */ + return UDR0; +} diff --git a/ports/arduino_uno/uart.h b/ports/arduino_uno/uart.h index e503c888..55b382e2 100644 --- a/ports/arduino_uno/uart.h +++ b/ports/arduino_uno/uart.h @@ -1,27 +1,27 @@ -/** - * @file - * @author Miguel Fernandes - * @date 6 de Jun de 2013 - * @brief BACnet Virtual Link Control for Wiznet on Arduino-Uno - */ -#ifndef UART_H_ -#define UART_H_ - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -void uart_init(void); -void uart_putchar(char c, - FILE * stream); -char uart_getchar(FILE * stream); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +/** + * @file + * @author Miguel Fernandes + * @date 6 de Jun de 2013 + * @brief BACnet Virtual Link Control for Wiznet on Arduino-Uno + */ +#ifndef UART_H_ +#define UART_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +void uart_init(void); +void uart_putchar(char c, + FILE * stream); +char uart_getchar(FILE * stream); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/esp32/src/bip_init.c b/ports/esp32/src/bip_init.c index 81dd71d3..dda1765b 100644 --- a/ports/esp32/src/bip_init.c +++ b/ports/esp32/src/bip_init.c @@ -1,57 +1,57 @@ -// -// Copyleft F.Chaxel 2017 -// - -#include "esp_log.h" -#include "esp_wifi.h" - -#include "lwip/sockets.h" -#include "lwip/netdb.h" - -#include "bip.h" - -long bip_getaddrbyname( - const char *host_name) -{ - return 0; -} - -void bip_set_interface(char *ifname) -{ -} - -void bip_cleanup (void) -{ - close(bip_socket()); - bip_set_socket(-1); -} - -bool bip_init(char *ifname) -{ - - tcpip_adapter_ip_info_t ip_info = { 0 }; - - int value = 1; - - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info); - - bip_set_interface(ifname); - bip_set_port(htons(0xBAC0)); - bip_set_addr(ip_info.ip.addr); - bip_set_broadcast_addr((ip_info.ip.addr&ip_info.netmask.addr)|(~ip_info.netmask.addr)); - - int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); - struct sockaddr_in saddr = { 0 }; - - saddr.sin_family = PF_INET; - saddr.sin_port = htons(0xBAC0); - saddr.sin_addr.s_addr = htonl(INADDR_ANY); - bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)); - - setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *) &value, sizeof(value)); - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value)); - - bip_set_socket(sock); - - return true; +// +// Copyleft F.Chaxel 2017 +// + +#include "esp_log.h" +#include "esp_wifi.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +#include "bip.h" + +long bip_getaddrbyname( + const char *host_name) +{ + return 0; +} + +void bip_set_interface(char *ifname) +{ +} + +void bip_cleanup (void) +{ + close(bip_socket()); + bip_set_socket(-1); +} + +bool bip_init(char *ifname) +{ + + tcpip_adapter_ip_info_t ip_info = { 0 }; + + int value = 1; + + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info); + + bip_set_interface(ifname); + bip_set_port(htons(0xBAC0)); + bip_set_addr(ip_info.ip.addr); + bip_set_broadcast_addr((ip_info.ip.addr&ip_info.netmask.addr)|(~ip_info.netmask.addr)); + + int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + struct sockaddr_in saddr = { 0 }; + + saddr.sin_family = PF_INET; + saddr.sin_port = htons(0xBAC0); + saddr.sin_addr.s_addr = htonl(INADDR_ANY); + bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)); + + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *) &value, sizeof(value)); + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value)); + + bip_set_socket(sock); + + return true; } \ No newline at end of file diff --git a/ports/esp32/src/main.c b/ports/esp32/src/main.c index 6a0296bb..4909234c 100644 --- a/ports/esp32/src/main.c +++ b/ports/esp32/src/main.c @@ -1,215 +1,215 @@ -// -// Copyleft F.Chaxel 2017 -// - -#include "config.h" -#include "txbuf.h" -#include "client.h" - -#include "handlers.h" -#include "datalink.h" -#include "dcc.h" -#include "tsm.h" -// conflict filename address.h with another file in default include paths -#include "../lib/stack/address.h" -#include "bip.h" - -#include "device.h" -#include "ai.h" -#include "bo.h" - -#include "esp_log.h" -#include "esp_wifi.h" -#include "esp_event_loop.h" -#include "nvs_flash.h" - -#include "driver/gpio.h" - -#include "lwip/sockets.h" -#include "lwip/netdb.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - -// hidden function not in any .h files -extern uint8_t temprature_sens_read(); -extern uint32_t hall_sens_read(); - -// Wifi params -wifi_config_t wifi_config = { - .sta = { - .ssid = "myWifi", - .password = "myPass", - }, -}; - -// GPIO 5 has a Led on Sparkfun ESP32 board -#define BACNET_LED 5 - -uint8_t Handler_Transmit_Buffer[MAX_PDU] = { 0 }; -uint8_t Rx_Buf[MAX_MPDU] = { 0 }; - -EventGroupHandle_t wifi_event_group; -const static int CONNECTED_BIT = BIT0; - -/* BACnet handler, stack init, IAm */ -void StartBACnet() -{ - /* we need to handle who-is to support dynamic device binding */ - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); - - /* set the handler for all the services we don't implement */ - /* It is required to send the proper reject message... */ - apdu_set_unrecognized_service_handler_handler - (handler_unrecognized_service); - /* Set the handlers for any confirmed services that we support. */ - /* We must implement read property - it's required! */ - apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, - handler_read_property); - apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, - handler_read_property_multiple); - - apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, - handler_write_property); - apdu_set_confirmed_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV, - handler_cov_subscribe); - - address_init(); - bip_init(NULL); - Send_I_Am(&Handler_Transmit_Buffer[0]); -} - -/* wifi events handler : start & stop bacnet with an event */ -esp_err_t wifi_event_handler(void *ctx, system_event_t *event) -{ - switch(event->event_id) { - case SYSTEM_EVENT_STA_START: - esp_wifi_connect(); - break; - case SYSTEM_EVENT_STA_CONNECTED: - break ; - case SYSTEM_EVENT_STA_GOT_IP: - if (xEventGroupGetBits(wifi_event_group)!=CONNECTED_BIT) - { - xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); - StartBACnet(); - } - break; - case SYSTEM_EVENT_STA_DISCONNECTED: - /* This is a workaround as ESP32 WiFi libs don't currently - auto-reassociate. */ - esp_wifi_connect(); - xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); - bip_cleanup(); - break; - default: - break; - } - return ESP_OK; -} - -/* tcpip & wifi station start */ - -void wifi_init_station(void) -{ - tcpip_adapter_init(); - wifi_event_group = xEventGroupCreate(); - esp_event_loop_init(wifi_event_handler, NULL); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - esp_wifi_init(&cfg); - - esp_wifi_set_storage(WIFI_STORAGE_RAM); - esp_wifi_set_mode(WIFI_MODE_STA); - - esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config); - - esp_wifi_start() ; -} - -/* setup gpio & nv flash, call wifi init code */ -void setup() -{ - gpio_pad_select_gpio(BACNET_LED); - gpio_set_direction(BACNET_LED, GPIO_MODE_OUTPUT); - - gpio_set_level(BACNET_LED,0); - - esp_err_t ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES) - { - nvs_flash_erase(); - ret = nvs_flash_init(); - } - wifi_init_station(); -} - -/* Bacnet Task */ -void BACnetTask(void *pvParameters) -{ - uint16_t pdu_len = 0; - BACNET_ADDRESS src = { - 0 - }; - unsigned timeout = 1; - - // Init Bacnet objets dictionnary - Device_Init(NULL); - Device_Set_Object_Instance_Number(12); - - setup(); - - uint32_t tickcount=xTaskGetTickCount(); - - for (;;) - { - vTaskDelay(10 / portTICK_PERIOD_MS); // could be remove to speed the code - - // do nothing if not connected to wifi - xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); - { - uint32_t newtick=xTaskGetTickCount(); - - // one second elapse at least (maybe much more if Wifi was deconnected for a long) - if ((newtick=configTICK_RATE_HZ)) - { - tickcount=newtick; - dcc_timer_seconds(1); - bvlc_maintenance_timer(1); - handler_cov_timer_seconds(1); - tsm_timer_milliseconds(1000); - - // Read analog values from internal sensors - Analog_Input_Present_Value_Set(0,temprature_sens_read()); - Analog_Input_Present_Value_Set(1,hall_sens_read()); - - } - - pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); - if (pdu_len) - { - npdu_handler(&src, &Rx_Buf[0], pdu_len); - - if(Binary_Output_Present_Value(0)==BINARY_ACTIVE) - gpio_set_level(BACNET_LED,1); - else - gpio_set_level(BACNET_LED,0); - } - - handler_cov_task(); - } - } -} -/* Entry point */ -void app_main() -{ - // Cannot run BACnet code here, the default stack size is to small : 4096 byte - xTaskCreate( - BACnetTask, /* Function to implement the task */ - "BACnetTask", /* Name of the task */ - 10000, /* Stack size in words */ - NULL, /* Task input parameter */ - 20, /* Priority of the task */ - NULL); /* Task handle. */ +// +// Copyleft F.Chaxel 2017 +// + +#include "config.h" +#include "txbuf.h" +#include "client.h" + +#include "handlers.h" +#include "datalink.h" +#include "dcc.h" +#include "tsm.h" +// conflict filename address.h with another file in default include paths +#include "../lib/stack/address.h" +#include "bip.h" + +#include "device.h" +#include "ai.h" +#include "bo.h" + +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "nvs_flash.h" + +#include "driver/gpio.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" + +// hidden function not in any .h files +extern uint8_t temprature_sens_read(); +extern uint32_t hall_sens_read(); + +// Wifi params +wifi_config_t wifi_config = { + .sta = { + .ssid = "myWifi", + .password = "myPass", + }, +}; + +// GPIO 5 has a Led on Sparkfun ESP32 board +#define BACNET_LED 5 + +uint8_t Handler_Transmit_Buffer[MAX_PDU] = { 0 }; +uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +EventGroupHandle_t wifi_event_group; +const static int CONNECTED_BIT = BIT0; + +/* BACnet handler, stack init, IAm */ +void StartBACnet() +{ + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + + /* set the handler for all the services we don't implement */ + /* It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler + (handler_unrecognized_service); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); + + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV, + handler_cov_subscribe); + + address_init(); + bip_init(NULL); + Send_I_Am(&Handler_Transmit_Buffer[0]); +} + +/* wifi events handler : start & stop bacnet with an event */ +esp_err_t wifi_event_handler(void *ctx, system_event_t *event) +{ + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + esp_wifi_connect(); + break; + case SYSTEM_EVENT_STA_CONNECTED: + break ; + case SYSTEM_EVENT_STA_GOT_IP: + if (xEventGroupGetBits(wifi_event_group)!=CONNECTED_BIT) + { + xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); + StartBACnet(); + } + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + /* This is a workaround as ESP32 WiFi libs don't currently + auto-reassociate. */ + esp_wifi_connect(); + xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); + bip_cleanup(); + break; + default: + break; + } + return ESP_OK; +} + +/* tcpip & wifi station start */ + +void wifi_init_station(void) +{ + tcpip_adapter_init(); + wifi_event_group = xEventGroupCreate(); + esp_event_loop_init(wifi_event_handler, NULL); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + esp_wifi_init(&cfg); + + esp_wifi_set_storage(WIFI_STORAGE_RAM); + esp_wifi_set_mode(WIFI_MODE_STA); + + esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config); + + esp_wifi_start() ; +} + +/* setup gpio & nv flash, call wifi init code */ +void setup() +{ + gpio_pad_select_gpio(BACNET_LED); + gpio_set_direction(BACNET_LED, GPIO_MODE_OUTPUT); + + gpio_set_level(BACNET_LED,0); + + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) + { + nvs_flash_erase(); + ret = nvs_flash_init(); + } + wifi_init_station(); +} + +/* Bacnet Task */ +void BACnetTask(void *pvParameters) +{ + uint16_t pdu_len = 0; + BACNET_ADDRESS src = { + 0 + }; + unsigned timeout = 1; + + // Init Bacnet objets dictionnary + Device_Init(NULL); + Device_Set_Object_Instance_Number(12); + + setup(); + + uint32_t tickcount=xTaskGetTickCount(); + + for (;;) + { + vTaskDelay(10 / portTICK_PERIOD_MS); // could be remove to speed the code + + // do nothing if not connected to wifi + xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY); + { + uint32_t newtick=xTaskGetTickCount(); + + // one second elapse at least (maybe much more if Wifi was deconnected for a long) + if ((newtick=configTICK_RATE_HZ)) + { + tickcount=newtick; + dcc_timer_seconds(1); + bvlc_maintenance_timer(1); + handler_cov_timer_seconds(1); + tsm_timer_milliseconds(1000); + + // Read analog values from internal sensors + Analog_Input_Present_Value_Set(0,temprature_sens_read()); + Analog_Input_Present_Value_Set(1,hall_sens_read()); + + } + + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + if (pdu_len) + { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + + if(Binary_Output_Present_Value(0)==BINARY_ACTIVE) + gpio_set_level(BACNET_LED,1); + else + gpio_set_level(BACNET_LED,0); + } + + handler_cov_task(); + } + } +} +/* Entry point */ +void app_main() +{ + // Cannot run BACnet code here, the default stack size is to small : 4096 byte + xTaskCreate( + BACnetTask, /* Function to implement the task */ + "BACnetTask", /* Name of the task */ + 10000, /* Stack size in words */ + NULL, /* Task input parameter */ + 20, /* Priority of the task */ + NULL); /* Task handle. */ } \ No newline at end of file diff --git a/ports/esp32/src/sdkconfig.h b/ports/esp32/src/sdkconfig.h index a3b619fe..64ef778b 100644 --- a/ports/esp32/src/sdkconfig.h +++ b/ports/esp32/src/sdkconfig.h @@ -1,125 +1,125 @@ -/* - * - * Automatically generated file; DO NOT EDIT. - * Espressif IoT Development Framework Configuration - * - */ -#define CONFIG_GATTC_ENABLE 1 -#define CONFIG_ESP32_PHY_MAX_TX_POWER 20 -#define CONFIG_PHY_ENABLED 1 -#define CONFIG_TRACEMEM_RESERVE_DRAM 0x0 -#define CONFIG_FREERTOS_MAX_TASK_NAME_LEN 16 -#define CONFIG_BLE_SMP_ENABLE 1 -#define CONFIG_IPC_TASK_STACK_SIZE 1024 -#define CONFIG_ESPTOOLPY_FLASHFREQ "40m" -#define CONFIG_NEWLIB_STDOUT_ADDCR 1 -#define CONFIG_TASK_WDT_CHECK_IDLE_TASK 1 -#define CONFIG_ESPTOOLPY_FLASHSIZE "2MB" -#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER 1 -#define CONFIG_ETHERNET 1 -#define CONFIG_INT_WDT 1 -#define CONFIG_ESPTOOLPY_FLASHFREQ_40M 1 -#define CONFIG_LOG_BOOTLOADER_LEVEL_INFO 1 -#define CONFIG_ESPTOOLPY_FLASHSIZE_2MB 1 -#define CONFIG_AWS_IOT_MQTT_PORT 8883 -#define CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS 1 -#define CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM 10 -#define CONFIG_LOG_DEFAULT_LEVEL_INFO 1 -#define CONFIG_BT_RESERVE_DRAM 0x10000 -#define CONFIG_ESP32_PANIC_PRINT_REBOOT 1 -#define CONFIG_CONSOLE_UART_BAUDRATE 115200 -#define CONFIG_LWIP_MAX_SOCKETS 10 -#define CONFIG_EMAC_TASK_PRIORITY 20 -#define CONFIG_TIMER_TASK_STACK_DEPTH 2048 -#define CONFIG_FATFS_CODEPAGE 1 -#define CONFIG_ESP32_DEFAULT_CPU_FREQ_160 1 -#define CONFIG_ULP_COPROC_RESERVE_MEM 0 -#define CONFIG_ESPTOOLPY_BAUD 115200 -#define CONFIG_INT_WDT_CHECK_CPU1 1 -#define CONFIG_FLASHMODE_DIO 1 -#define CONFIG_ESPTOOLPY_AFTER_RESET 1 -#define CONFIG_TOOLPREFIX "xtensa-esp32-elf-" -#define CONFIG_FREERTOS_IDLE_TASK_STACKSIZE 1024 -#define CONFIG_ESP32_WIFI_AMPDU_ENABLED 1 -#define CONFIG_CONSOLE_UART_NUM 0 -#define CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC 1 -#define CONFIG_ESPTOOLPY_BAUD_115200B 1 -#define CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX 0 -#define CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS 1 -#define CONFIG_CONSOLE_UART_DEFAULT 1 -#define CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN 16384 -#define CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS 4 -#define CONFIG_ESPTOOLPY_FLASHSIZE_DETECT 1 -#define CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE 1 -#define CONFIG_BTDM_CONTROLLER_RUN_CPU 0 -#define CONFIG_TCPIP_TASK_STACK_SIZE 2560 -#define CONFIG_TASK_WDT 1 -#define CONFIG_MAIN_TASK_STACK_SIZE 4096 -#define CONFIG_TASK_WDT_TIMEOUT_S 5 -#define CONFIG_INT_WDT_TIMEOUT_MS 300 -#define CONFIG_ESPTOOLPY_FLASHMODE "dio" -#define CONFIG_BTC_TASK_STACK_SIZE 3072 -#define CONFIG_BLUEDROID_ENABLED 1 -#define CONFIG_ESPTOOLPY_BEFORE "default_reset" -#define CONFIG_LOG_DEFAULT_LEVEL 3 -#define CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION 1 -#define CONFIG_TIMER_QUEUE_LENGTH 10 -#define CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM 32 -#define CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER 20 -#define CONFIG_ESP32_RTC_CLK_CAL_CYCLES 1024 -#define CONFIG_ESP32_WIFI_NVS_ENABLED 1 -#define CONFIG_AWS_IOT_SDK 1 -#define CONFIG_DMA_RX_BUF_NUM 10 -#define CONFIG_TCP_SYNMAXRTX 6 -#define CONFIG_PYTHON "python" -#define CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 1 -#define CONFIG_ESPTOOLPY_COMPRESSED 1 -#define CONFIG_PARTITION_TABLE_FILENAME "partitions_singleapp.csv" -#define CONFIG_LWIP_DHCP_MAX_NTP_SERVERS 1 -#define CONFIG_PARTITION_TABLE_SINGLE_APP 1 -#define CONFIG_WIFI_ENABLED 1 -#define CONFIG_LWIP_DHCP_DOES_ARP_CHECK 1 -#define CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE 4096 -#define CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY 2000 -#define CONFIG_ESP32_APPTRACE_DEST_NONE 1 -#define CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET 0x10000 -#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM 32 -#define CONFIG_FATFS_CODEPAGE_ASCII 1 -#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 1 -#define CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ 160 -#define CONFIG_FREERTOS_HZ 100 -#define CONFIG_LOG_COLORS 1 -#define CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE 1 -#define CONFIG_FREERTOS_ASSERT_FAIL_ABORT 1 -#define CONFIG_ESP32_XTAL_FREQ 0 -#define CONFIG_MONITOR_BAUD_115200B 1 -#define CONFIG_LOG_BOOTLOADER_LEVEL 3 -#define CONFIG_SMP_ENABLE 1 -#define CONFIG_ESPTOOLPY_BEFORE_RESET 1 -#define CONFIG_ESPTOOLPY_BAUD_OTHER_VAL 115200 -#define CONFIG_ESP32_XTAL_FREQ_AUTO 1 -#define CONFIG_TCP_MAXRTX 12 -#define CONFIG_ESPTOOLPY_AFTER "hard_reset" -#define CONFIG_DMA_TX_BUF_NUM 10 -#define CONFIG_ESP32_DEBUG_OCDAWARE 1 -#define CONFIG_TIMER_TASK_PRIORITY 1 -#define CONFIG_BT_ENABLED 1 -#define CONFIG_MONITOR_BAUD 115200 -#define CONFIG_FREERTOS_CORETIMER_0 1 -#define CONFIG_PARTITION_TABLE_CUSTOM_FILENAME "partitions.csv" -#define CONFIG_MBEDTLS_HAVE_TIME 1 -#define CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY 1 -#define CONFIG_GATTS_ENABLE 1 -#define CONFIG_FREERTOS_ISR_STACKSIZE 1536 -#define CONFIG_OPENSSL_ASSERT_DO_NOTHING 1 -#define CONFIG_AWS_IOT_MQTT_HOST "" -#define CONFIG_SYSTEM_EVENT_QUEUE_SIZE 32 -#define CONFIG_BT_ACL_CONNECTIONS 4 -#define CONFIG_FATFS_MAX_LFN 255 -#define CONFIG_ESP32_WIFI_TX_BUFFER_TYPE 1 -#define CONFIG_APP_OFFSET 0x10000 -#define CONFIG_MEMMAP_SMP 1 -#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 -#define CONFIG_MONITOR_BAUD_OTHER_VAL 115200 -#define CONFIG_ESPTOOLPY_PORT "/dev/ttyUSB0" -#define CONFIG_OPTIMIZATION_LEVEL_RELEASE 1 +/* + * + * Automatically generated file; DO NOT EDIT. + * Espressif IoT Development Framework Configuration + * + */ +#define CONFIG_GATTC_ENABLE 1 +#define CONFIG_ESP32_PHY_MAX_TX_POWER 20 +#define CONFIG_PHY_ENABLED 1 +#define CONFIG_TRACEMEM_RESERVE_DRAM 0x0 +#define CONFIG_FREERTOS_MAX_TASK_NAME_LEN 16 +#define CONFIG_BLE_SMP_ENABLE 1 +#define CONFIG_IPC_TASK_STACK_SIZE 1024 +#define CONFIG_ESPTOOLPY_FLASHFREQ "40m" +#define CONFIG_NEWLIB_STDOUT_ADDCR 1 +#define CONFIG_TASK_WDT_CHECK_IDLE_TASK 1 +#define CONFIG_ESPTOOLPY_FLASHSIZE "2MB" +#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER 1 +#define CONFIG_ETHERNET 1 +#define CONFIG_INT_WDT 1 +#define CONFIG_ESPTOOLPY_FLASHFREQ_40M 1 +#define CONFIG_LOG_BOOTLOADER_LEVEL_INFO 1 +#define CONFIG_ESPTOOLPY_FLASHSIZE_2MB 1 +#define CONFIG_AWS_IOT_MQTT_PORT 8883 +#define CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS 1 +#define CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM 10 +#define CONFIG_LOG_DEFAULT_LEVEL_INFO 1 +#define CONFIG_BT_RESERVE_DRAM 0x10000 +#define CONFIG_ESP32_PANIC_PRINT_REBOOT 1 +#define CONFIG_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_LWIP_MAX_SOCKETS 10 +#define CONFIG_EMAC_TASK_PRIORITY 20 +#define CONFIG_TIMER_TASK_STACK_DEPTH 2048 +#define CONFIG_FATFS_CODEPAGE 1 +#define CONFIG_ESP32_DEFAULT_CPU_FREQ_160 1 +#define CONFIG_ULP_COPROC_RESERVE_MEM 0 +#define CONFIG_ESPTOOLPY_BAUD 115200 +#define CONFIG_INT_WDT_CHECK_CPU1 1 +#define CONFIG_FLASHMODE_DIO 1 +#define CONFIG_ESPTOOLPY_AFTER_RESET 1 +#define CONFIG_TOOLPREFIX "xtensa-esp32-elf-" +#define CONFIG_FREERTOS_IDLE_TASK_STACKSIZE 1024 +#define CONFIG_ESP32_WIFI_AMPDU_ENABLED 1 +#define CONFIG_CONSOLE_UART_NUM 0 +#define CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC 1 +#define CONFIG_ESPTOOLPY_BAUD_115200B 1 +#define CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX 0 +#define CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS 1 +#define CONFIG_CONSOLE_UART_DEFAULT 1 +#define CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN 16384 +#define CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS 4 +#define CONFIG_ESPTOOLPY_FLASHSIZE_DETECT 1 +#define CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE 1 +#define CONFIG_BTDM_CONTROLLER_RUN_CPU 0 +#define CONFIG_TCPIP_TASK_STACK_SIZE 2560 +#define CONFIG_TASK_WDT 1 +#define CONFIG_MAIN_TASK_STACK_SIZE 4096 +#define CONFIG_TASK_WDT_TIMEOUT_S 5 +#define CONFIG_INT_WDT_TIMEOUT_MS 300 +#define CONFIG_ESPTOOLPY_FLASHMODE "dio" +#define CONFIG_BTC_TASK_STACK_SIZE 3072 +#define CONFIG_BLUEDROID_ENABLED 1 +#define CONFIG_ESPTOOLPY_BEFORE "default_reset" +#define CONFIG_LOG_DEFAULT_LEVEL 3 +#define CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION 1 +#define CONFIG_TIMER_QUEUE_LENGTH 10 +#define CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM 32 +#define CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER 20 +#define CONFIG_ESP32_RTC_CLK_CAL_CYCLES 1024 +#define CONFIG_ESP32_WIFI_NVS_ENABLED 1 +#define CONFIG_AWS_IOT_SDK 1 +#define CONFIG_DMA_RX_BUF_NUM 10 +#define CONFIG_TCP_SYNMAXRTX 6 +#define CONFIG_PYTHON "python" +#define CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 1 +#define CONFIG_ESPTOOLPY_COMPRESSED 1 +#define CONFIG_PARTITION_TABLE_FILENAME "partitions_singleapp.csv" +#define CONFIG_LWIP_DHCP_MAX_NTP_SERVERS 1 +#define CONFIG_PARTITION_TABLE_SINGLE_APP 1 +#define CONFIG_WIFI_ENABLED 1 +#define CONFIG_LWIP_DHCP_DOES_ARP_CHECK 1 +#define CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE 4096 +#define CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY 2000 +#define CONFIG_ESP32_APPTRACE_DEST_NONE 1 +#define CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET 0x10000 +#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM 32 +#define CONFIG_FATFS_CODEPAGE_ASCII 1 +#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 1 +#define CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ 160 +#define CONFIG_FREERTOS_HZ 100 +#define CONFIG_LOG_COLORS 1 +#define CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE 1 +#define CONFIG_FREERTOS_ASSERT_FAIL_ABORT 1 +#define CONFIG_ESP32_XTAL_FREQ 0 +#define CONFIG_MONITOR_BAUD_115200B 1 +#define CONFIG_LOG_BOOTLOADER_LEVEL 3 +#define CONFIG_SMP_ENABLE 1 +#define CONFIG_ESPTOOLPY_BEFORE_RESET 1 +#define CONFIG_ESPTOOLPY_BAUD_OTHER_VAL 115200 +#define CONFIG_ESP32_XTAL_FREQ_AUTO 1 +#define CONFIG_TCP_MAXRTX 12 +#define CONFIG_ESPTOOLPY_AFTER "hard_reset" +#define CONFIG_DMA_TX_BUF_NUM 10 +#define CONFIG_ESP32_DEBUG_OCDAWARE 1 +#define CONFIG_TIMER_TASK_PRIORITY 1 +#define CONFIG_BT_ENABLED 1 +#define CONFIG_MONITOR_BAUD 115200 +#define CONFIG_FREERTOS_CORETIMER_0 1 +#define CONFIG_PARTITION_TABLE_CUSTOM_FILENAME "partitions.csv" +#define CONFIG_MBEDTLS_HAVE_TIME 1 +#define CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY 1 +#define CONFIG_GATTS_ENABLE 1 +#define CONFIG_FREERTOS_ISR_STACKSIZE 1536 +#define CONFIG_OPENSSL_ASSERT_DO_NOTHING 1 +#define CONFIG_AWS_IOT_MQTT_HOST "" +#define CONFIG_SYSTEM_EVENT_QUEUE_SIZE 32 +#define CONFIG_BT_ACL_CONNECTIONS 4 +#define CONFIG_FATFS_MAX_LFN 255 +#define CONFIG_ESP32_WIFI_TX_BUFFER_TYPE 1 +#define CONFIG_APP_OFFSET 0x10000 +#define CONFIG_MEMMAP_SMP 1 +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_MONITOR_BAUD_OTHER_VAL 115200 +#define CONFIG_ESPTOOLPY_PORT "/dev/ttyUSB0" +#define CONFIG_OPTIMIZATION_LEVEL_RELEASE 1 diff --git a/ports/linux/bip6.c b/ports/linux/bip6.c index 0cf37911..1f0e4d67 100644 --- a/ports/linux/bip6.c +++ b/ports/linux/bip6.c @@ -1,453 +1,453 @@ -/*####COPYRIGHTBEGIN#### - ------------------------------------------- - Copyright (C) 2016 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 -#include -#include /* for standard integer types uint8_t etc. */ -#include /* for the standard bool type. */ -#include "bacdcode.h" -#include "config.h" -#include "bip6.h" -#include "debug.h" -#include "device.h" -#include "net.h" -#include - -static void debug_print_ipv6(const char *str, const struct in6_addr * addr) { - debug_printf( "BIP6: %s %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", - str, - (int)addr->s6_addr[0], (int)addr->s6_addr[1], - (int)addr->s6_addr[2], (int)addr->s6_addr[3], - (int)addr->s6_addr[4], (int)addr->s6_addr[5], - (int)addr->s6_addr[6], (int)addr->s6_addr[7], - (int)addr->s6_addr[8], (int)addr->s6_addr[9], - (int)addr->s6_addr[10], (int)addr->s6_addr[11], - (int)addr->s6_addr[12], (int)addr->s6_addr[13], - (int)addr->s6_addr[14], (int)addr->s6_addr[15]); -} - -/** @file linux/bip6.c Initializes BACnet/IPv6 interface (Linux). */ - -/* unix socket */ -static int BIP6_Socket = -1; -/* local address - filled by init functions */ -static BACNET_IP6_ADDRESS BIP6_Addr; -static BACNET_IP6_ADDRESS BIP6_Broadcast_Addr; - -/** - * Set the interface name. On Linux, ifname is the /dev/ name of the interface. - * - * @param ifname - C string for name or text address - */ -void bip6_set_interface( - char *ifname) -{ - struct ifaddrs *ifa, *ifa_tmp; - struct sockaddr_in6 *sin; - bool found = false; - - if (getifaddrs(&ifa) == -1) { - perror("BIP6: getifaddrs failed"); - exit(1); - } - ifa_tmp = ifa; - debug_printf("BIP6: seeking interface: %s\n", ifname); - while (ifa_tmp) { - if ((ifa_tmp->ifa_addr) && - (ifa_tmp->ifa_addr->sa_family == AF_INET6)) { - debug_printf("BIP6: found interface: %s\n", ifa_tmp->ifa_name); - } - if ((ifa_tmp->ifa_addr) && - (ifa_tmp->ifa_addr->sa_family == AF_INET6) && - (strcasecmp(ifa_tmp->ifa_name,ifname) == 0)) { - sin = (struct sockaddr_in6*) ifa_tmp->ifa_addr; - bvlc6_address_set(&BIP6_Addr, - ntohs(sin->sin6_addr.s6_addr16[0]), - ntohs(sin->sin6_addr.s6_addr16[1]), - ntohs(sin->sin6_addr.s6_addr16[2]), - ntohs(sin->sin6_addr.s6_addr16[3]), - ntohs(sin->sin6_addr.s6_addr16[4]), - ntohs(sin->sin6_addr.s6_addr16[5]), - ntohs(sin->sin6_addr.s6_addr16[6]), - ntohs(sin->sin6_addr.s6_addr16[7])); - debug_print_ipv6(ifname, (&sin->sin6_addr)); - found = true; - break; - } - ifa_tmp = ifa_tmp->ifa_next; - } - if (!found) { - debug_printf("BIP6: unable to set interface: %s\n", ifname); - exit(1); - } -} - -/** - * Set the BACnet IPv6 UDP port number - * - * @param port - IPv6 UDP port number - */ -void bip6_set_port( - uint16_t port) -{ - BIP6_Addr.port = port; - BIP6_Broadcast_Addr.port = port; -} - -/** - * Get the BACnet IPv6 UDP port number - * - * @return IPv6 UDP port number - */ -uint16_t bip6_get_port( - void) -{ - return BIP6_Addr.port; -} - -/** - * Get the BACnet broadcast address for my interface. - * Used as dest address in messages sent as BROADCAST - * - * @param addr - IPv6 source address - */ -void bip6_get_broadcast_address( - BACNET_ADDRESS * addr) -{ - if (addr) { - addr->net = BACNET_BROADCAST_NETWORK; - addr->mac_len = 0; - addr->len = 0; - } -} - -/** - * Get the IPv6 address for my interface. Used as src address in messages sent. - * - * @param addr - IPv6 source address - */ -void bip6_get_my_address( - BACNET_ADDRESS * addr) -{ - uint32_t device_id = 0; - - if (addr) { - device_id = Device_Object_Instance_Number(); - bvlc6_vmac_address_set(addr, device_id); - } -} - -/** - * Set the BACnet/IP address - * - * @param addr - network IPv6 address - */ -bool bip6_set_addr( - BACNET_IP6_ADDRESS *addr) -{ - return bvlc6_address_copy(&BIP6_Addr, addr); -} - -/** - * Get the BACnet/IP address - * - * @return BACnet/IP address - */ -bool bip6_get_addr( - BACNET_IP6_ADDRESS *addr) -{ - return bvlc6_address_copy(addr, &BIP6_Addr); -} - -/** - * Set the BACnet/IP address - * - * @param addr - network IPv6 address - */ -bool bip6_set_broadcast_addr( - BACNET_IP6_ADDRESS *addr) -{ - return bvlc6_address_copy(&BIP6_Broadcast_Addr, addr); -} - -/** - * Get the BACnet/IP address - * - * @return BACnet/IP address - */ -bool bip6_get_broadcast_addr( - BACNET_IP6_ADDRESS *addr) -{ - return bvlc6_address_copy(addr, &BIP6_Broadcast_Addr); -} - -/** - * The send function for BACnet/IPv6 driver layer - * - * @param dest - Points to a BACNET_IP6_ADDRESS structure containing the - * destination address. - * @param mtu - the bytes of data to send - * @param mtu_len - the number of bytes of data to send - * - * @return Upon successful completion, returns the number of bytes sent. - * Otherwise, -1 shall be returned and errno set to indicate the error. - */ -int bip6_send_mpdu( - BACNET_IP6_ADDRESS *dest, - uint8_t * mtu, - uint16_t mtu_len) -{ - struct sockaddr_in6 bvlc_dest = { 0 }; - uint16_t addr16[8]; - - /* assumes that the driver has already been initialized */ - if (BIP6_Socket < 0) { - return 0; - } - /* load destination IP address */ - bvlc_dest.sin6_family = AF_INET6; - bvlc6_address_get(dest, &addr16[0], &addr16[1], &addr16[2], &addr16[3], - &addr16[4], &addr16[5], &addr16[6], &addr16[7]); - bvlc_dest.sin6_addr.s6_addr16[0] = htons(addr16[0]); - bvlc_dest.sin6_addr.s6_addr16[1] = htons(addr16[1]); - bvlc_dest.sin6_addr.s6_addr16[2] = htons(addr16[2]); - bvlc_dest.sin6_addr.s6_addr16[3] = htons(addr16[3]); - bvlc_dest.sin6_addr.s6_addr16[4] = htons(addr16[4]); - bvlc_dest.sin6_addr.s6_addr16[5] = htons(addr16[5]); - bvlc_dest.sin6_addr.s6_addr16[6] = htons(addr16[6]); - bvlc_dest.sin6_addr.s6_addr16[7] = htons(addr16[7]); - bvlc_dest.sin6_port = htons(dest->port); - debug_print_ipv6("Sending MPDU->", &bvlc_dest.sin6_addr); - /* Send the packet */ - return sendto(BIP6_Socket, (char *) mtu, mtu_len, 0, - (struct sockaddr *) &bvlc_dest, sizeof(bvlc_dest)); -} - -/** - * BACnet/IP Datalink Receive handler. - * - * @param src - returns the source address - * @param npdu - returns the NPDU buffer - * @param max_npdu -maximum size of the NPDU buffer - * @param timeout - number of milliseconds to wait for a packet - * - * @return Number of bytes received, or 0 if none or timeout. - */ -uint16_t bip6_receive( - BACNET_ADDRESS * src, - uint8_t * npdu, - uint16_t max_npdu, - unsigned timeout) -{ - uint16_t npdu_len = 0; /* return value */ - fd_set read_fds; - int max = 0; - struct timeval select_timeout; - struct sockaddr_in6 sin = { 0 }; - BACNET_IP6_ADDRESS addr = {{ 0 }}; - socklen_t sin_len = sizeof(sin); - int received_bytes = 0; - int offset = 0; - uint16_t i = 0; - - /* Make sure the socket is open */ - if (BIP6_Socket < 0) { - return 0; - } - /* we could just use a non-blocking socket, but that consumes all - the CPU time. We can use a timeout; it is only supported as - a select. */ - if (timeout >= 1000) { - select_timeout.tv_sec = timeout / 1000; - select_timeout.tv_usec = - 1000 * (timeout - select_timeout.tv_sec * 1000); - } else { - select_timeout.tv_sec = 0; - select_timeout.tv_usec = 1000 * timeout; - } - FD_ZERO(&read_fds); - FD_SET(BIP6_Socket, &read_fds); - max = BIP6_Socket; - /* see if there is a packet for us */ - if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { - received_bytes = - recvfrom(BIP6_Socket, (char *) &npdu[0], max_npdu, 0, - (struct sockaddr *) &sin, &sin_len); - } else { - return 0; - } - /* See if there is a problem */ - if (received_bytes < 0) { - return 0; - } - /* no problem, just no bytes */ - if (received_bytes == 0) { - return 0; - } - /* the signature of a BACnet/IPv6 packet */ - if (npdu[0] != BVLL_TYPE_BACNET_IP6) { - return 0; - } - /* pass the packet into the BBMD handler */ - debug_print_ipv6("Received MPDU->", &sin.sin6_addr); - bvlc6_address_set(&addr, - ntohs(sin.sin6_addr.s6_addr16[0]), - ntohs(sin.sin6_addr.s6_addr16[1]), - ntohs(sin.sin6_addr.s6_addr16[2]), - ntohs(sin.sin6_addr.s6_addr16[3]), - ntohs(sin.sin6_addr.s6_addr16[4]), - ntohs(sin.sin6_addr.s6_addr16[5]), - ntohs(sin.sin6_addr.s6_addr16[6]), - ntohs(sin.sin6_addr.s6_addr16[7])); - addr.port = ntohs(sin.sin6_port); - offset = bvlc6_handler(&addr, src, npdu, received_bytes); - if (offset > 0) { - npdu_len = received_bytes - offset; - if (npdu_len <= max_npdu) { - /* shift the buffer to return a valid NPDU */ - for (i = 0; i < npdu_len; i++) { - npdu[i] = npdu[offset + i]; - } - } else { - npdu_len = 0; - } - } - - return npdu_len; -} - -/** Cleanup and close out the BACnet/IP services by closing the socket. - * @ingroup DLBIP6 - */ -void bip6_cleanup( - void) -{ - if (BIP6_Socket != -1) { - close(BIP6_Socket); - } - BIP6_Socket = -1; - - return; -} - -/** Initialize the BACnet/IP services at the given interface. - * @ingroup DLBIP6 - * -# Gets the local IP address and local broadcast address from the system, - * and saves it into the BACnet/IPv6 data structures. - * -# Opens a UDP socket - * -# Configures the socket for sending and receiving - * -# Configures the socket so it can send multicasts - * -# Binds the socket to the local IP address at the specified port for - * BACnet/IPv6 (by default, 0xBAC0 = 47808). - * - * @note For Linux, ifname is eth0, ath0, arc0, and others. - * - * @param ifname [in] The named interface to use for the network layer. - * If NULL, the "eth0" interface is assigned. - * @return True if the socket is successfully opened for BACnet/IP, - * else False if the socket functions fail. - */ -bool bip6_init( - char *ifname) -{ - int status = 0; /* return from socket lib calls */ - struct sockaddr_in6 server = {0}; - struct in6_addr broadcast_address; - struct ipv6_mreq join_request; - int sockopt = 0; - - if (ifname) { - bip6_set_interface(ifname); - } else { - bip6_set_interface("eth0"); - } - if (BIP6_Addr.port == 0) { - bip6_set_port(0xBAC0); - } - debug_printf("BIP6: IPv6 UDP port: 0x%04X\n", htons(BIP6_Addr.port)); - if (BIP6_Broadcast_Addr.address[0] == 0) { - bvlc6_address_set(&BIP6_Broadcast_Addr, - BIP6_MULTICAST_SITE_LOCAL, 0, 0, 0, 0, 0, 0, - BIP6_MULTICAST_GROUP_ID); - } - /* assumes that the driver has already been initialized */ - BIP6_Socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (BIP6_Socket < 0) - return false; - /* Allow us to use the same socket for sending and receiving */ - /* This makes sure that the src port is correct when sending */ - sockopt = 1; - status = - setsockopt(BIP6_Socket, SOL_SOCKET, SO_REUSEADDR, &sockopt, - sizeof(sockopt)); - if (status < 0) { - close(BIP6_Socket); - BIP6_Socket = -1; - return status; - } - /* allow us to send a broadcast */ - status = - setsockopt(BIP6_Socket, SOL_SOCKET, SO_BROADCAST, &sockopt, - sizeof(sockopt)); - if (status < 0) { - close(BIP6_Socket); - BIP6_Socket = -1; - return false; - } - /* subscribe to a multicast address */ - memcpy(&broadcast_address.s6_addr[0], &BIP6_Broadcast_Addr.address[0], IP6_ADDRESS_MAX); - memcpy(&join_request.ipv6mr_multiaddr, &broadcast_address, sizeof(struct in6_addr)); - /* Let system choose the interface */ - join_request.ipv6mr_interface = 0; - status = setsockopt(BIP6_Socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, - &join_request, sizeof(join_request)); - if (status < 0) { - perror("BIP: setsockopt(IPV6_JOIN_GROUP)"); - } - - /* bind the socket to the local port number and IP address */ - server.sin6_family = AF_INET6; - server.sin6_addr = in6addr_any; - server.sin6_port = htons(BIP6_Addr.port); - status = bind(BIP6_Socket, (const void*)&server, sizeof(server)); - if (status < 0) { - perror("BIP: bind"); - close(BIP6_Socket); - BIP6_Socket = -1; - return false; - } - bvlc6_init(); - - return true; -} +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2016 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 +#include +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "config.h" +#include "bip6.h" +#include "debug.h" +#include "device.h" +#include "net.h" +#include + +static void debug_print_ipv6(const char *str, const struct in6_addr * addr) { + debug_printf( "BIP6: %s %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", + str, + (int)addr->s6_addr[0], (int)addr->s6_addr[1], + (int)addr->s6_addr[2], (int)addr->s6_addr[3], + (int)addr->s6_addr[4], (int)addr->s6_addr[5], + (int)addr->s6_addr[6], (int)addr->s6_addr[7], + (int)addr->s6_addr[8], (int)addr->s6_addr[9], + (int)addr->s6_addr[10], (int)addr->s6_addr[11], + (int)addr->s6_addr[12], (int)addr->s6_addr[13], + (int)addr->s6_addr[14], (int)addr->s6_addr[15]); +} + +/** @file linux/bip6.c Initializes BACnet/IPv6 interface (Linux). */ + +/* unix socket */ +static int BIP6_Socket = -1; +/* local address - filled by init functions */ +static BACNET_IP6_ADDRESS BIP6_Addr; +static BACNET_IP6_ADDRESS BIP6_Broadcast_Addr; + +/** + * Set the interface name. On Linux, ifname is the /dev/ name of the interface. + * + * @param ifname - C string for name or text address + */ +void bip6_set_interface( + char *ifname) +{ + struct ifaddrs *ifa, *ifa_tmp; + struct sockaddr_in6 *sin; + bool found = false; + + if (getifaddrs(&ifa) == -1) { + perror("BIP6: getifaddrs failed"); + exit(1); + } + ifa_tmp = ifa; + debug_printf("BIP6: seeking interface: %s\n", ifname); + while (ifa_tmp) { + if ((ifa_tmp->ifa_addr) && + (ifa_tmp->ifa_addr->sa_family == AF_INET6)) { + debug_printf("BIP6: found interface: %s\n", ifa_tmp->ifa_name); + } + if ((ifa_tmp->ifa_addr) && + (ifa_tmp->ifa_addr->sa_family == AF_INET6) && + (strcasecmp(ifa_tmp->ifa_name,ifname) == 0)) { + sin = (struct sockaddr_in6*) ifa_tmp->ifa_addr; + bvlc6_address_set(&BIP6_Addr, + ntohs(sin->sin6_addr.s6_addr16[0]), + ntohs(sin->sin6_addr.s6_addr16[1]), + ntohs(sin->sin6_addr.s6_addr16[2]), + ntohs(sin->sin6_addr.s6_addr16[3]), + ntohs(sin->sin6_addr.s6_addr16[4]), + ntohs(sin->sin6_addr.s6_addr16[5]), + ntohs(sin->sin6_addr.s6_addr16[6]), + ntohs(sin->sin6_addr.s6_addr16[7])); + debug_print_ipv6(ifname, (&sin->sin6_addr)); + found = true; + break; + } + ifa_tmp = ifa_tmp->ifa_next; + } + if (!found) { + debug_printf("BIP6: unable to set interface: %s\n", ifname); + exit(1); + } +} + +/** + * Set the BACnet IPv6 UDP port number + * + * @param port - IPv6 UDP port number + */ +void bip6_set_port( + uint16_t port) +{ + BIP6_Addr.port = port; + BIP6_Broadcast_Addr.port = port; +} + +/** + * Get the BACnet IPv6 UDP port number + * + * @return IPv6 UDP port number + */ +uint16_t bip6_get_port( + void) +{ + return BIP6_Addr.port; +} + +/** + * Get the BACnet broadcast address for my interface. + * Used as dest address in messages sent as BROADCAST + * + * @param addr - IPv6 source address + */ +void bip6_get_broadcast_address( + BACNET_ADDRESS * addr) +{ + if (addr) { + addr->net = BACNET_BROADCAST_NETWORK; + addr->mac_len = 0; + addr->len = 0; + } +} + +/** + * Get the IPv6 address for my interface. Used as src address in messages sent. + * + * @param addr - IPv6 source address + */ +void bip6_get_my_address( + BACNET_ADDRESS * addr) +{ + uint32_t device_id = 0; + + if (addr) { + device_id = Device_Object_Instance_Number(); + bvlc6_vmac_address_set(addr, device_id); + } +} + +/** + * Set the BACnet/IP address + * + * @param addr - network IPv6 address + */ +bool bip6_set_addr( + BACNET_IP6_ADDRESS *addr) +{ + return bvlc6_address_copy(&BIP6_Addr, addr); +} + +/** + * Get the BACnet/IP address + * + * @return BACnet/IP address + */ +bool bip6_get_addr( + BACNET_IP6_ADDRESS *addr) +{ + return bvlc6_address_copy(addr, &BIP6_Addr); +} + +/** + * Set the BACnet/IP address + * + * @param addr - network IPv6 address + */ +bool bip6_set_broadcast_addr( + BACNET_IP6_ADDRESS *addr) +{ + return bvlc6_address_copy(&BIP6_Broadcast_Addr, addr); +} + +/** + * Get the BACnet/IP address + * + * @return BACnet/IP address + */ +bool bip6_get_broadcast_addr( + BACNET_IP6_ADDRESS *addr) +{ + return bvlc6_address_copy(addr, &BIP6_Broadcast_Addr); +} + +/** + * The send function for BACnet/IPv6 driver layer + * + * @param dest - Points to a BACNET_IP6_ADDRESS structure containing the + * destination address. + * @param mtu - the bytes of data to send + * @param mtu_len - the number of bytes of data to send + * + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +int bip6_send_mpdu( + BACNET_IP6_ADDRESS *dest, + uint8_t * mtu, + uint16_t mtu_len) +{ + struct sockaddr_in6 bvlc_dest = { 0 }; + uint16_t addr16[8]; + + /* assumes that the driver has already been initialized */ + if (BIP6_Socket < 0) { + return 0; + } + /* load destination IP address */ + bvlc_dest.sin6_family = AF_INET6; + bvlc6_address_get(dest, &addr16[0], &addr16[1], &addr16[2], &addr16[3], + &addr16[4], &addr16[5], &addr16[6], &addr16[7]); + bvlc_dest.sin6_addr.s6_addr16[0] = htons(addr16[0]); + bvlc_dest.sin6_addr.s6_addr16[1] = htons(addr16[1]); + bvlc_dest.sin6_addr.s6_addr16[2] = htons(addr16[2]); + bvlc_dest.sin6_addr.s6_addr16[3] = htons(addr16[3]); + bvlc_dest.sin6_addr.s6_addr16[4] = htons(addr16[4]); + bvlc_dest.sin6_addr.s6_addr16[5] = htons(addr16[5]); + bvlc_dest.sin6_addr.s6_addr16[6] = htons(addr16[6]); + bvlc_dest.sin6_addr.s6_addr16[7] = htons(addr16[7]); + bvlc_dest.sin6_port = htons(dest->port); + debug_print_ipv6("Sending MPDU->", &bvlc_dest.sin6_addr); + /* Send the packet */ + return sendto(BIP6_Socket, (char *) mtu, mtu_len, 0, + (struct sockaddr *) &bvlc_dest, sizeof(bvlc_dest)); +} + +/** + * BACnet/IP Datalink Receive handler. + * + * @param src - returns the source address + * @param npdu - returns the NPDU buffer + * @param max_npdu -maximum size of the NPDU buffer + * @param timeout - number of milliseconds to wait for a packet + * + * @return Number of bytes received, or 0 if none or timeout. + */ +uint16_t bip6_receive( + BACNET_ADDRESS * src, + uint8_t * npdu, + uint16_t max_npdu, + unsigned timeout) +{ + uint16_t npdu_len = 0; /* return value */ + fd_set read_fds; + int max = 0; + struct timeval select_timeout; + struct sockaddr_in6 sin = { 0 }; + BACNET_IP6_ADDRESS addr = {{ 0 }}; + socklen_t sin_len = sizeof(sin); + int received_bytes = 0; + int offset = 0; + uint16_t i = 0; + + /* Make sure the socket is open */ + if (BIP6_Socket < 0) { + return 0; + } + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } else { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + FD_ZERO(&read_fds); + FD_SET(BIP6_Socket, &read_fds); + max = BIP6_Socket; + /* see if there is a packet for us */ + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { + received_bytes = + recvfrom(BIP6_Socket, (char *) &npdu[0], max_npdu, 0, + (struct sockaddr *) &sin, &sin_len); + } else { + return 0; + } + /* See if there is a problem */ + if (received_bytes < 0) { + return 0; + } + /* no problem, just no bytes */ + if (received_bytes == 0) { + return 0; + } + /* the signature of a BACnet/IPv6 packet */ + if (npdu[0] != BVLL_TYPE_BACNET_IP6) { + return 0; + } + /* pass the packet into the BBMD handler */ + debug_print_ipv6("Received MPDU->", &sin.sin6_addr); + bvlc6_address_set(&addr, + ntohs(sin.sin6_addr.s6_addr16[0]), + ntohs(sin.sin6_addr.s6_addr16[1]), + ntohs(sin.sin6_addr.s6_addr16[2]), + ntohs(sin.sin6_addr.s6_addr16[3]), + ntohs(sin.sin6_addr.s6_addr16[4]), + ntohs(sin.sin6_addr.s6_addr16[5]), + ntohs(sin.sin6_addr.s6_addr16[6]), + ntohs(sin.sin6_addr.s6_addr16[7])); + addr.port = ntohs(sin.sin6_port); + offset = bvlc6_handler(&addr, src, npdu, received_bytes); + if (offset > 0) { + npdu_len = received_bytes - offset; + if (npdu_len <= max_npdu) { + /* shift the buffer to return a valid NPDU */ + for (i = 0; i < npdu_len; i++) { + npdu[i] = npdu[offset + i]; + } + } else { + npdu_len = 0; + } + } + + return npdu_len; +} + +/** Cleanup and close out the BACnet/IP services by closing the socket. + * @ingroup DLBIP6 + */ +void bip6_cleanup( + void) +{ + if (BIP6_Socket != -1) { + close(BIP6_Socket); + } + BIP6_Socket = -1; + + return; +} + +/** Initialize the BACnet/IP services at the given interface. + * @ingroup DLBIP6 + * -# Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IPv6 data structures. + * -# Opens a UDP socket + * -# Configures the socket for sending and receiving + * -# Configures the socket so it can send multicasts + * -# Binds the socket to the local IP address at the specified port for + * BACnet/IPv6 (by default, 0xBAC0 = 47808). + * + * @note For Linux, ifname is eth0, ath0, arc0, and others. + * + * @param ifname [in] The named interface to use for the network layer. + * If NULL, the "eth0" interface is assigned. + * @return True if the socket is successfully opened for BACnet/IP, + * else False if the socket functions fail. + */ +bool bip6_init( + char *ifname) +{ + int status = 0; /* return from socket lib calls */ + struct sockaddr_in6 server = {0}; + struct in6_addr broadcast_address; + struct ipv6_mreq join_request; + int sockopt = 0; + + if (ifname) { + bip6_set_interface(ifname); + } else { + bip6_set_interface("eth0"); + } + if (BIP6_Addr.port == 0) { + bip6_set_port(0xBAC0); + } + debug_printf("BIP6: IPv6 UDP port: 0x%04X\n", htons(BIP6_Addr.port)); + if (BIP6_Broadcast_Addr.address[0] == 0) { + bvlc6_address_set(&BIP6_Broadcast_Addr, + BIP6_MULTICAST_SITE_LOCAL, 0, 0, 0, 0, 0, 0, + BIP6_MULTICAST_GROUP_ID); + } + /* assumes that the driver has already been initialized */ + BIP6_Socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (BIP6_Socket < 0) + return false; + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + sockopt = 1; + status = + setsockopt(BIP6_Socket, SOL_SOCKET, SO_REUSEADDR, &sockopt, + sizeof(sockopt)); + if (status < 0) { + close(BIP6_Socket); + BIP6_Socket = -1; + return status; + } + /* allow us to send a broadcast */ + status = + setsockopt(BIP6_Socket, SOL_SOCKET, SO_BROADCAST, &sockopt, + sizeof(sockopt)); + if (status < 0) { + close(BIP6_Socket); + BIP6_Socket = -1; + return false; + } + /* subscribe to a multicast address */ + memcpy(&broadcast_address.s6_addr[0], &BIP6_Broadcast_Addr.address[0], IP6_ADDRESS_MAX); + memcpy(&join_request.ipv6mr_multiaddr, &broadcast_address, sizeof(struct in6_addr)); + /* Let system choose the interface */ + join_request.ipv6mr_interface = 0; + status = setsockopt(BIP6_Socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &join_request, sizeof(join_request)); + if (status < 0) { + perror("BIP: setsockopt(IPV6_JOIN_GROUP)"); + } + + /* bind the socket to the local port number and IP address */ + server.sin6_family = AF_INET6; + server.sin6_addr = in6addr_any; + server.sin6_port = htons(BIP6_Addr.port); + status = bind(BIP6_Socket, (const void*)&server, sizeof(server)); + if (status < 0) { + perror("BIP: bind"); + close(BIP6_Socket); + BIP6_Socket = -1; + return false; + } + bvlc6_init(); + + return true; +} diff --git a/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Development.sln b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Development.sln index 46584d79..05820780 100644 --- a/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Development.sln +++ b/ports/win32/Microsoft Visual Studio 2010/BACnet Stack Development.sln @@ -1,98 +1,98 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C++ Express 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Stack Library", "BACnet Stack Library\BACnet Stack Library.vcxproj", "{E9A65567-B028-4278-881D-674604B2E126}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Object Definitions", "BACnet Object Definitions\BACnet Object Definitions.vcxproj", "{6A8668E1-F08E-496B-B624-D6C05001806D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "Server\Server.vcxproj", "{EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}" - ProjectSection(ProjectDependencies) = postProject - {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacwi", "Who-Is\Who-Is.vcxproj", "{A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}" - ProjectSection(ProjectDependencies) = postProject - {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacdcc", "dcc\dcc.vcxproj", "{CC217FCF-AEF6-4859-8452-1E222C393383}" - ProjectSection(ProjectDependencies) = postProject - {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} - {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Handler Library", "BACnet Handler Library\BACnet Handler Library.vcxproj", "{9D4C3515-CA91-4C54-93DC-9962A3139A38}" - ProjectSection(ProjectDependencies) = postProject - {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} - {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacwp", "writeprop\writeprop.vcxproj", "{826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}" - ProjectSection(ProjectDependencies) = postProject - {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} - {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} - {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacwir", "bacwir\bacwir.vcxproj", "{7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacrp", "bacrp\bacrp.vcxproj", "{2DED9AEF-13AB-408B-B914-F7F30AE15EC0}" - ProjectSection(ProjectDependencies) = postProject - {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} - {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} - {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacepics", "bacepics\bacepics.vcxproj", "{17BEB9AD-5DA8-4B49-9369-B6715831C381}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E9A65567-B028-4278-881D-674604B2E126}.Debug|Win32.ActiveCfg = Debug|Win32 - {E9A65567-B028-4278-881D-674604B2E126}.Debug|Win32.Build.0 = Debug|Win32 - {E9A65567-B028-4278-881D-674604B2E126}.Release|Win32.ActiveCfg = Release|Win32 - {E9A65567-B028-4278-881D-674604B2E126}.Release|Win32.Build.0 = Release|Win32 - {6A8668E1-F08E-496B-B624-D6C05001806D}.Debug|Win32.ActiveCfg = Debug|Win32 - {6A8668E1-F08E-496B-B624-D6C05001806D}.Debug|Win32.Build.0 = Debug|Win32 - {6A8668E1-F08E-496B-B624-D6C05001806D}.Release|Win32.ActiveCfg = Release|Win32 - {6A8668E1-F08E-496B-B624-D6C05001806D}.Release|Win32.Build.0 = Release|Win32 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|Win32.ActiveCfg = Debug|Win32 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|Win32.Build.0 = Debug|Win32 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|Win32.ActiveCfg = Release|Win32 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|Win32.Build.0 = Release|Win32 - {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Debug|Win32.ActiveCfg = Debug|Win32 - {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Debug|Win32.Build.0 = Debug|Win32 - {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Release|Win32.ActiveCfg = Release|Win32 - {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Release|Win32.Build.0 = Release|Win32 - {CC217FCF-AEF6-4859-8452-1E222C393383}.Debug|Win32.ActiveCfg = Debug|Win32 - {CC217FCF-AEF6-4859-8452-1E222C393383}.Debug|Win32.Build.0 = Debug|Win32 - {CC217FCF-AEF6-4859-8452-1E222C393383}.Release|Win32.ActiveCfg = Release|Win32 - {CC217FCF-AEF6-4859-8452-1E222C393383}.Release|Win32.Build.0 = Release|Win32 - {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Debug|Win32.ActiveCfg = Debug|Win32 - {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Debug|Win32.Build.0 = Debug|Win32 - {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Release|Win32.ActiveCfg = Release|Win32 - {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Release|Win32.Build.0 = Release|Win32 - {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Debug|Win32.ActiveCfg = Debug|Win32 - {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Debug|Win32.Build.0 = Debug|Win32 - {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Release|Win32.ActiveCfg = Release|Win32 - {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Release|Win32.Build.0 = Release|Win32 - {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Debug|Win32.ActiveCfg = Debug|Win32 - {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Debug|Win32.Build.0 = Debug|Win32 - {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Release|Win32.ActiveCfg = Release|Win32 - {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Release|Win32.Build.0 = Release|Win32 - {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Debug|Win32.ActiveCfg = Debug|Win32 - {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Debug|Win32.Build.0 = Debug|Win32 - {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Release|Win32.ActiveCfg = Release|Win32 - {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Release|Win32.Build.0 = Release|Win32 - {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Debug|Win32.ActiveCfg = Debug|Win32 - {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Debug|Win32.Build.0 = Debug|Win32 - {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Release|Win32.ActiveCfg = Release|Win32 - {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Stack Library", "BACnet Stack Library\BACnet Stack Library.vcxproj", "{E9A65567-B028-4278-881D-674604B2E126}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Object Definitions", "BACnet Object Definitions\BACnet Object Definitions.vcxproj", "{6A8668E1-F08E-496B-B624-D6C05001806D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "Server\Server.vcxproj", "{EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}" + ProjectSection(ProjectDependencies) = postProject + {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacwi", "Who-Is\Who-Is.vcxproj", "{A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}" + ProjectSection(ProjectDependencies) = postProject + {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacdcc", "dcc\dcc.vcxproj", "{CC217FCF-AEF6-4859-8452-1E222C393383}" + ProjectSection(ProjectDependencies) = postProject + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet Handler Library", "BACnet Handler Library\BACnet Handler Library.vcxproj", "{9D4C3515-CA91-4C54-93DC-9962A3139A38}" + ProjectSection(ProjectDependencies) = postProject + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacwp", "writeprop\writeprop.vcxproj", "{826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}" + ProjectSection(ProjectDependencies) = postProject + {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacwir", "bacwir\bacwir.vcxproj", "{7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacrp", "bacrp\bacrp.vcxproj", "{2DED9AEF-13AB-408B-B914-F7F30AE15EC0}" + ProjectSection(ProjectDependencies) = postProject + {9D4C3515-CA91-4C54-93DC-9962A3139A38} = {9D4C3515-CA91-4C54-93DC-9962A3139A38} + {E9A65567-B028-4278-881D-674604B2E126} = {E9A65567-B028-4278-881D-674604B2E126} + {6A8668E1-F08E-496B-B624-D6C05001806D} = {6A8668E1-F08E-496B-B624-D6C05001806D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bacepics", "bacepics\bacepics.vcxproj", "{17BEB9AD-5DA8-4B49-9369-B6715831C381}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E9A65567-B028-4278-881D-674604B2E126}.Debug|Win32.ActiveCfg = Debug|Win32 + {E9A65567-B028-4278-881D-674604B2E126}.Debug|Win32.Build.0 = Debug|Win32 + {E9A65567-B028-4278-881D-674604B2E126}.Release|Win32.ActiveCfg = Release|Win32 + {E9A65567-B028-4278-881D-674604B2E126}.Release|Win32.Build.0 = Release|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Debug|Win32.ActiveCfg = Debug|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Debug|Win32.Build.0 = Debug|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Release|Win32.ActiveCfg = Release|Win32 + {6A8668E1-F08E-496B-B624-D6C05001806D}.Release|Win32.Build.0 = Release|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|Win32.ActiveCfg = Debug|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|Win32.Build.0 = Debug|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|Win32.ActiveCfg = Release|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|Win32.Build.0 = Release|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Debug|Win32.ActiveCfg = Debug|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Debug|Win32.Build.0 = Debug|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Release|Win32.ActiveCfg = Release|Win32 + {A6287B6E-0BCE-43FA-ADFB-9263DB069BA4}.Release|Win32.Build.0 = Release|Win32 + {CC217FCF-AEF6-4859-8452-1E222C393383}.Debug|Win32.ActiveCfg = Debug|Win32 + {CC217FCF-AEF6-4859-8452-1E222C393383}.Debug|Win32.Build.0 = Debug|Win32 + {CC217FCF-AEF6-4859-8452-1E222C393383}.Release|Win32.ActiveCfg = Release|Win32 + {CC217FCF-AEF6-4859-8452-1E222C393383}.Release|Win32.Build.0 = Release|Win32 + {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Debug|Win32.ActiveCfg = Debug|Win32 + {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Debug|Win32.Build.0 = Debug|Win32 + {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Release|Win32.ActiveCfg = Release|Win32 + {9D4C3515-CA91-4C54-93DC-9962A3139A38}.Release|Win32.Build.0 = Release|Win32 + {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Debug|Win32.ActiveCfg = Debug|Win32 + {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Debug|Win32.Build.0 = Debug|Win32 + {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Release|Win32.ActiveCfg = Release|Win32 + {826EF765-7828-44D3-B8AF-7AEDBAA8E0D2}.Release|Win32.Build.0 = Release|Win32 + {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Debug|Win32.ActiveCfg = Debug|Win32 + {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Debug|Win32.Build.0 = Debug|Win32 + {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Release|Win32.ActiveCfg = Release|Win32 + {7E30D4FE-B8A7-442D-B6FB-E8DC3129EFEB}.Release|Win32.Build.0 = Release|Win32 + {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Debug|Win32.ActiveCfg = Debug|Win32 + {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Debug|Win32.Build.0 = Debug|Win32 + {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Release|Win32.ActiveCfg = Release|Win32 + {2DED9AEF-13AB-408B-B914-F7F30AE15EC0}.Release|Win32.Build.0 = Release|Win32 + {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Debug|Win32.ActiveCfg = Debug|Win32 + {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Debug|Win32.Build.0 = Debug|Win32 + {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Release|Win32.ActiveCfg = Release|Win32 + {17BEB9AD-5DA8-4B49-9369-B6715831C381}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ports/win32/Microsoft Visual Studio 2015/BACnet Stack Development.sln b/ports/win32/Microsoft Visual Studio 2015/BACnet Stack Development.sln index f32d5bf4..5b77958d 100644 --- a/ports/win32/Microsoft Visual Studio 2015/BACnet Stack Development.sln +++ b/ports/win32/Microsoft Visual Studio 2015/BACnet Stack Development.sln @@ -1,57 +1,57 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet_Handler_Library", "BACnet_Handler_Library\BACnet_Handler_Library.vcxproj", "{2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet_Object_Definitions", "BACnet_Object_Definitions\BACnet_Object_Definitions.vcxproj", "{6D42B11A-84DA-46DB-9D08-319329D51473}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet_Stack_Library", "BACnet_Stack_Library\BACnet_Stack_Library.vcxproj", "{D0875CC6-8B68-404C-ABD7-823FE0C084DD}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "server", "Server\Server.vcxproj", "{EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x64.ActiveCfg = Debug|x64 - {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x64.Build.0 = Debug|x64 - {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x86.ActiveCfg = Debug|Win32 - {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x86.Build.0 = Debug|Win32 - {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x64.ActiveCfg = Release|x64 - {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x64.Build.0 = Release|x64 - {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x86.ActiveCfg = Release|Win32 - {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x86.Build.0 = Release|Win32 - {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x64.ActiveCfg = Debug|x64 - {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x64.Build.0 = Debug|x64 - {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x86.ActiveCfg = Debug|Win32 - {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x86.Build.0 = Debug|Win32 - {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x64.ActiveCfg = Release|x64 - {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x64.Build.0 = Release|x64 - {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x86.ActiveCfg = Release|Win32 - {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x86.Build.0 = Release|Win32 - {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x64.ActiveCfg = Debug|x64 - {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x64.Build.0 = Debug|x64 - {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x86.ActiveCfg = Debug|Win32 - {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x86.Build.0 = Debug|Win32 - {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x64.ActiveCfg = Release|x64 - {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x64.Build.0 = Release|x64 - {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x86.ActiveCfg = Release|Win32 - {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x86.Build.0 = Release|Win32 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|x64.ActiveCfg = Debug|x64 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|x64.Build.0 = Debug|x64 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|x86.ActiveCfg = Debug|Win32 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|x86.Build.0 = Debug|Win32 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|x64.ActiveCfg = Release|Win32 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|x86.ActiveCfg = Release|Win32 - {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet_Handler_Library", "BACnet_Handler_Library\BACnet_Handler_Library.vcxproj", "{2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet_Object_Definitions", "BACnet_Object_Definitions\BACnet_Object_Definitions.vcxproj", "{6D42B11A-84DA-46DB-9D08-319329D51473}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BACnet_Stack_Library", "BACnet_Stack_Library\BACnet_Stack_Library.vcxproj", "{D0875CC6-8B68-404C-ABD7-823FE0C084DD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "server", "Server\Server.vcxproj", "{EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x64.ActiveCfg = Debug|x64 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x64.Build.0 = Debug|x64 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x86.ActiveCfg = Debug|Win32 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Debug|x86.Build.0 = Debug|Win32 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x64.ActiveCfg = Release|x64 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x64.Build.0 = Release|x64 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x86.ActiveCfg = Release|Win32 + {2001A15D-2D0E-4FFA-8B90-5E7938AE6ECF}.Release|x86.Build.0 = Release|Win32 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x64.ActiveCfg = Debug|x64 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x64.Build.0 = Debug|x64 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x86.ActiveCfg = Debug|Win32 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Debug|x86.Build.0 = Debug|Win32 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x64.ActiveCfg = Release|x64 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x64.Build.0 = Release|x64 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x86.ActiveCfg = Release|Win32 + {6D42B11A-84DA-46DB-9D08-319329D51473}.Release|x86.Build.0 = Release|Win32 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x64.ActiveCfg = Debug|x64 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x64.Build.0 = Debug|x64 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x86.ActiveCfg = Debug|Win32 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Debug|x86.Build.0 = Debug|Win32 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x64.ActiveCfg = Release|x64 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x64.Build.0 = Release|x64 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x86.ActiveCfg = Release|Win32 + {D0875CC6-8B68-404C-ABD7-823FE0C084DD}.Release|x86.Build.0 = Release|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|x64.ActiveCfg = Debug|x64 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|x64.Build.0 = Debug|x64 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|x86.ActiveCfg = Debug|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Debug|x86.Build.0 = Debug|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|x64.ActiveCfg = Release|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|x86.ActiveCfg = Release|Win32 + {EF250061-B9E3-4EEF-8C87-4AB52AF30B3E}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ports/win32/bip6.c b/ports/win32/bip6.c index 89c63a2d..682ea200 100644 --- a/ports/win32/bip6.c +++ b/ports/win32/bip6.c @@ -1,528 +1,528 @@ -/*####COPYRIGHTBEGIN#### - ------------------------------------------- - Copyright (C) 2016 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 -#include -#include /* for standard integer types uint8_t etc. */ -#include /* for the standard bool type. */ -#include "bacdcode.h" -#include "config.h" -#include "debug.h" -#include "device.h" -#include "bip6.h" -#include "net.h" - -/* Win32 Socket */ -static SOCKET BIP6_Socket = INVALID_SOCKET; -/* local address - filled by init functions */ -static BACNET_IP6_ADDRESS BIP6_Addr; -static BACNET_IP6_ADDRESS BIP6_Broadcast_Addr; - -static void debug_print_ipv6(const char *str, const struct in6_addr * addr) { - debug_printf( "BIP6: %s %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", - str, - (int)addr->s6_addr[0], (int)addr->s6_addr[1], - (int)addr->s6_addr[2], (int)addr->s6_addr[3], - (int)addr->s6_addr[4], (int)addr->s6_addr[5], - (int)addr->s6_addr[6], (int)addr->s6_addr[7], - (int)addr->s6_addr[8], (int)addr->s6_addr[9], - (int)addr->s6_addr[10], (int)addr->s6_addr[11], - (int)addr->s6_addr[12], (int)addr->s6_addr[13], - (int)addr->s6_addr[14], (int)addr->s6_addr[15]); -} - -static LPSTR PrintError(int ErrorCode) -{ - static char Message[1024]; - - // If this program was multithreaded, we'd want to use - // FORMAT_MESSAGE_ALLOCATE_BUFFER instead of a static buffer here. - // (And of course, free the buffer when we were done with it) - - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, ErrorCode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR) Message, 1024, NULL); - return Message; -} - -/* on Windows, ifname is the IPv6 address of the interface */ -void bip6_set_interface( - char *ifname) -{ - int i, RetVal; - struct addrinfo Hints, *AddrInfo, *AI; - struct sockaddr_in6* sin; - struct sockaddr_in6 server; - struct in6_addr broadcast_address; - struct ipv6_mreq join_request; - SOCKET ServSock[FD_SETSIZE]; - char port[6] = ""; - int sockopt = 0; - - // By setting the AI_PASSIVE flag in the hints to getaddrinfo, we're - // indicating that we intend to use the resulting address(es) to bind - // to a socket(s) for accepting incoming connections. This means that - // when the Address parameter is NULL, getaddrinfo will return one - // entry per allowed protocol family containing the unspecified address - // for that family. - // - memset(&Hints, 0, sizeof (Hints)); - Hints.ai_family = PF_INET6; - Hints.ai_socktype = SOCK_DGRAM; - Hints.ai_protocol = IPPROTO_UDP; - Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; - snprintf(port, sizeof(port), "%u", BIP6_Addr.port); - debug_printf("BIP6: getaddrinfo - IPv6 address %s port %s\n", ifname, port); - RetVal = getaddrinfo(ifname, &port[0], &Hints, &AddrInfo); - if (RetVal != 0) { - fprintf(stderr, "BIP6: getaddrinfo failed with error %d: %s\n", - RetVal, gai_strerror(RetVal)); - WSACleanup(); - return; - } - // - // Find the first matching address getaddrinfo returned so that - // we can create a new socket and bind that address to it, - // and create a queue to listen on. - // - for (i = 0, AI = AddrInfo; AI != NULL; AI = AI->ai_next) { - // Highly unlikely, but check anyway. - if (i == FD_SETSIZE) { - fprintf(stderr, - "BIP6: getaddrinfo returned more addresses than we could use.\n"); - break; - } - // only support PF_INET6. - if (AI->ai_family != PF_INET6) { - continue; - } - // only support SOCK_DGRAM. - if (AI->ai_socktype != SOCK_DGRAM) { - continue; - } - // only support IPPROTO_UDP. - if (AI->ai_protocol != IPPROTO_UDP) { - continue; - } - BIP6_Socket = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (BIP6_Socket == INVALID_SOCKET) { - fprintf(stderr, "BIP6: socket() failed with error %d: %s\n", - WSAGetLastError(), PrintError(WSAGetLastError())); - continue; - } - if ((AI->ai_family == PF_INET6) && - IN6_IS_ADDR_LINKLOCAL(AI->ai_addr) && - (((SOCKADDR_IN6 *) (AI->ai_addr))->sin6_scope_id == 0)) { - fprintf(stderr, - "BIP6: IPv6 link local addresses needs a scope ID!\n"); - } - /* Allow us to use the same socket for sending and receiving */ - /* This makes sure that the src port is correct when sending */ - sockopt = 1; - RetVal = - setsockopt(BIP6_Socket, SOL_SOCKET, SO_REUSEADDR, - (char *)&sockopt, sizeof(sockopt)); - if (RetVal < 0) { - closesocket(BIP6_Socket); - BIP6_Socket = INVALID_SOCKET; - continue; - } - /* allow us to send a broadcast */ - RetVal = - setsockopt(BIP6_Socket, SOL_SOCKET, SO_BROADCAST, - (char *)&sockopt, sizeof(sockopt)); - if (RetVal < 0) { - closesocket(BIP6_Socket); - BIP6_Socket = INVALID_SOCKET; - fprintf(stderr, "BIP6: setsockopt(SO_BROADCAST) failed " - "with error %d: %s\n", - WSAGetLastError(), PrintError(WSAGetLastError())); - continue; - } - /* subscribe to a multicast address */ - memcpy(&broadcast_address.s6_addr[0], &BIP6_Broadcast_Addr.address[0], - IP6_ADDRESS_MAX); - memcpy(&join_request.ipv6mr_multiaddr, &broadcast_address, - sizeof(struct in6_addr)); - /* Let system choose the interface */ - join_request.ipv6mr_interface = 0; - RetVal = setsockopt(BIP6_Socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, - (char *)&join_request, sizeof(join_request)); - if (RetVal < 0) { - fprintf(stderr, "BIP6: setsockopt(IPV6_JOIN_GROUP) failed " - "with error %d: %s\n", - WSAGetLastError(), PrintError(WSAGetLastError())); - } - // - // bind() associates a local address and port combination - // with the socket just created. This is most useful when - // the application is a server that has a well-known port - // that clients know about in advance. - // - memset(&server, 0, sizeof(server)); - server.sin6_family = PF_INET6; - server.sin6_port = htons(BIP6_Addr.port); - server.sin6_addr = in6addr_any; - if (bind(BIP6_Socket, (struct sockaddr *) &server, sizeof(server)) == - SOCKET_ERROR) { - fprintf(stderr, "BIP6: bind() failed with error %d: %s\n", - WSAGetLastError(), PrintError(WSAGetLastError())); - closesocket(ServSock[i]); - continue; - } else { - sin = (struct sockaddr_in6*)AI->ai_addr; - bvlc6_address_set(&BIP6_Addr, - ntohs(sin->sin6_addr.s6_addr16[0]), - ntohs(sin->sin6_addr.s6_addr16[1]), - ntohs(sin->sin6_addr.s6_addr16[2]), - ntohs(sin->sin6_addr.s6_addr16[3]), - ntohs(sin->sin6_addr.s6_addr16[4]), - ntohs(sin->sin6_addr.s6_addr16[5]), - ntohs(sin->sin6_addr.s6_addr16[6]), - ntohs(sin->sin6_addr.s6_addr16[7])); - /* https://msdn.microsoft.com/en-us/library/windows/desktop/ms740496(v=vs.85).aspx */ - } - i++; - if (BIP6_Socket != INVALID_SOCKET) { - break; - } - } - freeaddrinfo(AddrInfo); -} - -/** - * Set the BACnet IPv6 UDP port number - * - * @param port - IPv6 UDP port number - */ -void bip6_set_port( - uint16_t port) -{ - BIP6_Addr.port = port; - BIP6_Broadcast_Addr.port = port; -} - -/** - * Get the BACnet IPv6 UDP port number - * - * @return IPv6 UDP port number - */ -uint16_t bip6_get_port( - void) -{ - return BIP6_Addr.port; -} - -/** - * Get the BACnet broadcast address for my interface. - * Used as dest address in messages sent as BROADCAST - * - * @param addr - IPv6 source address - */ -void bip6_get_broadcast_address( - BACNET_ADDRESS * addr) -{ - if (addr) { - addr->net = BACNET_BROADCAST_NETWORK; - addr->mac_len = 0; - addr->len = 0; - } -} - -/** - * Get the IPv6 address for my interface. Used as src address in messages sent. - * - * @param addr - IPv6 source address - */ -void bip6_get_my_address( - BACNET_ADDRESS * addr) -{ - uint32_t device_id = 0; - - if (addr) { - device_id = Device_Object_Instance_Number(); - bvlc6_vmac_address_set(addr, device_id); - } -} - -/** - * Set the BACnet/IP address - * - * @param addr - network IPv6 address - */ -bool bip6_set_addr( - BACNET_IP6_ADDRESS *addr) -{ - return bvlc6_address_copy(&BIP6_Addr, addr); -} - -/** - * Get the BACnet/IP address - * - * @return BACnet/IP address - */ -bool bip6_get_addr( - BACNET_IP6_ADDRESS *addr) -{ - return bvlc6_address_copy(addr, &BIP6_Addr); -} - - -/** - * Determine if the BACnet/IPv6 address matches our own address - * - * @param addr - network IPv6 address - * - * @return true if the BACnet/IPv6 address matches our own address - */ -bool bip6_address_match_self( - BACNET_IP6_ADDRESS *addr) -{ - return !bvlc6_address_different(addr, &BIP6_Addr); -} - -/** - * Set the BACnet/IP address - * - * @param addr - network IPv6 address - */ -bool bip6_set_broadcast_addr( - BACNET_IP6_ADDRESS *addr) -{ - return bvlc6_address_copy(&BIP6_Broadcast_Addr, addr); -} - -/** - * Get the BACnet/IP address - * - * @return BACnet/IP address - */ -bool bip6_get_broadcast_addr( - BACNET_IP6_ADDRESS *addr) -{ - return bvlc6_address_copy(addr, &BIP6_Broadcast_Addr); -} - -/** - * The send function for BACnet/IPv6 driver layer - * - * @param dest - Points to a BACNET_IP6_ADDRESS structure containing the - * destination address. - * @param mtu - the bytes of data to send - * @param mtu_len - the number of bytes of data to send - * - * @return Upon successful completion, returns the number of bytes sent. - * Otherwise, -1 shall be returned and errno set to indicate the error. - */ -int bip6_send_mpdu( - BACNET_IP6_ADDRESS *dest, - uint8_t * mtu, - uint16_t mtu_len) -{ - struct sockaddr_in6 bvlc_dest = { 0 }; - uint16_t addr16[8]; - - /* assumes that the driver has already been initialized */ - if (BIP6_Socket < 0) { - return 0; - } - /* load destination IP address */ - bvlc_dest.sin6_family = AF_INET6; - bvlc6_address_get(dest, &addr16[0], &addr16[1], &addr16[2], &addr16[3], - &addr16[4], &addr16[5], &addr16[6], &addr16[7]); - bvlc_dest.sin6_addr.s6_addr16[0] = htons(addr16[0]); - bvlc_dest.sin6_addr.s6_addr16[1] = htons(addr16[1]); - bvlc_dest.sin6_addr.s6_addr16[2] = htons(addr16[2]); - bvlc_dest.sin6_addr.s6_addr16[3] = htons(addr16[3]); - bvlc_dest.sin6_addr.s6_addr16[4] = htons(addr16[4]); - bvlc_dest.sin6_addr.s6_addr16[5] = htons(addr16[5]); - bvlc_dest.sin6_addr.s6_addr16[6] = htons(addr16[6]); - bvlc_dest.sin6_addr.s6_addr16[7] = htons(addr16[7]); - bvlc_dest.sin6_port = htons(dest->port); - debug_print_ipv6("BIP6: Sending MPDU->", &bvlc_dest.sin6_addr); - /* Send the packet */ - return sendto(BIP6_Socket, (char *) mtu, mtu_len, 0, - (struct sockaddr *) &bvlc_dest, sizeof(bvlc_dest)); -} - -/** - * BACnet/IP Datalink Receive handler. - * - * @param src - returns the source address - * @param npdu - returns the NPDU buffer - * @param max_npdu -maximum size of the NPDU buffer - * @param timeout - number of milliseconds to wait for a packet - * - * @return Number of bytes received, or 0 if none or timeout. - */ -uint16_t bip6_receive( - BACNET_ADDRESS * src, - uint8_t * npdu, - uint16_t max_npdu, - unsigned timeout) -{ - uint16_t npdu_len = 0; /* return value */ - fd_set read_fds; - int max = 0; - struct timeval select_timeout; - struct sockaddr_in6 sin = { 0 }; - BACNET_IP6_ADDRESS addr = {{ 0 }}; - socklen_t sin_len = sizeof(sin); - int received_bytes = 0; - int offset = 0; - uint16_t i = 0; - - /* Make sure the socket is open */ - if (BIP6_Socket < 0) { - return 0; - } - /* we could just use a non-blocking socket, but that consumes all - the CPU time. We can use a timeout; it is only supported as - a select. */ - if (timeout >= 1000) { - select_timeout.tv_sec = timeout / 1000; - select_timeout.tv_usec = - 1000 * (timeout - select_timeout.tv_sec * 1000); - } else { - select_timeout.tv_sec = 0; - select_timeout.tv_usec = 1000 * timeout; - } - FD_ZERO(&read_fds); - FD_SET(BIP6_Socket, &read_fds); - max = BIP6_Socket; - /* see if there is a packet for us */ - if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { - received_bytes = - recvfrom(BIP6_Socket, (char *) &npdu[0], max_npdu, 0, - (struct sockaddr *) &sin, &sin_len); - } else { - return 0; - } - /* See if there is a problem */ - if (received_bytes < 0) { - return 0; - } - /* no problem, just no bytes */ - if (received_bytes == 0) { - return 0; - } - /* the signature of a BACnet/IPv6 packet */ - if (npdu[0] != BVLL_TYPE_BACNET_IP6) { - return 0; - } - /* pass the packet into the BBMD handler */ - memcpy(&addr.address[0], &(sin.sin6_addr), 16); - memcpy(&addr.port, &sin.sin6_port, 2); - offset = bvlc6_handler(&addr, src, npdu, received_bytes); - if (offset > 0) { - npdu_len = received_bytes - offset; - if (npdu_len <= max_npdu) { - /* shift the buffer to return a valid NPDU */ - for (i = 0; i < npdu_len; i++) { - npdu[i] = npdu[offset + i]; - } - } else { - npdu_len = 0; - } - } - - return npdu_len; -} - -/** Cleanup and close out the BACnet/IP services by closing the socket. - * @ingroup DLBIP6 - */ -void bip6_cleanup( - void) -{ - if (BIP6_Socket != -1) { - close(BIP6_Socket); - } - BIP6_Socket = -1; - WSACleanup(); - - return; -} - -/** Initialize the BACnet/IP services at the given interface. - * @ingroup DLBIP6 - * -# Gets the local IP address and local broadcast address from the system, - * and saves it into the BACnet/IPv6 data structures. - * -# Opens a UDP socket - * -# Configures the socket for sending and receiving - * -# Configures the socket so it can send multicasts - * -# Binds the socket to the local IP address at the specified port for - * BACnet/IPv6 (by default, 0xBAC0 = 47808). - * - * @note For Windows, ifname is the IPv6 address of the interface. - * - * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738639(v=vs.85).aspx - * - * @param ifname [in] The named interface to use for the network layer. - * - * @return True if the socket is successfully opened for BACnet/IPv6, - * else False if the socket functions fail. - */ -bool bip6_init( - char *ifname) -{ - WSADATA wd; - int RetVal; - - // Ask for Winsock version 2.2. - if ((RetVal = WSAStartup(MAKEWORD(2, 2), &wd)) != 0) { - fprintf(stderr, "BIP6: WSAStartup failed with error %d: %s\n", - RetVal, PrintError(RetVal)); - WSACleanup(); - exit(1); - } - if (BIP6_Addr.port == 0) { - bip6_set_port(0xBAC0); - } - debug_printf("BIP6: IPv6 UDP port: 0x%04X\n", BIP6_Addr.port); - bip6_set_interface(ifname); - if (BIP6_Broadcast_Addr.address[0] == 0) { - bvlc6_address_set(&BIP6_Broadcast_Addr, - BIP6_MULTICAST_LINK_LOCAL, 0, 0, 0, 0, 0, 0, - BIP6_MULTICAST_GROUP_ID); - } - if (BIP6_Socket == INVALID_SOCKET) { - fprintf(stderr, "BIP6: Fatal error: unable to serve on any address.\n"); - WSACleanup(); - exit(1); - } - - return true; -} +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2016 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 +#include +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "config.h" +#include "debug.h" +#include "device.h" +#include "bip6.h" +#include "net.h" + +/* Win32 Socket */ +static SOCKET BIP6_Socket = INVALID_SOCKET; +/* local address - filled by init functions */ +static BACNET_IP6_ADDRESS BIP6_Addr; +static BACNET_IP6_ADDRESS BIP6_Broadcast_Addr; + +static void debug_print_ipv6(const char *str, const struct in6_addr * addr) { + debug_printf( "BIP6: %s %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", + str, + (int)addr->s6_addr[0], (int)addr->s6_addr[1], + (int)addr->s6_addr[2], (int)addr->s6_addr[3], + (int)addr->s6_addr[4], (int)addr->s6_addr[5], + (int)addr->s6_addr[6], (int)addr->s6_addr[7], + (int)addr->s6_addr[8], (int)addr->s6_addr[9], + (int)addr->s6_addr[10], (int)addr->s6_addr[11], + (int)addr->s6_addr[12], (int)addr->s6_addr[13], + (int)addr->s6_addr[14], (int)addr->s6_addr[15]); +} + +static LPSTR PrintError(int ErrorCode) +{ + static char Message[1024]; + + // If this program was multithreaded, we'd want to use + // FORMAT_MESSAGE_ALLOCATE_BUFFER instead of a static buffer here. + // (And of course, free the buffer when we were done with it) + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, ErrorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) Message, 1024, NULL); + return Message; +} + +/* on Windows, ifname is the IPv6 address of the interface */ +void bip6_set_interface( + char *ifname) +{ + int i, RetVal; + struct addrinfo Hints, *AddrInfo, *AI; + struct sockaddr_in6* sin; + struct sockaddr_in6 server; + struct in6_addr broadcast_address; + struct ipv6_mreq join_request; + SOCKET ServSock[FD_SETSIZE]; + char port[6] = ""; + int sockopt = 0; + + // By setting the AI_PASSIVE flag in the hints to getaddrinfo, we're + // indicating that we intend to use the resulting address(es) to bind + // to a socket(s) for accepting incoming connections. This means that + // when the Address parameter is NULL, getaddrinfo will return one + // entry per allowed protocol family containing the unspecified address + // for that family. + // + memset(&Hints, 0, sizeof (Hints)); + Hints.ai_family = PF_INET6; + Hints.ai_socktype = SOCK_DGRAM; + Hints.ai_protocol = IPPROTO_UDP; + Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + snprintf(port, sizeof(port), "%u", BIP6_Addr.port); + debug_printf("BIP6: getaddrinfo - IPv6 address %s port %s\n", ifname, port); + RetVal = getaddrinfo(ifname, &port[0], &Hints, &AddrInfo); + if (RetVal != 0) { + fprintf(stderr, "BIP6: getaddrinfo failed with error %d: %s\n", + RetVal, gai_strerror(RetVal)); + WSACleanup(); + return; + } + // + // Find the first matching address getaddrinfo returned so that + // we can create a new socket and bind that address to it, + // and create a queue to listen on. + // + for (i = 0, AI = AddrInfo; AI != NULL; AI = AI->ai_next) { + // Highly unlikely, but check anyway. + if (i == FD_SETSIZE) { + fprintf(stderr, + "BIP6: getaddrinfo returned more addresses than we could use.\n"); + break; + } + // only support PF_INET6. + if (AI->ai_family != PF_INET6) { + continue; + } + // only support SOCK_DGRAM. + if (AI->ai_socktype != SOCK_DGRAM) { + continue; + } + // only support IPPROTO_UDP. + if (AI->ai_protocol != IPPROTO_UDP) { + continue; + } + BIP6_Socket = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (BIP6_Socket == INVALID_SOCKET) { + fprintf(stderr, "BIP6: socket() failed with error %d: %s\n", + WSAGetLastError(), PrintError(WSAGetLastError())); + continue; + } + if ((AI->ai_family == PF_INET6) && + IN6_IS_ADDR_LINKLOCAL(AI->ai_addr) && + (((SOCKADDR_IN6 *) (AI->ai_addr))->sin6_scope_id == 0)) { + fprintf(stderr, + "BIP6: IPv6 link local addresses needs a scope ID!\n"); + } + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + sockopt = 1; + RetVal = + setsockopt(BIP6_Socket, SOL_SOCKET, SO_REUSEADDR, + (char *)&sockopt, sizeof(sockopt)); + if (RetVal < 0) { + closesocket(BIP6_Socket); + BIP6_Socket = INVALID_SOCKET; + continue; + } + /* allow us to send a broadcast */ + RetVal = + setsockopt(BIP6_Socket, SOL_SOCKET, SO_BROADCAST, + (char *)&sockopt, sizeof(sockopt)); + if (RetVal < 0) { + closesocket(BIP6_Socket); + BIP6_Socket = INVALID_SOCKET; + fprintf(stderr, "BIP6: setsockopt(SO_BROADCAST) failed " + "with error %d: %s\n", + WSAGetLastError(), PrintError(WSAGetLastError())); + continue; + } + /* subscribe to a multicast address */ + memcpy(&broadcast_address.s6_addr[0], &BIP6_Broadcast_Addr.address[0], + IP6_ADDRESS_MAX); + memcpy(&join_request.ipv6mr_multiaddr, &broadcast_address, + sizeof(struct in6_addr)); + /* Let system choose the interface */ + join_request.ipv6mr_interface = 0; + RetVal = setsockopt(BIP6_Socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, + (char *)&join_request, sizeof(join_request)); + if (RetVal < 0) { + fprintf(stderr, "BIP6: setsockopt(IPV6_JOIN_GROUP) failed " + "with error %d: %s\n", + WSAGetLastError(), PrintError(WSAGetLastError())); + } + // + // bind() associates a local address and port combination + // with the socket just created. This is most useful when + // the application is a server that has a well-known port + // that clients know about in advance. + // + memset(&server, 0, sizeof(server)); + server.sin6_family = PF_INET6; + server.sin6_port = htons(BIP6_Addr.port); + server.sin6_addr = in6addr_any; + if (bind(BIP6_Socket, (struct sockaddr *) &server, sizeof(server)) == + SOCKET_ERROR) { + fprintf(stderr, "BIP6: bind() failed with error %d: %s\n", + WSAGetLastError(), PrintError(WSAGetLastError())); + closesocket(ServSock[i]); + continue; + } else { + sin = (struct sockaddr_in6*)AI->ai_addr; + bvlc6_address_set(&BIP6_Addr, + ntohs(sin->sin6_addr.s6_addr16[0]), + ntohs(sin->sin6_addr.s6_addr16[1]), + ntohs(sin->sin6_addr.s6_addr16[2]), + ntohs(sin->sin6_addr.s6_addr16[3]), + ntohs(sin->sin6_addr.s6_addr16[4]), + ntohs(sin->sin6_addr.s6_addr16[5]), + ntohs(sin->sin6_addr.s6_addr16[6]), + ntohs(sin->sin6_addr.s6_addr16[7])); + /* https://msdn.microsoft.com/en-us/library/windows/desktop/ms740496(v=vs.85).aspx */ + } + i++; + if (BIP6_Socket != INVALID_SOCKET) { + break; + } + } + freeaddrinfo(AddrInfo); +} + +/** + * Set the BACnet IPv6 UDP port number + * + * @param port - IPv6 UDP port number + */ +void bip6_set_port( + uint16_t port) +{ + BIP6_Addr.port = port; + BIP6_Broadcast_Addr.port = port; +} + +/** + * Get the BACnet IPv6 UDP port number + * + * @return IPv6 UDP port number + */ +uint16_t bip6_get_port( + void) +{ + return BIP6_Addr.port; +} + +/** + * Get the BACnet broadcast address for my interface. + * Used as dest address in messages sent as BROADCAST + * + * @param addr - IPv6 source address + */ +void bip6_get_broadcast_address( + BACNET_ADDRESS * addr) +{ + if (addr) { + addr->net = BACNET_BROADCAST_NETWORK; + addr->mac_len = 0; + addr->len = 0; + } +} + +/** + * Get the IPv6 address for my interface. Used as src address in messages sent. + * + * @param addr - IPv6 source address + */ +void bip6_get_my_address( + BACNET_ADDRESS * addr) +{ + uint32_t device_id = 0; + + if (addr) { + device_id = Device_Object_Instance_Number(); + bvlc6_vmac_address_set(addr, device_id); + } +} + +/** + * Set the BACnet/IP address + * + * @param addr - network IPv6 address + */ +bool bip6_set_addr( + BACNET_IP6_ADDRESS *addr) +{ + return bvlc6_address_copy(&BIP6_Addr, addr); +} + +/** + * Get the BACnet/IP address + * + * @return BACnet/IP address + */ +bool bip6_get_addr( + BACNET_IP6_ADDRESS *addr) +{ + return bvlc6_address_copy(addr, &BIP6_Addr); +} + + +/** + * Determine if the BACnet/IPv6 address matches our own address + * + * @param addr - network IPv6 address + * + * @return true if the BACnet/IPv6 address matches our own address + */ +bool bip6_address_match_self( + BACNET_IP6_ADDRESS *addr) +{ + return !bvlc6_address_different(addr, &BIP6_Addr); +} + +/** + * Set the BACnet/IP address + * + * @param addr - network IPv6 address + */ +bool bip6_set_broadcast_addr( + BACNET_IP6_ADDRESS *addr) +{ + return bvlc6_address_copy(&BIP6_Broadcast_Addr, addr); +} + +/** + * Get the BACnet/IP address + * + * @return BACnet/IP address + */ +bool bip6_get_broadcast_addr( + BACNET_IP6_ADDRESS *addr) +{ + return bvlc6_address_copy(addr, &BIP6_Broadcast_Addr); +} + +/** + * The send function for BACnet/IPv6 driver layer + * + * @param dest - Points to a BACNET_IP6_ADDRESS structure containing the + * destination address. + * @param mtu - the bytes of data to send + * @param mtu_len - the number of bytes of data to send + * + * @return Upon successful completion, returns the number of bytes sent. + * Otherwise, -1 shall be returned and errno set to indicate the error. + */ +int bip6_send_mpdu( + BACNET_IP6_ADDRESS *dest, + uint8_t * mtu, + uint16_t mtu_len) +{ + struct sockaddr_in6 bvlc_dest = { 0 }; + uint16_t addr16[8]; + + /* assumes that the driver has already been initialized */ + if (BIP6_Socket < 0) { + return 0; + } + /* load destination IP address */ + bvlc_dest.sin6_family = AF_INET6; + bvlc6_address_get(dest, &addr16[0], &addr16[1], &addr16[2], &addr16[3], + &addr16[4], &addr16[5], &addr16[6], &addr16[7]); + bvlc_dest.sin6_addr.s6_addr16[0] = htons(addr16[0]); + bvlc_dest.sin6_addr.s6_addr16[1] = htons(addr16[1]); + bvlc_dest.sin6_addr.s6_addr16[2] = htons(addr16[2]); + bvlc_dest.sin6_addr.s6_addr16[3] = htons(addr16[3]); + bvlc_dest.sin6_addr.s6_addr16[4] = htons(addr16[4]); + bvlc_dest.sin6_addr.s6_addr16[5] = htons(addr16[5]); + bvlc_dest.sin6_addr.s6_addr16[6] = htons(addr16[6]); + bvlc_dest.sin6_addr.s6_addr16[7] = htons(addr16[7]); + bvlc_dest.sin6_port = htons(dest->port); + debug_print_ipv6("BIP6: Sending MPDU->", &bvlc_dest.sin6_addr); + /* Send the packet */ + return sendto(BIP6_Socket, (char *) mtu, mtu_len, 0, + (struct sockaddr *) &bvlc_dest, sizeof(bvlc_dest)); +} + +/** + * BACnet/IP Datalink Receive handler. + * + * @param src - returns the source address + * @param npdu - returns the NPDU buffer + * @param max_npdu -maximum size of the NPDU buffer + * @param timeout - number of milliseconds to wait for a packet + * + * @return Number of bytes received, or 0 if none or timeout. + */ +uint16_t bip6_receive( + BACNET_ADDRESS * src, + uint8_t * npdu, + uint16_t max_npdu, + unsigned timeout) +{ + uint16_t npdu_len = 0; /* return value */ + fd_set read_fds; + int max = 0; + struct timeval select_timeout; + struct sockaddr_in6 sin = { 0 }; + BACNET_IP6_ADDRESS addr = {{ 0 }}; + socklen_t sin_len = sizeof(sin); + int received_bytes = 0; + int offset = 0; + uint16_t i = 0; + + /* Make sure the socket is open */ + if (BIP6_Socket < 0) { + return 0; + } + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } else { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + FD_ZERO(&read_fds); + FD_SET(BIP6_Socket, &read_fds); + max = BIP6_Socket; + /* see if there is a packet for us */ + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { + received_bytes = + recvfrom(BIP6_Socket, (char *) &npdu[0], max_npdu, 0, + (struct sockaddr *) &sin, &sin_len); + } else { + return 0; + } + /* See if there is a problem */ + if (received_bytes < 0) { + return 0; + } + /* no problem, just no bytes */ + if (received_bytes == 0) { + return 0; + } + /* the signature of a BACnet/IPv6 packet */ + if (npdu[0] != BVLL_TYPE_BACNET_IP6) { + return 0; + } + /* pass the packet into the BBMD handler */ + memcpy(&addr.address[0], &(sin.sin6_addr), 16); + memcpy(&addr.port, &sin.sin6_port, 2); + offset = bvlc6_handler(&addr, src, npdu, received_bytes); + if (offset > 0) { + npdu_len = received_bytes - offset; + if (npdu_len <= max_npdu) { + /* shift the buffer to return a valid NPDU */ + for (i = 0; i < npdu_len; i++) { + npdu[i] = npdu[offset + i]; + } + } else { + npdu_len = 0; + } + } + + return npdu_len; +} + +/** Cleanup and close out the BACnet/IP services by closing the socket. + * @ingroup DLBIP6 + */ +void bip6_cleanup( + void) +{ + if (BIP6_Socket != -1) { + close(BIP6_Socket); + } + BIP6_Socket = -1; + WSACleanup(); + + return; +} + +/** Initialize the BACnet/IP services at the given interface. + * @ingroup DLBIP6 + * -# Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IPv6 data structures. + * -# Opens a UDP socket + * -# Configures the socket for sending and receiving + * -# Configures the socket so it can send multicasts + * -# Binds the socket to the local IP address at the specified port for + * BACnet/IPv6 (by default, 0xBAC0 = 47808). + * + * @note For Windows, ifname is the IPv6 address of the interface. + * + * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738639(v=vs.85).aspx + * + * @param ifname [in] The named interface to use for the network layer. + * + * @return True if the socket is successfully opened for BACnet/IPv6, + * else False if the socket functions fail. + */ +bool bip6_init( + char *ifname) +{ + WSADATA wd; + int RetVal; + + // Ask for Winsock version 2.2. + if ((RetVal = WSAStartup(MAKEWORD(2, 2), &wd)) != 0) { + fprintf(stderr, "BIP6: WSAStartup failed with error %d: %s\n", + RetVal, PrintError(RetVal)); + WSACleanup(); + exit(1); + } + if (BIP6_Addr.port == 0) { + bip6_set_port(0xBAC0); + } + debug_printf("BIP6: IPv6 UDP port: 0x%04X\n", BIP6_Addr.port); + bip6_set_interface(ifname); + if (BIP6_Broadcast_Addr.address[0] == 0) { + bvlc6_address_set(&BIP6_Broadcast_Addr, + BIP6_MULTICAST_LINK_LOCAL, 0, 0, 0, 0, 0, 0, + BIP6_MULTICAST_GROUP_ID); + } + if (BIP6_Socket == INVALID_SOCKET) { + fprintf(stderr, "BIP6: Fatal error: unable to serve on any address.\n"); + WSACleanup(); + exit(1); + } + + return true; +} diff --git a/ports/xplained/ASF/common/utils/make/Makefile.avr.in b/ports/xplained/ASF/common/utils/make/Makefile.avr.in index 5c73377e..1e9f0488 100644 --- a/ports/xplained/ASF/common/utils/make/Makefile.avr.in +++ b/ports/xplained/ASF/common/utils/make/Makefile.avr.in @@ -1,459 +1,459 @@ -# List of available make goals: -# -# all Default target, builds the project -# clean Clean up the project -# rebuild Rebuild the project -# -# doc Build the documentation -# cleandoc Clean up the documentation -# rebuilddoc Rebuild the documentation -# -# -# Copyright (c) 2009-2012 Atmel Corporation. All rights reserved. -# -# \asf_license_start -# -# \page License -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# 3. The name of Atmel may not be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# 4. This software may only be redistributed and used in connection with an -# Atmel microcontroller product. -# -# THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED -# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE -# EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# \asf_license_stop -# - -# Include the config.mk file from the current working path, e.g., where the -# user called make. -include config.mk - -# Tool to use to generate documentation from the source code -DOCGEN ?= doxygen - -# Look for source files relative to the top-level source directory -VPATH := $(PRJ_PATH) - -# Output target file -target := $(TARGET) - -# Output project name (target name minus suffix) -project := $(basename $(target)) - -# Output target file (typically ELF or static library) -ifeq ($(suffix $(target)),.a) -target_type := lib -else -ifeq ($(suffix $(target)),.elf) -target_type := elf -else -$(error "Target type $(target_type) is not supported") -endif -endif - -# Allow override of operating system detection. The user can add OS=Linux or -# OS=Windows on the command line to explicit set the host OS. -# -# This allows to work around broken uname utility on certain systems. -ifdef OS - ifeq ($(strip $(OS)), Linux) - os_type := Linux - endif - ifeq ($(strip $(OS)), Windows) - os_type := windows32_64 - endif -endif - -os_type ?= $(strip $(shell uname)) - -ifeq ($(os_type),windows32) -os := Windows -else -ifeq ($(os_type),windows64) -os := Windows -else -ifeq ($(os_type),windows32_64) -os ?= Windows -else -ifeq ($(os_type),) -os := Windows -else -# Default to Linux style operating system. Both Cygwin and mingw are fully -# compatible (for this Makefile) with Linux. -os := Linux -endif -endif -endif -endif - -# Output documentation directory and configuration file. -docdir := ../doxygen/html -doccfg := ../doxygen/doxyfile.doxygen - -CROSS ?= avr- -AR := $(CROSS)ar -AS := $(CROSS)as -CC := $(CROSS)gcc -CPP := $(CROSS)gcc -E -CXX := $(CROSS)g++ -LD := $(CROSS)g++ -NM := $(CROSS)nm -OBJCOPY := $(CROSS)objcopy -OBJDUMP := $(CROSS)objdump -SIZE := $(CROSS)size - -RM := rm -ifeq ($(os),Windows) -RMDIR := rmdir /S /Q -else -RMDIR := rmdir -p --ignore-fail-on-non-empty -endif - -# Strings for beautifying output -MSG_CLEAN_FILES = "RM *.o *.d" -MSG_CLEAN_DIRS = "RMDIR $(strip $(clean-dirs))" -MSG_CLEAN_DOC = "RMDIR $(docdir)" -MSG_MKDIR = "MKDIR $(dir $@)" - -MSG_INFO = "INFO " - -MSG_ARCHIVING = "AR $@" -MSG_ASSEMBLING = "AS $@" -MSG_BINARY_IMAGE = "OBJCOPY $@" -MSG_COMPILING = "CC $@" -MSG_COMPILING_CXX = "CXX $@" -MSG_EEPROM_IMAGE = "OBJCOPY $@" -MSG_EXTENDED_LISTING = "OBJDUMP $@" -MSG_IHEX_IMAGE = "OBJCOPY $@" -MSG_LINKING = "LN $@" -MSG_PREPROCESSING = "CPP $@" -MSG_SIZE = "SIZE $@" -MSG_SYMBOL_TABLE = "NM $@" - -MSG_GENERATING_DOC = "DOXYGEN $(docdir)" - -# Don't use make's built-in rules and variables -MAKEFLAGS += -rR - -# Don't print 'Entering directory ...' -MAKEFLAGS += --no-print-directory - -# Function for reversing the order of a list -reverse = $(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) - -# Hide command output by default, but allow the user to override this -# by adding V=1 on the command line. -# -# This is inspired by the Kbuild system used by the Linux kernel. -ifdef V - ifeq ("$(origin V)", "command line") - VERBOSE = $(V) - endif -endif -ifndef VERBOSE - VERBOSE = 0 -endif - -ifeq ($(VERBOSE), 1) - Q = -else - Q = @ -endif - -arflags-gnu-y := $(ARFLAGS) -asflags-gnu-y := $(ASFLAGS) -cflags-gnu-y := $(CFLAGS) -cxxflags-gnu-y := $(CXXFLAGS) -cppflags-gnu-y := $(CPPFLAGS) -cpuflags-gnu-y := -dbgflags-gnu-y := $(DBGFLAGS) -libflags-gnu-y := $(foreach LIB,$(LIBS),-l$(LIB)) -ldflags-gnu-y := $(LDFLAGS) -flashflags-gnu-y := $(FLASHFLAGS) -eepromflags-gnu-y := $(EEPROMFLAGS) -clean-files := -clean-dirs := - -clean-files += $(wildcard $(target) $(project).map) -clean-files += $(wildcard $(project).hex $(project).eep) -clean-files += $(wildcard $(project).lss $(project).sym) -clean-files += $(wildcard $(build)) - -# Use pipes instead of temporary files for communication between processes -cflags-gnu-y += -pipe -asflags-gnu-y += -pipe -ldflags-gnu-y += -pipe - -# Archiver flags. -arflags-gnu-y += rcs - -# Always enable warnings. And be very careful about implicit -# declarations. -cflags-gnu-y += -Wall -Wstrict-prototypes -Wmissing-prototypes -cflags-gnu-y += -Werror-implicit-function-declaration -cxxflags-gnu-y += -Wall -# IAR doesn't allow arithmetic on void pointers, so warn about that. -cflags-gnu-y += -Wpointer-arith -cxxflags-gnu-y += -Wpointer-arith - -# Preprocessor flags. -cppflags-gnu-y += $(foreach INC,$(addprefix $(PRJ_PATH)/,$(INC_PATH)),-I$(INC)) -asflags-gnu-y += $(foreach INC,$(addprefix $(PRJ_PATH)/,$(INC_PATH)),'-Wa,-I$(INC)') - -# CPU specific flags. -cpuflags-gnu-y += -mmcu=$(MCU) - -# Dependency file flags. -depflags = -MD -MP -MQ $@ - -# Debug specific flags. -ifdef BUILD_DEBUG_LEVEL -dbgflags-gnu-y += -g$(BUILD_DEBUG_LEVEL) -else -dbgflags-gnu-y += -gdwarf-2 -endif - -# Optimization specific flags. -ifdef BUILD_OPTIMIZATION -optflags-gnu-y = -O$(BUILD_OPTIMIZATION) -else -optflags-gnu-y = $(OPTIMIZATION) -endif - -# Relax compilation and linking. -cflags-gnu-y += -mrelax -cxxflags-gnu-y += -mrelax -asflags-gnu-y += -mrelax -ldflags-gnu-y += -Wl,--relax - -# Always preprocess assembler files. -asflags-gnu-y += -x assembler-with-cpp -# Compile C files using the GNU99 standard. -cflags-gnu-y += -std=gnu99 -# Compile C++ files using the GNU++98 standard. -cxxflags-gnu-y += -std=gnu++98 - -# Use unsigned character type when compiling. -cflags-gnu-y += -funsigned-char -cxxflags-gnu-y += -funsigned-char - -# Don't use strict aliasing (very common in embedded applications). -cflags-gnu-y += -fno-strict-aliasing -cxxflags-gnu-y += -fno-strict-aliasing - -# Separate each function and data into its own separate section to allow -# garbage collection of unused sections. -cflags-gnu-y += -ffunction-sections -fdata-sections -cxxflags-gnu-y += -ffunction-sections -fdata-sections - -# Garbage collect unreferred sections when linking. -ldflags-gnu-y += -Wl,--gc-sections - -# Output a link map file and a cross reference table -ldflags-gnu-y += -Wl,-Map=$(project).map,--cref - -# Add library search paths relative to the top level directory. -ldflags-gnu-y += $(foreach _LIB_PATH,$(addprefix $(PRJ_PATH)/,$(LIB_PATH)),-L$(_LIB_PATH)) - -a_flags = $(cpuflags-gnu-y) $(depflags) $(cppflags-gnu-y) $(asflags-gnu-y) -D__ASSEMBLY__ -c_flags = $(cpuflags-gnu-y) $(dbgflags-gnu-y) $(depflags) $(optflags-gnu-y) $(cppflags-gnu-y) $(cflags-gnu-y) -cxx_flags= $(cpuflags-gnu-y) $(dbgflags-gnu-y) $(depflags) $(optflags-gnu-y) $(cppflags-gnu-y) $(cxxflags-gnu-y) -l_flags = $(cpuflags-gnu-y) $(optflags-gnu-y) $(ldflags-gnu-y) -ar_flags = $(arflags-gnu-y) - -# Intel Hex file production flags -flashflags-gnu-y += -R .eeprom -R .usb_descriptor_table - -# Eeprom file production flags -eepromflags-gnu-y += -j .eeprom -eepromflags-gnu-y += --set-section-flags=.eeprom="alloc,load" -eepromflags-gnu-y += --change-section-lma .eeprom=0 - -# Source files list and part informations must already be included before -# running this makefile - -# If a custom build directory is specified, use it -- force trailing / in directory name. -ifdef BUILD_DIR - build-dir := $(dir $(BUILD_DIR))$(if $(notdir $(BUILD_DIR)),$(notdir $(BUILD_DIR))/) -else - build-dir = -endif - -# Create object files list from source files list. -obj-y := $(addprefix $(build-dir), $(addsuffix .o,$(basename $(CSRCS) $(ASSRCS)))) - -# Create dependency files list from source files list. -dep-files := $(wildcard $(foreach f,$(obj-y),$(basename $(f)).d)) - -clean-files += $(wildcard $(obj-y)) -clean-files += $(dep-files) - -clean-dirs += $(call reverse,$(sort $(wildcard $(dir $(obj-y))))) - -# Default target. -.PHONY: all -ifeq ($(target_type),lib) -all: $(target) $(project).lss $(project).sym -else -ifeq ($(target_type),elf) -all: $(target) $(project).hex $(project).lss $(project).sym -endif -endif - -# Clean up the project. -.PHONY: clean -clean: - @$(if $(strip $(clean-files)),echo $(MSG_CLEAN_FILES)) - $(if $(strip $(clean-files)),$(Q)$(RM) $(clean-files),) - @$(if $(strip $(clean-dirs)),echo $(MSG_CLEAN_DIRS)) -# Remove created directories, and make sure we only remove existing -# directories, since recursive rmdir might help us a bit on the way. -ifeq ($(os),Windows) - $(Q)$(if $(strip $(clean-dirs)), \ - $(RMDIR) $(strip $(subst /,\,$(clean-dirs)))) -else - $(Q)$(if $(strip $(clean-dirs)), \ - for directory in $(strip $(clean-dirs)); do \ - if [ -d "$$directory" ]; then \ - $(RMDIR) $$directory; \ - fi \ - done \ - ) -endif - -# Rebuild the project. -.PHONY: rebuild -rebuild: clean all - -.PHONY: objfiles -objfiles: $(obj-y) - -# Create object files from C source files. -$(build-dir)%.o: %.c $(MAKEFILE_PATH) config.mk - $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) -ifeq ($(os),Windows) - $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) -else - $(Q)test -d $(dir $@) || mkdir -p $(dir $@) -endif - @echo $(MSG_COMPILING) - $(Q)$(CC) $(c_flags) -c $< -o $@ - -# Create object files from C++ source files. -$(build-dir)%.o: %.cpp $(MAKEFILE_PATH) config.mk - $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) -ifeq ($(os),Windows) - $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) -else - $(Q)test -d $(dir $@) || mkdir -p $(dir $@) -endif - @echo $(MSG_COMPILING_CXX) - $(Q)$(CXX) $(cxx_flags) -c $< -o $@ - -# Preprocess and assemble: create object files from assembler source files. -$(build-dir)%.o: %.s $(MAKEFILE_PATH) config.mk - $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) -ifeq ($(os),Windows) - $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) -else - $(Q)test -d $(dir $@) || mkdir -p $(dir $@) -endif - @echo $(MSG_ASSEMBLING) - $(Q)$(CC) $(a_flags) -c $< -o $@ - -# Preprocess and assemble: create object files from assembler source files. -$(build-dir)%.o: %.S $(MAKEFILE_PATH) config.mk - $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) -ifeq ($(os),Windows) - $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) -else - $(Q)test -d $(dir $@) || mkdir -p $(dir $@) -endif - @echo $(MSG_ASSEMBLING) - $(Q)$(CC) $(a_flags) -c $< -o $@ - -# Include all dependency files to add depedency to all header files in use. -include $(dep-files) - -ifeq ($(target_type),lib) -# Archive object files into an archive -$(target): $(MAKEFILE_PATH) config.mk $(obj-y) - @echo $(MSG_ARCHIVING) - $(Q)$(AR) $(ar_flags) $@ $(obj-y) - @echo $(MSG_SIZE) - $(Q)$(SIZE) -Bxt $@ -else -ifeq ($(target_type),elf) -# Link the object files into an ELF file. Also make sure the target is rebuilt -# if the common Makefile.avr.in or project config.mk is changed. -$(target): $(MAKEFILE_PATH) config.mk $(obj-y) - @echo $(MSG_LINKING) - $(Q)$(CC) $(l_flags) $(obj-y) $(libflags-gnu-y) -o $@ - @echo $(MSG_SIZE) - $(Q)$(SIZE) -Ax $@ - $(Q)$(SIZE) -Bx $@ -endif -endif - -# Create extended function listing from target output file. -%.lss: $(target) - @echo $(MSG_EXTENDED_LISTING) - $(Q)$(OBJDUMP) -h -S $< > $@ - -# Create symbol table from target output file. -%.sym: $(target) - @echo $(MSG_SYMBOL_TABLE) - $(Q)$(NM) -n $< > $@ - -# Create Intel HEX image from ELF output file. -%.hex: $(target) - @echo $(MSG_IHEX_IMAGE) - $(Q)$(OBJCOPY) -O ihex $(flashflags-gnu-y) $< $@ - -# Create EEPROM Intel HEX image from ELF output file. -%.eep: $(target) - @echo $(MSG_EEPROM_IMAGE) - $(Q)$(OBJCOPY) $(eepromflags-gnu-y) -O ihex $< $@ || exit 0 - -# Provide information about the detected host operating system. -.SECONDARY: info-os -info-os: - @echo $(MSG_INFO)$(os) build host detected - -# Build Doxygen generated documentation. -.PHONY: doc -doc: - @echo $(MSG_GENERATING_DOC) - $(Q)cd $(dir $(doccfg)) && $(DOCGEN) $(notdir $(doccfg)) - -# Clean Doxygen generated documentation. -.PHONY: cleandoc -cleandoc: - @$(if $(wildcard $(docdir)),echo $(MSG_CLEAN_DOC)) - $(Q)$(if $(wildcard $(docdir)),$(RM) --recursive $(docdir)) - -# Rebuild the Doxygen generated documentation. -.PHONY: rebuilddoc -rebuilddoc: cleandoc doc +# List of available make goals: +# +# all Default target, builds the project +# clean Clean up the project +# rebuild Rebuild the project +# +# doc Build the documentation +# cleandoc Clean up the documentation +# rebuilddoc Rebuild the documentation +# +# +# Copyright (c) 2009-2012 Atmel Corporation. All rights reserved. +# +# \asf_license_start +# +# \page License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. The name of Atmel may not be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# 4. This software may only be redistributed and used in connection with an +# Atmel microcontroller product. +# +# THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE +# EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# \asf_license_stop +# + +# Include the config.mk file from the current working path, e.g., where the +# user called make. +include config.mk + +# Tool to use to generate documentation from the source code +DOCGEN ?= doxygen + +# Look for source files relative to the top-level source directory +VPATH := $(PRJ_PATH) + +# Output target file +target := $(TARGET) + +# Output project name (target name minus suffix) +project := $(basename $(target)) + +# Output target file (typically ELF or static library) +ifeq ($(suffix $(target)),.a) +target_type := lib +else +ifeq ($(suffix $(target)),.elf) +target_type := elf +else +$(error "Target type $(target_type) is not supported") +endif +endif + +# Allow override of operating system detection. The user can add OS=Linux or +# OS=Windows on the command line to explicit set the host OS. +# +# This allows to work around broken uname utility on certain systems. +ifdef OS + ifeq ($(strip $(OS)), Linux) + os_type := Linux + endif + ifeq ($(strip $(OS)), Windows) + os_type := windows32_64 + endif +endif + +os_type ?= $(strip $(shell uname)) + +ifeq ($(os_type),windows32) +os := Windows +else +ifeq ($(os_type),windows64) +os := Windows +else +ifeq ($(os_type),windows32_64) +os ?= Windows +else +ifeq ($(os_type),) +os := Windows +else +# Default to Linux style operating system. Both Cygwin and mingw are fully +# compatible (for this Makefile) with Linux. +os := Linux +endif +endif +endif +endif + +# Output documentation directory and configuration file. +docdir := ../doxygen/html +doccfg := ../doxygen/doxyfile.doxygen + +CROSS ?= avr- +AR := $(CROSS)ar +AS := $(CROSS)as +CC := $(CROSS)gcc +CPP := $(CROSS)gcc -E +CXX := $(CROSS)g++ +LD := $(CROSS)g++ +NM := $(CROSS)nm +OBJCOPY := $(CROSS)objcopy +OBJDUMP := $(CROSS)objdump +SIZE := $(CROSS)size + +RM := rm +ifeq ($(os),Windows) +RMDIR := rmdir /S /Q +else +RMDIR := rmdir -p --ignore-fail-on-non-empty +endif + +# Strings for beautifying output +MSG_CLEAN_FILES = "RM *.o *.d" +MSG_CLEAN_DIRS = "RMDIR $(strip $(clean-dirs))" +MSG_CLEAN_DOC = "RMDIR $(docdir)" +MSG_MKDIR = "MKDIR $(dir $@)" + +MSG_INFO = "INFO " + +MSG_ARCHIVING = "AR $@" +MSG_ASSEMBLING = "AS $@" +MSG_BINARY_IMAGE = "OBJCOPY $@" +MSG_COMPILING = "CC $@" +MSG_COMPILING_CXX = "CXX $@" +MSG_EEPROM_IMAGE = "OBJCOPY $@" +MSG_EXTENDED_LISTING = "OBJDUMP $@" +MSG_IHEX_IMAGE = "OBJCOPY $@" +MSG_LINKING = "LN $@" +MSG_PREPROCESSING = "CPP $@" +MSG_SIZE = "SIZE $@" +MSG_SYMBOL_TABLE = "NM $@" + +MSG_GENERATING_DOC = "DOXYGEN $(docdir)" + +# Don't use make's built-in rules and variables +MAKEFLAGS += -rR + +# Don't print 'Entering directory ...' +MAKEFLAGS += --no-print-directory + +# Function for reversing the order of a list +reverse = $(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) + +# Hide command output by default, but allow the user to override this +# by adding V=1 on the command line. +# +# This is inspired by the Kbuild system used by the Linux kernel. +ifdef V + ifeq ("$(origin V)", "command line") + VERBOSE = $(V) + endif +endif +ifndef VERBOSE + VERBOSE = 0 +endif + +ifeq ($(VERBOSE), 1) + Q = +else + Q = @ +endif + +arflags-gnu-y := $(ARFLAGS) +asflags-gnu-y := $(ASFLAGS) +cflags-gnu-y := $(CFLAGS) +cxxflags-gnu-y := $(CXXFLAGS) +cppflags-gnu-y := $(CPPFLAGS) +cpuflags-gnu-y := +dbgflags-gnu-y := $(DBGFLAGS) +libflags-gnu-y := $(foreach LIB,$(LIBS),-l$(LIB)) +ldflags-gnu-y := $(LDFLAGS) +flashflags-gnu-y := $(FLASHFLAGS) +eepromflags-gnu-y := $(EEPROMFLAGS) +clean-files := +clean-dirs := + +clean-files += $(wildcard $(target) $(project).map) +clean-files += $(wildcard $(project).hex $(project).eep) +clean-files += $(wildcard $(project).lss $(project).sym) +clean-files += $(wildcard $(build)) + +# Use pipes instead of temporary files for communication between processes +cflags-gnu-y += -pipe +asflags-gnu-y += -pipe +ldflags-gnu-y += -pipe + +# Archiver flags. +arflags-gnu-y += rcs + +# Always enable warnings. And be very careful about implicit +# declarations. +cflags-gnu-y += -Wall -Wstrict-prototypes -Wmissing-prototypes +cflags-gnu-y += -Werror-implicit-function-declaration +cxxflags-gnu-y += -Wall +# IAR doesn't allow arithmetic on void pointers, so warn about that. +cflags-gnu-y += -Wpointer-arith +cxxflags-gnu-y += -Wpointer-arith + +# Preprocessor flags. +cppflags-gnu-y += $(foreach INC,$(addprefix $(PRJ_PATH)/,$(INC_PATH)),-I$(INC)) +asflags-gnu-y += $(foreach INC,$(addprefix $(PRJ_PATH)/,$(INC_PATH)),'-Wa,-I$(INC)') + +# CPU specific flags. +cpuflags-gnu-y += -mmcu=$(MCU) + +# Dependency file flags. +depflags = -MD -MP -MQ $@ + +# Debug specific flags. +ifdef BUILD_DEBUG_LEVEL +dbgflags-gnu-y += -g$(BUILD_DEBUG_LEVEL) +else +dbgflags-gnu-y += -gdwarf-2 +endif + +# Optimization specific flags. +ifdef BUILD_OPTIMIZATION +optflags-gnu-y = -O$(BUILD_OPTIMIZATION) +else +optflags-gnu-y = $(OPTIMIZATION) +endif + +# Relax compilation and linking. +cflags-gnu-y += -mrelax +cxxflags-gnu-y += -mrelax +asflags-gnu-y += -mrelax +ldflags-gnu-y += -Wl,--relax + +# Always preprocess assembler files. +asflags-gnu-y += -x assembler-with-cpp +# Compile C files using the GNU99 standard. +cflags-gnu-y += -std=gnu99 +# Compile C++ files using the GNU++98 standard. +cxxflags-gnu-y += -std=gnu++98 + +# Use unsigned character type when compiling. +cflags-gnu-y += -funsigned-char +cxxflags-gnu-y += -funsigned-char + +# Don't use strict aliasing (very common in embedded applications). +cflags-gnu-y += -fno-strict-aliasing +cxxflags-gnu-y += -fno-strict-aliasing + +# Separate each function and data into its own separate section to allow +# garbage collection of unused sections. +cflags-gnu-y += -ffunction-sections -fdata-sections +cxxflags-gnu-y += -ffunction-sections -fdata-sections + +# Garbage collect unreferred sections when linking. +ldflags-gnu-y += -Wl,--gc-sections + +# Output a link map file and a cross reference table +ldflags-gnu-y += -Wl,-Map=$(project).map,--cref + +# Add library search paths relative to the top level directory. +ldflags-gnu-y += $(foreach _LIB_PATH,$(addprefix $(PRJ_PATH)/,$(LIB_PATH)),-L$(_LIB_PATH)) + +a_flags = $(cpuflags-gnu-y) $(depflags) $(cppflags-gnu-y) $(asflags-gnu-y) -D__ASSEMBLY__ +c_flags = $(cpuflags-gnu-y) $(dbgflags-gnu-y) $(depflags) $(optflags-gnu-y) $(cppflags-gnu-y) $(cflags-gnu-y) +cxx_flags= $(cpuflags-gnu-y) $(dbgflags-gnu-y) $(depflags) $(optflags-gnu-y) $(cppflags-gnu-y) $(cxxflags-gnu-y) +l_flags = $(cpuflags-gnu-y) $(optflags-gnu-y) $(ldflags-gnu-y) +ar_flags = $(arflags-gnu-y) + +# Intel Hex file production flags +flashflags-gnu-y += -R .eeprom -R .usb_descriptor_table + +# Eeprom file production flags +eepromflags-gnu-y += -j .eeprom +eepromflags-gnu-y += --set-section-flags=.eeprom="alloc,load" +eepromflags-gnu-y += --change-section-lma .eeprom=0 + +# Source files list and part informations must already be included before +# running this makefile + +# If a custom build directory is specified, use it -- force trailing / in directory name. +ifdef BUILD_DIR + build-dir := $(dir $(BUILD_DIR))$(if $(notdir $(BUILD_DIR)),$(notdir $(BUILD_DIR))/) +else + build-dir = +endif + +# Create object files list from source files list. +obj-y := $(addprefix $(build-dir), $(addsuffix .o,$(basename $(CSRCS) $(ASSRCS)))) + +# Create dependency files list from source files list. +dep-files := $(wildcard $(foreach f,$(obj-y),$(basename $(f)).d)) + +clean-files += $(wildcard $(obj-y)) +clean-files += $(dep-files) + +clean-dirs += $(call reverse,$(sort $(wildcard $(dir $(obj-y))))) + +# Default target. +.PHONY: all +ifeq ($(target_type),lib) +all: $(target) $(project).lss $(project).sym +else +ifeq ($(target_type),elf) +all: $(target) $(project).hex $(project).lss $(project).sym +endif +endif + +# Clean up the project. +.PHONY: clean +clean: + @$(if $(strip $(clean-files)),echo $(MSG_CLEAN_FILES)) + $(if $(strip $(clean-files)),$(Q)$(RM) $(clean-files),) + @$(if $(strip $(clean-dirs)),echo $(MSG_CLEAN_DIRS)) +# Remove created directories, and make sure we only remove existing +# directories, since recursive rmdir might help us a bit on the way. +ifeq ($(os),Windows) + $(Q)$(if $(strip $(clean-dirs)), \ + $(RMDIR) $(strip $(subst /,\,$(clean-dirs)))) +else + $(Q)$(if $(strip $(clean-dirs)), \ + for directory in $(strip $(clean-dirs)); do \ + if [ -d "$$directory" ]; then \ + $(RMDIR) $$directory; \ + fi \ + done \ + ) +endif + +# Rebuild the project. +.PHONY: rebuild +rebuild: clean all + +.PHONY: objfiles +objfiles: $(obj-y) + +# Create object files from C source files. +$(build-dir)%.o: %.c $(MAKEFILE_PATH) config.mk + $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) +ifeq ($(os),Windows) + $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) +else + $(Q)test -d $(dir $@) || mkdir -p $(dir $@) +endif + @echo $(MSG_COMPILING) + $(Q)$(CC) $(c_flags) -c $< -o $@ + +# Create object files from C++ source files. +$(build-dir)%.o: %.cpp $(MAKEFILE_PATH) config.mk + $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) +ifeq ($(os),Windows) + $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) +else + $(Q)test -d $(dir $@) || mkdir -p $(dir $@) +endif + @echo $(MSG_COMPILING_CXX) + $(Q)$(CXX) $(cxx_flags) -c $< -o $@ + +# Preprocess and assemble: create object files from assembler source files. +$(build-dir)%.o: %.s $(MAKEFILE_PATH) config.mk + $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) +ifeq ($(os),Windows) + $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) +else + $(Q)test -d $(dir $@) || mkdir -p $(dir $@) +endif + @echo $(MSG_ASSEMBLING) + $(Q)$(CC) $(a_flags) -c $< -o $@ + +# Preprocess and assemble: create object files from assembler source files. +$(build-dir)%.o: %.S $(MAKEFILE_PATH) config.mk + $(Q)test -d $(dir $@) || echo $(MSG_MKDIR) +ifeq ($(os),Windows) + $(Q)test -d $(dir $@) || mkdir $(subst /,\,$(dir $@)) +else + $(Q)test -d $(dir $@) || mkdir -p $(dir $@) +endif + @echo $(MSG_ASSEMBLING) + $(Q)$(CC) $(a_flags) -c $< -o $@ + +# Include all dependency files to add depedency to all header files in use. +include $(dep-files) + +ifeq ($(target_type),lib) +# Archive object files into an archive +$(target): $(MAKEFILE_PATH) config.mk $(obj-y) + @echo $(MSG_ARCHIVING) + $(Q)$(AR) $(ar_flags) $@ $(obj-y) + @echo $(MSG_SIZE) + $(Q)$(SIZE) -Bxt $@ +else +ifeq ($(target_type),elf) +# Link the object files into an ELF file. Also make sure the target is rebuilt +# if the common Makefile.avr.in or project config.mk is changed. +$(target): $(MAKEFILE_PATH) config.mk $(obj-y) + @echo $(MSG_LINKING) + $(Q)$(CC) $(l_flags) $(obj-y) $(libflags-gnu-y) -o $@ + @echo $(MSG_SIZE) + $(Q)$(SIZE) -Ax $@ + $(Q)$(SIZE) -Bx $@ +endif +endif + +# Create extended function listing from target output file. +%.lss: $(target) + @echo $(MSG_EXTENDED_LISTING) + $(Q)$(OBJDUMP) -h -S $< > $@ + +# Create symbol table from target output file. +%.sym: $(target) + @echo $(MSG_SYMBOL_TABLE) + $(Q)$(NM) -n $< > $@ + +# Create Intel HEX image from ELF output file. +%.hex: $(target) + @echo $(MSG_IHEX_IMAGE) + $(Q)$(OBJCOPY) -O ihex $(flashflags-gnu-y) $< $@ + +# Create EEPROM Intel HEX image from ELF output file. +%.eep: $(target) + @echo $(MSG_EEPROM_IMAGE) + $(Q)$(OBJCOPY) $(eepromflags-gnu-y) -O ihex $< $@ || exit 0 + +# Provide information about the detected host operating system. +.SECONDARY: info-os +info-os: + @echo $(MSG_INFO)$(os) build host detected + +# Build Doxygen generated documentation. +.PHONY: doc +doc: + @echo $(MSG_GENERATING_DOC) + $(Q)cd $(dir $(doccfg)) && $(DOCGEN) $(notdir $(doccfg)) + +# Clean Doxygen generated documentation. +.PHONY: cleandoc +cleandoc: + @$(if $(wildcard $(docdir)),echo $(MSG_CLEAN_DOC)) + $(Q)$(if $(wildcard $(docdir)),$(RM) --recursive $(docdir)) + +# Rebuild the Doxygen generated documentation. +.PHONY: rebuilddoc +rebuilddoc: cleandoc doc diff --git a/src/bvlc6.c b/src/bvlc6.c index a28d528e..f77692a6 100644 --- a/src/bvlc6.c +++ b/src/bvlc6.c @@ -1,2337 +1,2337 @@ -/*####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 /* for standard integer types uint8_t etc. */ -#include /* for the standard bool type. */ -#include -#include "bacenum.h" -#include "bacdcode.h" -#include "bacint.h" -#include "bvlc6.h" -#ifndef DEBUG_ENABLED -#define DEBUG_ENABLED 0 -#endif -#include "debug.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 && length) { - 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; -} - -/** 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 - */ -int bvlc6_encode_delete_foreign_device( - uint8_t * pdu, - uint16_t pdu_size, - uint32_t vmac_src, - BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY * fdt_entry) -{ - 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 (fdt_entry) { - bvlc6_encode_address(&pdu[offset], pdu_size-offset, - &fdt_entry->bip6_address); - offset += BIP6_ADDRESS_MAX; - encode_unsigned16(&pdu[offset], - fdt_entry->ttl_seconds); - offset += 2; - encode_unsigned16(&pdu[offset], - fdt_entry->ttl_seconds_remaining); - 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 - FDT Entry - * - * @return number of bytes decoded - */ -int bvlc6_decode_delete_foreign_device( - uint8_t * pdu, - uint16_t pdu_len, - uint32_t * vmac_src, - BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY * fdt_entry) -{ - int bytes_consumed = 0; - - const uint16_t length = BIP6_ADDRESS_MAX+3+2+2-4; - uint16_t offset = 0; - - if (pdu && (pdu_len >= length)) { - if (vmac_src) { - decode_unsigned24(&pdu[offset], vmac_src); - bytes_consumed = 3; - } - offset += 3; - if (fdt_entry) { - bvlc6_decode_address(&pdu[offset], pdu_len-offset, - &fdt_entry->bip6_address); - offset += BIP6_ADDRESS_MAX; - decode_unsigned16(&pdu[offset], - &fdt_entry->ttl_seconds); - offset += 2; - decode_unsigned16(&pdu[offset], - &fdt_entry->ttl_seconds_remaining); - bytes_consumed = (int)length; - } - 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 = 7; - 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 && length) { - 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; -} - -#ifdef TEST -#include -#include -#include "ctest.h" - -static void test_BVLC6_Address( - Test * pTest, - BACNET_IP6_ADDRESS * bip6_address_1, - BACNET_IP6_ADDRESS * bip6_address_2) -{ - unsigned i = 0; - - if (bip6_address_1 && bip6_address_2) { - ct_test(pTest, bip6_address_1->port == bip6_address_2->port); - for (i = 0; i < IP6_ADDRESS_MAX; i++) { - ct_test(pTest, bip6_address_1->address[i] == - bip6_address_2->address[i]); - } - } - - return; -} - -static int test_BVLC6_Header( - Test * pTest, - uint8_t * pdu, - uint16_t pdu_len, - uint8_t * message_type, - uint16_t * length) - -{ - int bytes_consumed = 0; - int len = 0; - - if (pdu && message_type && length) { - len = bvlc6_decode_header(pdu, pdu_len, message_type, - length); - ct_test(pTest, len == 4); - bytes_consumed = len; - } - - return bytes_consumed; -} - -static void test_BVLC6_Result_Code( - Test * pTest, - uint32_t vmac, - uint16_t result_code) -{ - uint8_t pdu[50] = { 0 }; - uint32_t test_vmac = 0; - uint16_t test_result_code = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, test_len = 0; - - len = bvlc6_encode_result(pdu, sizeof(pdu), vmac, result_code); - ct_test(pTest, len == 9); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_RESULT); - ct_test(pTest, length == 9); - test_len += bvlc6_decode_result(&pdu[4], length-4, - &test_vmac, &test_result_code); - ct_test(pTest, len == test_len); - ct_test(pTest, vmac == test_vmac); - ct_test(pTest, result_code == test_result_code); - len = bvlc6_encode_result(pdu, sizeof(pdu), 0xffffff+1, result_code); - ct_test(pTest, len == 0); -} - -static void test_BVLC6_Result( - Test * pTest) -{ - uint32_t vmac = 0; - uint16_t result_code[6] = { - BVLC6_RESULT_SUCCESSFUL_COMPLETION, - BVLC6_RESULT_ADDRESS_RESOLUTION_NAK, - BVLC6_RESULT_VIRTUAL_ADDRESS_RESOLUTION_NAK, - BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK, - BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK, - BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK - }; - unsigned int i = 0; - - vmac = 4194303; - for (i = 0; i < 6; i++) { - test_BVLC6_Result_Code(pTest, vmac, result_code[i]); - } -} - -static void test_BVLC6_Original_Unicast_NPDU_Message( - Test * pTest, - uint8_t * npdu, - uint16_t npdu_len, - uint32_t vmac_src, - uint32_t vmac_dst) -{ - uint8_t test_npdu[50] = { 0 }; - uint8_t pdu[60] = { 0 }; - uint32_t test_vmac_src = 0; - uint32_t test_vmac_dst = 0; - uint16_t test_npdu_len = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, msg_len = 0, test_len = 0; - uint16_t i = 0; - - len = bvlc6_encode_original_unicast(pdu, sizeof(pdu), - vmac_src, vmac_dst, - npdu, npdu_len); - msg_len = 10 + npdu_len; - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_ORIGINAL_UNICAST_NPDU); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_original_unicast(&pdu[4], length-4, - &test_vmac_src, &test_vmac_dst, - test_npdu, sizeof(test_npdu), &test_npdu_len); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac_src == test_vmac_src); - ct_test(pTest, vmac_dst == test_vmac_dst); - ct_test(pTest, npdu_len == test_npdu_len); - for (i = 0; i < npdu_len; i++) { - ct_test(pTest, npdu[i] == test_npdu[i]); - } -} - -static void test_BVLC6_Original_Unicast_NPDU( - Test * pTest) -{ - uint8_t npdu[50] = { 0 }; - uint32_t vmac_src = 0; - uint32_t vmac_dst = 0; - uint16_t npdu_len = 0; - uint16_t i = 0; - - test_BVLC6_Original_Unicast_NPDU_Message(pTest, - npdu, npdu_len, vmac_src, vmac_dst); - /* now with some NPDU data */ - for (i = 0; i < sizeof(npdu); i++) { - npdu[i] = i; - } - npdu_len = sizeof(npdu); - vmac_src = 4194303; - vmac_dst = 4194302; - test_BVLC6_Original_Unicast_NPDU_Message(pTest, - npdu, npdu_len, vmac_src, vmac_dst); -} - -static void test_BVLC6_Original_Broadcast_NPDU_Message( - Test * pTest, - uint8_t * npdu, - uint16_t npdu_len, - uint32_t vmac) -{ - uint8_t test_npdu[50] = { 0 }; - uint8_t pdu[60] = { 0 }; - uint32_t test_vmac = 0; - uint16_t test_npdu_len = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, msg_len = 0, test_len = 0; - uint16_t i = 0; - - len = bvlc6_encode_original_broadcast(pdu, sizeof(pdu), - vmac, npdu, npdu_len); - msg_len = 7 + npdu_len; - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_ORIGINAL_BROADCAST_NPDU); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_original_broadcast(&pdu[4], length-4, - &test_vmac, test_npdu, sizeof(test_npdu), &test_npdu_len); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac == test_vmac); - ct_test(pTest, npdu_len == test_npdu_len); - for (i = 0; i < npdu_len; i++) { - ct_test(pTest, npdu[i] == test_npdu[i]); - } -} - -static void test_BVLC6_Original_Broadcast_NPDU( - Test * pTest) -{ - uint8_t npdu[50] = { 0 }; - uint32_t vmac = 0; - uint16_t npdu_len = 0; - uint16_t i = 0; - - test_BVLC6_Original_Broadcast_NPDU_Message(pTest, - npdu, npdu_len, vmac); - /* now with some NPDU data */ - for (i = 0; i < sizeof(npdu); i++) { - npdu[i] = i; - } - npdu_len = sizeof(npdu); - vmac = 4194303; - test_BVLC6_Original_Broadcast_NPDU_Message(pTest, - npdu, npdu_len, vmac); -} - -static void test_BVLC6_Address_Resolution_Message( - Test * pTest, - uint32_t vmac_src, - uint32_t vmac_target) -{ - uint8_t pdu[60] = { 0 }; - uint32_t test_vmac_src = 0; - uint32_t test_vmac_target = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, test_len = 0; - const int msg_len = 10; - - len = bvlc6_encode_address_resolution(pdu, sizeof(pdu), - vmac_src, vmac_target); - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_ADDRESS_RESOLUTION); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_address_resolution(&pdu[4], length-4, - &test_vmac_src, &test_vmac_target); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac_src == test_vmac_src); - ct_test(pTest, vmac_target == test_vmac_target); -} - -static void test_BVLC6_Address_Resolution( - Test * pTest) -{ - uint32_t vmac_src = 0; - uint32_t vmac_target = 0; - - test_BVLC6_Address_Resolution_Message(pTest, - vmac_src, vmac_target); - vmac_src = 4194303; - vmac_target = 4194302; - test_BVLC6_Address_Resolution_Message(pTest, - vmac_src, vmac_target); -} - -static void test_BVLC6_Forwarded_Address_Resolution_Message( - Test * pTest, - uint32_t vmac_src, - uint32_t vmac_dst, - BACNET_IP6_ADDRESS * bip6_address) -{ - BACNET_IP6_ADDRESS test_bip6_address = {{0}}; - uint8_t pdu[60] = { 0 }; - uint32_t test_vmac_src = 0; - uint32_t test_vmac_dst = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, test_len = 0; - const int msg_len = 4 + 3 + 3 + BIP6_ADDRESS_MAX; - - len = bvlc6_encode_forwarded_address_resolution( - pdu, sizeof(pdu), - vmac_src, vmac_dst, - bip6_address); - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_FORWARDED_ADDRESS_RESOLUTION); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_forwarded_address_resolution( - &pdu[4], length-4, - &test_vmac_src, &test_vmac_dst, - &test_bip6_address); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac_src == test_vmac_src); - ct_test(pTest, vmac_dst == test_vmac_dst); - test_BVLC6_Address(pTest, bip6_address, &test_bip6_address); -} - -static void test_BVLC6_Forwarded_Address_Resolution( - Test * pTest) -{ - BACNET_IP6_ADDRESS bip6_address = {{0}}; - uint32_t vmac_src = 0; - uint32_t vmac_target = 0; - uint16_t i = 0; - - test_BVLC6_Forwarded_Address_Resolution_Message(pTest, - vmac_src, vmac_target, &bip6_address); - /* now with some address data */ - for (i = 0; i < sizeof(bip6_address.address); i++) { - bip6_address.address[i] = i; - } - bip6_address.port = 47808; - vmac_src = 4194303; - vmac_target = 4194302; - test_BVLC6_Forwarded_Address_Resolution_Message(pTest, - vmac_src, vmac_target, &bip6_address); -} - -static void test_BVLC6_Address_Resolution_Ack_Message( - Test * pTest, - uint32_t vmac_src, - uint32_t vmac_dst) -{ - uint8_t pdu[60] = { 0 }; - uint32_t test_vmac_src = 0; - uint32_t test_vmac_dst = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, test_len = 0; - const int msg_len = 10; - - len = bvlc6_encode_address_resolution_ack(pdu, sizeof(pdu), - vmac_src, vmac_dst); - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_ADDRESS_RESOLUTION_ACK); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_address_resolution_ack(&pdu[4], length-4, - &test_vmac_src, &test_vmac_dst); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac_src == test_vmac_src); - ct_test(pTest, vmac_dst == test_vmac_dst); -} - -static void test_BVLC6_Address_Resolution_Ack( - Test * pTest) -{ - uint32_t vmac_src = 0; - uint32_t vmac_dst = 0; - - test_BVLC6_Address_Resolution_Ack_Message(pTest, - vmac_src, vmac_dst); - vmac_src = 4194303; - vmac_dst = 4194302; - test_BVLC6_Address_Resolution_Ack_Message(pTest, - vmac_src, vmac_dst); -} - -static void test_BVLC6_Virtual_Address_Resolution_Message( - Test * pTest, - uint32_t vmac_src) -{ - uint8_t pdu[60] = { 0 }; - uint32_t test_vmac_src = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, test_len = 0; - const int msg_len = 7; - - len = bvlc6_encode_virtual_address_resolution(pdu, sizeof(pdu), - vmac_src); - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_VIRTUAL_ADDRESS_RESOLUTION); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_virtual_address_resolution(&pdu[4], length-4, - &test_vmac_src); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac_src == test_vmac_src); -} - -static void test_BVLC6_Virtual_Address_Resolution( - Test * pTest) -{ - uint32_t vmac_src = 0; - - test_BVLC6_Virtual_Address_Resolution_Message(pTest, - vmac_src); - vmac_src = 0x1234; - test_BVLC6_Virtual_Address_Resolution_Message(pTest, - vmac_src); -} - -static void test_BVLC6_Virtual_Address_Resolution_Ack_Message( - Test * pTest, - uint32_t vmac_src, - uint32_t vmac_dst) -{ - uint8_t pdu[60] = { 0 }; - uint32_t test_vmac_src = 0; - uint32_t test_vmac_dst = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, test_len = 0; - const int msg_len = 10; - - len = bvlc6_encode_virtual_address_resolution_ack(pdu, sizeof(pdu), - vmac_src, vmac_dst); - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_VIRTUAL_ADDRESS_RESOLUTION_ACK); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_virtual_address_resolution_ack(&pdu[4], length-4, - &test_vmac_src, &test_vmac_dst); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac_src == test_vmac_src); - ct_test(pTest, vmac_dst == test_vmac_dst); -} - -static void test_BVLC6_Virtual_Address_Resolution_Ack( - Test * pTest) -{ - uint32_t vmac_src = 0; - uint32_t vmac_dst = 0; - - test_BVLC6_Virtual_Address_Resolution_Ack_Message(pTest, - vmac_src, vmac_dst); - vmac_src = 4194303; - vmac_dst = 4194302; - test_BVLC6_Virtual_Address_Resolution_Ack_Message(pTest, - vmac_src, vmac_dst); -} - -static void test_BVLC6_Forwarded_NPDU_Message( - Test * pTest, - uint8_t * npdu, - uint16_t npdu_len, - uint32_t vmac_src, - BACNET_IP6_ADDRESS * bip6_address) -{ - uint8_t test_npdu[50] = { 0 }; - uint8_t pdu[75] = { 0 }; - uint32_t test_vmac_src = 0; - BACNET_IP6_ADDRESS test_bip6_address = {{0}}; - uint16_t test_npdu_len = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, msg_len = 0, test_len = 0; - uint16_t i = 0; - - len = bvlc6_encode_forwarded_npdu(pdu, sizeof(pdu), - vmac_src, bip6_address, - npdu, npdu_len); - msg_len = 1+1+2+3+BIP6_ADDRESS_MAX+npdu_len; - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_FORWARDED_NPDU); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_forwarded_npdu(&pdu[4], length-4, - &test_vmac_src, &test_bip6_address, - test_npdu, sizeof(test_npdu), &test_npdu_len); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac_src == test_vmac_src); - test_BVLC6_Address(pTest, bip6_address, &test_bip6_address); - ct_test(pTest, npdu_len == test_npdu_len); - for (i = 0; i < npdu_len; i++) { - ct_test(pTest, npdu[i] == test_npdu[i]); - } -} - -static void test_BVLC6_Forwarded_NPDU( - Test * pTest) -{ - uint8_t npdu[50] = { 0 }; - uint32_t vmac_src = 0; - BACNET_IP6_ADDRESS bip6_address = {{0}}; - uint16_t npdu_len = 0; - uint16_t i = 0; - - test_BVLC6_Forwarded_NPDU_Message(pTest, - npdu, npdu_len, vmac_src, &bip6_address); - for (i = 0; i < sizeof(bip6_address.address); i++) { - bip6_address.address[i] = i; - } - bip6_address.port = 47808; - /* now with some NPDU data */ - for (i = 0; i < sizeof(npdu); i++) { - npdu[i] = i; - } - npdu_len = sizeof(npdu); - vmac_src = 4194303; - test_BVLC6_Forwarded_NPDU_Message(pTest, - npdu, npdu_len, vmac_src, &bip6_address); -} - -static void test_BVLC6_Register_Foreign_Device_Message( - Test * pTest, - uint32_t vmac_src, - uint16_t ttl_seconds) -{ - uint8_t pdu[60] = { 0 }; - uint32_t test_vmac_src = 0; - uint16_t test_ttl_seconds = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, test_len = 0; - const int msg_len = 9; - - len = bvlc6_encode_register_foreign_device(pdu, sizeof(pdu), - vmac_src, ttl_seconds); - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_REGISTER_FOREIGN_DEVICE); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_register_foreign_device(&pdu[4], length-4, - &test_vmac_src, &test_ttl_seconds); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac_src == test_vmac_src); - ct_test(pTest, ttl_seconds == test_ttl_seconds); -} - -static void test_BVLC6_Register_Foreign_Device( - Test * pTest) -{ - uint32_t vmac_src = 0; - uint16_t ttl_seconds = 0; - - test_BVLC6_Register_Foreign_Device_Message(pTest, - vmac_src, ttl_seconds); - vmac_src = 4194303; - ttl_seconds = 600; - test_BVLC6_Register_Foreign_Device_Message(pTest, - vmac_src, ttl_seconds); -} - -static void test_BVLC6_Delete_Foreign_Device_Message( - Test * pTest, - uint32_t vmac_src, - BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY *fdt_entry) -{ - uint8_t pdu[64] = { 0 }; - uint32_t test_vmac_src = 0; - BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY test_fdt_entry = {0}; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, test_len = 0; - const int msg_len = 0x0019; - - len = bvlc6_encode_delete_foreign_device(pdu, sizeof(pdu), - vmac_src, fdt_entry); - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_DELETE_FOREIGN_DEVICE); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_delete_foreign_device(&pdu[4], length-4, - &test_vmac_src, &test_fdt_entry); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac_src == test_vmac_src); - test_BVLC6_Address(pTest, &fdt_entry->bip6_address, - &test_fdt_entry.bip6_address); - ct_test(pTest, fdt_entry->ttl_seconds == test_fdt_entry.ttl_seconds); - ct_test(pTest, fdt_entry->ttl_seconds_remaining == - test_fdt_entry.ttl_seconds_remaining); -} - -static void test_BVLC6_Delete_Foreign_Device( - Test * pTest) -{ - uint32_t vmac_src = 0; - BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY fdt_entry = {0}; - unsigned int i = 0; - - /* test with zeros */ - test_BVLC6_Delete_Foreign_Device_Message(pTest, - vmac_src, &fdt_entry); - /* test with valid values */ - vmac_src = 4194303; - for (i = 0; i < sizeof(fdt_entry.bip6_address.address); i++) { - fdt_entry.bip6_address.address[i] = i; - } - fdt_entry.bip6_address.port = 47808; - fdt_entry.ttl_seconds = 600; - fdt_entry.ttl_seconds_remaining = 42; - fdt_entry.next = NULL; - test_BVLC6_Delete_Foreign_Device_Message(pTest, - vmac_src, &fdt_entry); -} - -static void test_BVLC6_Secure_BVLL_Message( - Test * pTest, - uint8_t * sbuf, - uint16_t sbuf_len) -{ - uint8_t test_sbuf[50] = { 0 }; - uint8_t pdu[60] = { 0 }; - uint16_t test_sbuf_len = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, msg_len = 0, test_len = 0; - uint16_t i = 0; - - len = bvlc6_encode_secure_bvll(pdu, sizeof(pdu), - sbuf, sbuf_len); - msg_len = 1+1+2+sbuf_len; - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_SECURE_BVLL); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_secure_bvll(&pdu[4], length-4, - test_sbuf, sizeof(test_sbuf), &test_sbuf_len); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, sbuf_len == test_sbuf_len); - for (i = 0; i < sbuf_len; i++) { - ct_test(pTest, sbuf[i] == test_sbuf[i]); - } -} - -static void test_BVLC6_Secure_BVLL( - Test * pTest) -{ - uint8_t sbuf[50] = { 0 }; - uint16_t sbuf_len = 0; - uint16_t i = 0; - - test_BVLC6_Secure_BVLL_Message(pTest, - sbuf, sbuf_len); - /* now with some NPDU data */ - for (i = 0; i < sizeof(sbuf); i++) { - sbuf[i] = i; - } - sbuf_len = sizeof(sbuf); - test_BVLC6_Secure_BVLL_Message(pTest, - sbuf, sbuf_len); -} - -static void test_BVLC6_Distribute_Broadcast_To_Network_Message( - Test * pTest, - uint8_t * npdu, - uint16_t npdu_len, - uint32_t vmac) -{ - uint8_t test_npdu[50] = { 0 }; - uint8_t pdu[60] = { 0 }; - uint32_t test_vmac = 0; - uint16_t test_npdu_len = 0; - uint8_t message_type = 0; - uint16_t length = 0; - int len = 0, msg_len = 0, test_len = 0; - uint16_t i = 0; - - len = bvlc6_encode_distribute_broadcast_to_network(pdu, sizeof(pdu), - vmac, npdu, npdu_len); - msg_len = 7 + npdu_len; - ct_test(pTest, len == msg_len); - test_len = test_BVLC6_Header(pTest, - pdu, len, &message_type, &length); - ct_test(pTest, test_len == 4); - ct_test(pTest, message_type == BVLC6_DISTRIBUTE_BROADCAST_TO_NETWORK); - ct_test(pTest, length == msg_len); - test_len += bvlc6_decode_distribute_broadcast_to_network( - &pdu[4], length-4, - &test_vmac, test_npdu, sizeof(test_npdu), &test_npdu_len); - ct_test(pTest, len == test_len); - ct_test(pTest, msg_len == test_len); - ct_test(pTest, vmac == test_vmac); - ct_test(pTest, npdu_len == test_npdu_len); - for (i = 0; i < npdu_len; i++) { - ct_test(pTest, npdu[i] == test_npdu[i]); - } -} - -static void test_BVLC6_Distribute_Broadcast_To_Network( - Test * pTest) -{ - uint8_t npdu[50] = { 0 }; - uint32_t vmac = 0; - uint16_t npdu_len = 0; - uint16_t i = 0; - - test_BVLC6_Distribute_Broadcast_To_Network_Message(pTest, - npdu, npdu_len, vmac); - /* now with some NPDU data */ - for (i = 0; i < sizeof(npdu); i++) { - npdu[i] = i; - } - npdu_len = sizeof(npdu); - vmac = 4194303; - test_BVLC6_Distribute_Broadcast_To_Network_Message(pTest, - npdu, npdu_len, vmac); -} - -static void test_BVLC6_Address_Copy( - Test * pTest) -{ - unsigned int i = 0; - BACNET_IP6_ADDRESS src = {{0}}; - BACNET_IP6_ADDRESS dst = {{0}}; - bool status = false; - - /* test with zeros */ - status = bvlc6_address_copy(&dst, &src); - ct_test(pTest, status); - status = bvlc6_address_different(&dst, &src); - ct_test(pTest, !status); - /* test with valid values */ - for (i = 0; i < sizeof(src.address); i++) { - src.address[i] = 1 + i; - } - src.port = 47808; - status = bvlc6_address_copy(&dst, &src); - ct_test(pTest, status); - status = bvlc6_address_different(&dst, &src); - ct_test(pTest, !status); - /* test for different port */ - dst.port = 47809; - status = bvlc6_address_different(&dst, &src); - ct_test(pTest, status); - /* test for different address */ - dst.port = src.port; - for (i = 0; i < sizeof(src.address); i++) { - dst.address[i] = 0; - status = bvlc6_address_different(&dst, &src); - ct_test(pTest, status); - dst.address[i] = 1 + i; - } -} - -static void test_BVLC6_Address_Get_Set( - Test * pTest) -{ - uint16_t i = 0; - BACNET_IP6_ADDRESS src = {{0}}; - uint16_t group = 1; - uint16_t test_group = 0; - bool status = false; - - for (i = 0; i < 16; i++) { - status = bvlc6_address_set(&src, - group, 0, 0, 0, 0, 0, 0, 0); - ct_test(pTest, status); - status = bvlc6_address_get(&src, - &test_group, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - ct_test(pTest, status); - ct_test(pTest, group == test_group); - group = group<<1; - } -} - -static void test_BVLC6_VMAC_Address_Get_Set( - Test * pTest) -{ - uint16_t i = 0; - BACNET_ADDRESS addr; - uint32_t device_id = 1; - uint32_t test_device_id = 0; - bool status = false; - - for (i = 0; i < 24; i++) { - status = bvlc6_vmac_address_set(&addr, device_id); - ct_test(pTest, status); - ct_test(pTest, addr.mac_len == 3); - ct_test(pTest, addr.net == 0); - ct_test(pTest, addr.len == 0); - status = bvlc6_vmac_address_get(&addr, &test_device_id); - ct_test(pTest, status); - ct_test(pTest, device_id == test_device_id); - device_id = device_id<<1; - } -} - -void test_BVLC6( - Test * pTest) -{ - bool rc; - - /* individual tests */ - rc = ct_addTestFunction(pTest, test_BVLC6_Result); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Original_Unicast_NPDU); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Original_Broadcast_NPDU); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Address_Resolution); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Forwarded_Address_Resolution); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Address_Resolution_Ack); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Virtual_Address_Resolution); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Virtual_Address_Resolution_Ack); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Forwarded_NPDU); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Register_Foreign_Device); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Delete_Foreign_Device); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Secure_BVLL); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Distribute_Broadcast_To_Network); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Address_Copy); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_Address_Get_Set); - assert(rc); - rc = ct_addTestFunction(pTest, test_BVLC6_VMAC_Address_Get_Set); - assert(rc); -} - -#ifdef TEST_BVLC6 -int main( - void) -{ - Test *pTest; - - pTest = ct_create("BACnet Virtual Link Control IP/v6", NULL); - test_BVLC6(pTest); - /* configure output */ - ct_setStream(pTest, stdout); - ct_run(pTest); - (void) ct_report(pTest); - ct_destroy(pTest); - - return 0; -} -#endif /* TEST_BBMD */ -#endif /* TEST */ +/*####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 /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacint.h" +#include "bvlc6.h" +#ifndef DEBUG_ENABLED +#define DEBUG_ENABLED 0 +#endif +#include "debug.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 && length) { + 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; +} + +/** 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 + */ +int bvlc6_encode_delete_foreign_device( + uint8_t * pdu, + uint16_t pdu_size, + uint32_t vmac_src, + BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY * fdt_entry) +{ + 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 (fdt_entry) { + bvlc6_encode_address(&pdu[offset], pdu_size-offset, + &fdt_entry->bip6_address); + offset += BIP6_ADDRESS_MAX; + encode_unsigned16(&pdu[offset], + fdt_entry->ttl_seconds); + offset += 2; + encode_unsigned16(&pdu[offset], + fdt_entry->ttl_seconds_remaining); + 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 - FDT Entry + * + * @return number of bytes decoded + */ +int bvlc6_decode_delete_foreign_device( + uint8_t * pdu, + uint16_t pdu_len, + uint32_t * vmac_src, + BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY * fdt_entry) +{ + int bytes_consumed = 0; + + const uint16_t length = BIP6_ADDRESS_MAX+3+2+2-4; + uint16_t offset = 0; + + if (pdu && (pdu_len >= length)) { + if (vmac_src) { + decode_unsigned24(&pdu[offset], vmac_src); + bytes_consumed = 3; + } + offset += 3; + if (fdt_entry) { + bvlc6_decode_address(&pdu[offset], pdu_len-offset, + &fdt_entry->bip6_address); + offset += BIP6_ADDRESS_MAX; + decode_unsigned16(&pdu[offset], + &fdt_entry->ttl_seconds); + offset += 2; + decode_unsigned16(&pdu[offset], + &fdt_entry->ttl_seconds_remaining); + bytes_consumed = (int)length; + } + 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 = 7; + 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 && length) { + 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; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +static void test_BVLC6_Address( + Test * pTest, + BACNET_IP6_ADDRESS * bip6_address_1, + BACNET_IP6_ADDRESS * bip6_address_2) +{ + unsigned i = 0; + + if (bip6_address_1 && bip6_address_2) { + ct_test(pTest, bip6_address_1->port == bip6_address_2->port); + for (i = 0; i < IP6_ADDRESS_MAX; i++) { + ct_test(pTest, bip6_address_1->address[i] == + bip6_address_2->address[i]); + } + } + + return; +} + +static int test_BVLC6_Header( + Test * pTest, + uint8_t * pdu, + uint16_t pdu_len, + uint8_t * message_type, + uint16_t * length) + +{ + int bytes_consumed = 0; + int len = 0; + + if (pdu && message_type && length) { + len = bvlc6_decode_header(pdu, pdu_len, message_type, + length); + ct_test(pTest, len == 4); + bytes_consumed = len; + } + + return bytes_consumed; +} + +static void test_BVLC6_Result_Code( + Test * pTest, + uint32_t vmac, + uint16_t result_code) +{ + uint8_t pdu[50] = { 0 }; + uint32_t test_vmac = 0; + uint16_t test_result_code = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, test_len = 0; + + len = bvlc6_encode_result(pdu, sizeof(pdu), vmac, result_code); + ct_test(pTest, len == 9); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_RESULT); + ct_test(pTest, length == 9); + test_len += bvlc6_decode_result(&pdu[4], length-4, + &test_vmac, &test_result_code); + ct_test(pTest, len == test_len); + ct_test(pTest, vmac == test_vmac); + ct_test(pTest, result_code == test_result_code); + len = bvlc6_encode_result(pdu, sizeof(pdu), 0xffffff+1, result_code); + ct_test(pTest, len == 0); +} + +static void test_BVLC6_Result( + Test * pTest) +{ + uint32_t vmac = 0; + uint16_t result_code[6] = { + BVLC6_RESULT_SUCCESSFUL_COMPLETION, + BVLC6_RESULT_ADDRESS_RESOLUTION_NAK, + BVLC6_RESULT_VIRTUAL_ADDRESS_RESOLUTION_NAK, + BVLC6_RESULT_REGISTER_FOREIGN_DEVICE_NAK, + BVLC6_RESULT_DELETE_FOREIGN_DEVICE_NAK, + BVLC6_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK + }; + unsigned int i = 0; + + vmac = 4194303; + for (i = 0; i < 6; i++) { + test_BVLC6_Result_Code(pTest, vmac, result_code[i]); + } +} + +static void test_BVLC6_Original_Unicast_NPDU_Message( + Test * pTest, + uint8_t * npdu, + uint16_t npdu_len, + uint32_t vmac_src, + uint32_t vmac_dst) +{ + uint8_t test_npdu[50] = { 0 }; + uint8_t pdu[60] = { 0 }; + uint32_t test_vmac_src = 0; + uint32_t test_vmac_dst = 0; + uint16_t test_npdu_len = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, msg_len = 0, test_len = 0; + uint16_t i = 0; + + len = bvlc6_encode_original_unicast(pdu, sizeof(pdu), + vmac_src, vmac_dst, + npdu, npdu_len); + msg_len = 10 + npdu_len; + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_ORIGINAL_UNICAST_NPDU); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_original_unicast(&pdu[4], length-4, + &test_vmac_src, &test_vmac_dst, + test_npdu, sizeof(test_npdu), &test_npdu_len); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac_src == test_vmac_src); + ct_test(pTest, vmac_dst == test_vmac_dst); + ct_test(pTest, npdu_len == test_npdu_len); + for (i = 0; i < npdu_len; i++) { + ct_test(pTest, npdu[i] == test_npdu[i]); + } +} + +static void test_BVLC6_Original_Unicast_NPDU( + Test * pTest) +{ + uint8_t npdu[50] = { 0 }; + uint32_t vmac_src = 0; + uint32_t vmac_dst = 0; + uint16_t npdu_len = 0; + uint16_t i = 0; + + test_BVLC6_Original_Unicast_NPDU_Message(pTest, + npdu, npdu_len, vmac_src, vmac_dst); + /* now with some NPDU data */ + for (i = 0; i < sizeof(npdu); i++) { + npdu[i] = i; + } + npdu_len = sizeof(npdu); + vmac_src = 4194303; + vmac_dst = 4194302; + test_BVLC6_Original_Unicast_NPDU_Message(pTest, + npdu, npdu_len, vmac_src, vmac_dst); +} + +static void test_BVLC6_Original_Broadcast_NPDU_Message( + Test * pTest, + uint8_t * npdu, + uint16_t npdu_len, + uint32_t vmac) +{ + uint8_t test_npdu[50] = { 0 }; + uint8_t pdu[60] = { 0 }; + uint32_t test_vmac = 0; + uint16_t test_npdu_len = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, msg_len = 0, test_len = 0; + uint16_t i = 0; + + len = bvlc6_encode_original_broadcast(pdu, sizeof(pdu), + vmac, npdu, npdu_len); + msg_len = 7 + npdu_len; + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_ORIGINAL_BROADCAST_NPDU); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_original_broadcast(&pdu[4], length-4, + &test_vmac, test_npdu, sizeof(test_npdu), &test_npdu_len); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac == test_vmac); + ct_test(pTest, npdu_len == test_npdu_len); + for (i = 0; i < npdu_len; i++) { + ct_test(pTest, npdu[i] == test_npdu[i]); + } +} + +static void test_BVLC6_Original_Broadcast_NPDU( + Test * pTest) +{ + uint8_t npdu[50] = { 0 }; + uint32_t vmac = 0; + uint16_t npdu_len = 0; + uint16_t i = 0; + + test_BVLC6_Original_Broadcast_NPDU_Message(pTest, + npdu, npdu_len, vmac); + /* now with some NPDU data */ + for (i = 0; i < sizeof(npdu); i++) { + npdu[i] = i; + } + npdu_len = sizeof(npdu); + vmac = 4194303; + test_BVLC6_Original_Broadcast_NPDU_Message(pTest, + npdu, npdu_len, vmac); +} + +static void test_BVLC6_Address_Resolution_Message( + Test * pTest, + uint32_t vmac_src, + uint32_t vmac_target) +{ + uint8_t pdu[60] = { 0 }; + uint32_t test_vmac_src = 0; + uint32_t test_vmac_target = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, test_len = 0; + const int msg_len = 10; + + len = bvlc6_encode_address_resolution(pdu, sizeof(pdu), + vmac_src, vmac_target); + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_ADDRESS_RESOLUTION); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_address_resolution(&pdu[4], length-4, + &test_vmac_src, &test_vmac_target); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac_src == test_vmac_src); + ct_test(pTest, vmac_target == test_vmac_target); +} + +static void test_BVLC6_Address_Resolution( + Test * pTest) +{ + uint32_t vmac_src = 0; + uint32_t vmac_target = 0; + + test_BVLC6_Address_Resolution_Message(pTest, + vmac_src, vmac_target); + vmac_src = 4194303; + vmac_target = 4194302; + test_BVLC6_Address_Resolution_Message(pTest, + vmac_src, vmac_target); +} + +static void test_BVLC6_Forwarded_Address_Resolution_Message( + Test * pTest, + uint32_t vmac_src, + uint32_t vmac_dst, + BACNET_IP6_ADDRESS * bip6_address) +{ + BACNET_IP6_ADDRESS test_bip6_address = {{0}}; + uint8_t pdu[60] = { 0 }; + uint32_t test_vmac_src = 0; + uint32_t test_vmac_dst = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, test_len = 0; + const int msg_len = 4 + 3 + 3 + BIP6_ADDRESS_MAX; + + len = bvlc6_encode_forwarded_address_resolution( + pdu, sizeof(pdu), + vmac_src, vmac_dst, + bip6_address); + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_FORWARDED_ADDRESS_RESOLUTION); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_forwarded_address_resolution( + &pdu[4], length-4, + &test_vmac_src, &test_vmac_dst, + &test_bip6_address); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac_src == test_vmac_src); + ct_test(pTest, vmac_dst == test_vmac_dst); + test_BVLC6_Address(pTest, bip6_address, &test_bip6_address); +} + +static void test_BVLC6_Forwarded_Address_Resolution( + Test * pTest) +{ + BACNET_IP6_ADDRESS bip6_address = {{0}}; + uint32_t vmac_src = 0; + uint32_t vmac_target = 0; + uint16_t i = 0; + + test_BVLC6_Forwarded_Address_Resolution_Message(pTest, + vmac_src, vmac_target, &bip6_address); + /* now with some address data */ + for (i = 0; i < sizeof(bip6_address.address); i++) { + bip6_address.address[i] = i; + } + bip6_address.port = 47808; + vmac_src = 4194303; + vmac_target = 4194302; + test_BVLC6_Forwarded_Address_Resolution_Message(pTest, + vmac_src, vmac_target, &bip6_address); +} + +static void test_BVLC6_Address_Resolution_Ack_Message( + Test * pTest, + uint32_t vmac_src, + uint32_t vmac_dst) +{ + uint8_t pdu[60] = { 0 }; + uint32_t test_vmac_src = 0; + uint32_t test_vmac_dst = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, test_len = 0; + const int msg_len = 10; + + len = bvlc6_encode_address_resolution_ack(pdu, sizeof(pdu), + vmac_src, vmac_dst); + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_ADDRESS_RESOLUTION_ACK); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_address_resolution_ack(&pdu[4], length-4, + &test_vmac_src, &test_vmac_dst); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac_src == test_vmac_src); + ct_test(pTest, vmac_dst == test_vmac_dst); +} + +static void test_BVLC6_Address_Resolution_Ack( + Test * pTest) +{ + uint32_t vmac_src = 0; + uint32_t vmac_dst = 0; + + test_BVLC6_Address_Resolution_Ack_Message(pTest, + vmac_src, vmac_dst); + vmac_src = 4194303; + vmac_dst = 4194302; + test_BVLC6_Address_Resolution_Ack_Message(pTest, + vmac_src, vmac_dst); +} + +static void test_BVLC6_Virtual_Address_Resolution_Message( + Test * pTest, + uint32_t vmac_src) +{ + uint8_t pdu[60] = { 0 }; + uint32_t test_vmac_src = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, test_len = 0; + const int msg_len = 7; + + len = bvlc6_encode_virtual_address_resolution(pdu, sizeof(pdu), + vmac_src); + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_VIRTUAL_ADDRESS_RESOLUTION); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_virtual_address_resolution(&pdu[4], length-4, + &test_vmac_src); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac_src == test_vmac_src); +} + +static void test_BVLC6_Virtual_Address_Resolution( + Test * pTest) +{ + uint32_t vmac_src = 0; + + test_BVLC6_Virtual_Address_Resolution_Message(pTest, + vmac_src); + vmac_src = 0x1234; + test_BVLC6_Virtual_Address_Resolution_Message(pTest, + vmac_src); +} + +static void test_BVLC6_Virtual_Address_Resolution_Ack_Message( + Test * pTest, + uint32_t vmac_src, + uint32_t vmac_dst) +{ + uint8_t pdu[60] = { 0 }; + uint32_t test_vmac_src = 0; + uint32_t test_vmac_dst = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, test_len = 0; + const int msg_len = 10; + + len = bvlc6_encode_virtual_address_resolution_ack(pdu, sizeof(pdu), + vmac_src, vmac_dst); + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_VIRTUAL_ADDRESS_RESOLUTION_ACK); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_virtual_address_resolution_ack(&pdu[4], length-4, + &test_vmac_src, &test_vmac_dst); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac_src == test_vmac_src); + ct_test(pTest, vmac_dst == test_vmac_dst); +} + +static void test_BVLC6_Virtual_Address_Resolution_Ack( + Test * pTest) +{ + uint32_t vmac_src = 0; + uint32_t vmac_dst = 0; + + test_BVLC6_Virtual_Address_Resolution_Ack_Message(pTest, + vmac_src, vmac_dst); + vmac_src = 4194303; + vmac_dst = 4194302; + test_BVLC6_Virtual_Address_Resolution_Ack_Message(pTest, + vmac_src, vmac_dst); +} + +static void test_BVLC6_Forwarded_NPDU_Message( + Test * pTest, + uint8_t * npdu, + uint16_t npdu_len, + uint32_t vmac_src, + BACNET_IP6_ADDRESS * bip6_address) +{ + uint8_t test_npdu[50] = { 0 }; + uint8_t pdu[75] = { 0 }; + uint32_t test_vmac_src = 0; + BACNET_IP6_ADDRESS test_bip6_address = {{0}}; + uint16_t test_npdu_len = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, msg_len = 0, test_len = 0; + uint16_t i = 0; + + len = bvlc6_encode_forwarded_npdu(pdu, sizeof(pdu), + vmac_src, bip6_address, + npdu, npdu_len); + msg_len = 1+1+2+3+BIP6_ADDRESS_MAX+npdu_len; + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_FORWARDED_NPDU); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_forwarded_npdu(&pdu[4], length-4, + &test_vmac_src, &test_bip6_address, + test_npdu, sizeof(test_npdu), &test_npdu_len); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac_src == test_vmac_src); + test_BVLC6_Address(pTest, bip6_address, &test_bip6_address); + ct_test(pTest, npdu_len == test_npdu_len); + for (i = 0; i < npdu_len; i++) { + ct_test(pTest, npdu[i] == test_npdu[i]); + } +} + +static void test_BVLC6_Forwarded_NPDU( + Test * pTest) +{ + uint8_t npdu[50] = { 0 }; + uint32_t vmac_src = 0; + BACNET_IP6_ADDRESS bip6_address = {{0}}; + uint16_t npdu_len = 0; + uint16_t i = 0; + + test_BVLC6_Forwarded_NPDU_Message(pTest, + npdu, npdu_len, vmac_src, &bip6_address); + for (i = 0; i < sizeof(bip6_address.address); i++) { + bip6_address.address[i] = i; + } + bip6_address.port = 47808; + /* now with some NPDU data */ + for (i = 0; i < sizeof(npdu); i++) { + npdu[i] = i; + } + npdu_len = sizeof(npdu); + vmac_src = 4194303; + test_BVLC6_Forwarded_NPDU_Message(pTest, + npdu, npdu_len, vmac_src, &bip6_address); +} + +static void test_BVLC6_Register_Foreign_Device_Message( + Test * pTest, + uint32_t vmac_src, + uint16_t ttl_seconds) +{ + uint8_t pdu[60] = { 0 }; + uint32_t test_vmac_src = 0; + uint16_t test_ttl_seconds = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, test_len = 0; + const int msg_len = 9; + + len = bvlc6_encode_register_foreign_device(pdu, sizeof(pdu), + vmac_src, ttl_seconds); + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_REGISTER_FOREIGN_DEVICE); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_register_foreign_device(&pdu[4], length-4, + &test_vmac_src, &test_ttl_seconds); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac_src == test_vmac_src); + ct_test(pTest, ttl_seconds == test_ttl_seconds); +} + +static void test_BVLC6_Register_Foreign_Device( + Test * pTest) +{ + uint32_t vmac_src = 0; + uint16_t ttl_seconds = 0; + + test_BVLC6_Register_Foreign_Device_Message(pTest, + vmac_src, ttl_seconds); + vmac_src = 4194303; + ttl_seconds = 600; + test_BVLC6_Register_Foreign_Device_Message(pTest, + vmac_src, ttl_seconds); +} + +static void test_BVLC6_Delete_Foreign_Device_Message( + Test * pTest, + uint32_t vmac_src, + BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY *fdt_entry) +{ + uint8_t pdu[64] = { 0 }; + uint32_t test_vmac_src = 0; + BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY test_fdt_entry = {0}; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, test_len = 0; + const int msg_len = 0x0019; + + len = bvlc6_encode_delete_foreign_device(pdu, sizeof(pdu), + vmac_src, fdt_entry); + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_DELETE_FOREIGN_DEVICE); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_delete_foreign_device(&pdu[4], length-4, + &test_vmac_src, &test_fdt_entry); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac_src == test_vmac_src); + test_BVLC6_Address(pTest, &fdt_entry->bip6_address, + &test_fdt_entry.bip6_address); + ct_test(pTest, fdt_entry->ttl_seconds == test_fdt_entry.ttl_seconds); + ct_test(pTest, fdt_entry->ttl_seconds_remaining == + test_fdt_entry.ttl_seconds_remaining); +} + +static void test_BVLC6_Delete_Foreign_Device( + Test * pTest) +{ + uint32_t vmac_src = 0; + BACNET_IP6_FOREIGN_DEVICE_TABLE_ENTRY fdt_entry = {0}; + unsigned int i = 0; + + /* test with zeros */ + test_BVLC6_Delete_Foreign_Device_Message(pTest, + vmac_src, &fdt_entry); + /* test with valid values */ + vmac_src = 4194303; + for (i = 0; i < sizeof(fdt_entry.bip6_address.address); i++) { + fdt_entry.bip6_address.address[i] = i; + } + fdt_entry.bip6_address.port = 47808; + fdt_entry.ttl_seconds = 600; + fdt_entry.ttl_seconds_remaining = 42; + fdt_entry.next = NULL; + test_BVLC6_Delete_Foreign_Device_Message(pTest, + vmac_src, &fdt_entry); +} + +static void test_BVLC6_Secure_BVLL_Message( + Test * pTest, + uint8_t * sbuf, + uint16_t sbuf_len) +{ + uint8_t test_sbuf[50] = { 0 }; + uint8_t pdu[60] = { 0 }; + uint16_t test_sbuf_len = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, msg_len = 0, test_len = 0; + uint16_t i = 0; + + len = bvlc6_encode_secure_bvll(pdu, sizeof(pdu), + sbuf, sbuf_len); + msg_len = 1+1+2+sbuf_len; + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_SECURE_BVLL); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_secure_bvll(&pdu[4], length-4, + test_sbuf, sizeof(test_sbuf), &test_sbuf_len); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, sbuf_len == test_sbuf_len); + for (i = 0; i < sbuf_len; i++) { + ct_test(pTest, sbuf[i] == test_sbuf[i]); + } +} + +static void test_BVLC6_Secure_BVLL( + Test * pTest) +{ + uint8_t sbuf[50] = { 0 }; + uint16_t sbuf_len = 0; + uint16_t i = 0; + + test_BVLC6_Secure_BVLL_Message(pTest, + sbuf, sbuf_len); + /* now with some NPDU data */ + for (i = 0; i < sizeof(sbuf); i++) { + sbuf[i] = i; + } + sbuf_len = sizeof(sbuf); + test_BVLC6_Secure_BVLL_Message(pTest, + sbuf, sbuf_len); +} + +static void test_BVLC6_Distribute_Broadcast_To_Network_Message( + Test * pTest, + uint8_t * npdu, + uint16_t npdu_len, + uint32_t vmac) +{ + uint8_t test_npdu[50] = { 0 }; + uint8_t pdu[60] = { 0 }; + uint32_t test_vmac = 0; + uint16_t test_npdu_len = 0; + uint8_t message_type = 0; + uint16_t length = 0; + int len = 0, msg_len = 0, test_len = 0; + uint16_t i = 0; + + len = bvlc6_encode_distribute_broadcast_to_network(pdu, sizeof(pdu), + vmac, npdu, npdu_len); + msg_len = 7 + npdu_len; + ct_test(pTest, len == msg_len); + test_len = test_BVLC6_Header(pTest, + pdu, len, &message_type, &length); + ct_test(pTest, test_len == 4); + ct_test(pTest, message_type == BVLC6_DISTRIBUTE_BROADCAST_TO_NETWORK); + ct_test(pTest, length == msg_len); + test_len += bvlc6_decode_distribute_broadcast_to_network( + &pdu[4], length-4, + &test_vmac, test_npdu, sizeof(test_npdu), &test_npdu_len); + ct_test(pTest, len == test_len); + ct_test(pTest, msg_len == test_len); + ct_test(pTest, vmac == test_vmac); + ct_test(pTest, npdu_len == test_npdu_len); + for (i = 0; i < npdu_len; i++) { + ct_test(pTest, npdu[i] == test_npdu[i]); + } +} + +static void test_BVLC6_Distribute_Broadcast_To_Network( + Test * pTest) +{ + uint8_t npdu[50] = { 0 }; + uint32_t vmac = 0; + uint16_t npdu_len = 0; + uint16_t i = 0; + + test_BVLC6_Distribute_Broadcast_To_Network_Message(pTest, + npdu, npdu_len, vmac); + /* now with some NPDU data */ + for (i = 0; i < sizeof(npdu); i++) { + npdu[i] = i; + } + npdu_len = sizeof(npdu); + vmac = 4194303; + test_BVLC6_Distribute_Broadcast_To_Network_Message(pTest, + npdu, npdu_len, vmac); +} + +static void test_BVLC6_Address_Copy( + Test * pTest) +{ + unsigned int i = 0; + BACNET_IP6_ADDRESS src = {{0}}; + BACNET_IP6_ADDRESS dst = {{0}}; + bool status = false; + + /* test with zeros */ + status = bvlc6_address_copy(&dst, &src); + ct_test(pTest, status); + status = bvlc6_address_different(&dst, &src); + ct_test(pTest, !status); + /* test with valid values */ + for (i = 0; i < sizeof(src.address); i++) { + src.address[i] = 1 + i; + } + src.port = 47808; + status = bvlc6_address_copy(&dst, &src); + ct_test(pTest, status); + status = bvlc6_address_different(&dst, &src); + ct_test(pTest, !status); + /* test for different port */ + dst.port = 47809; + status = bvlc6_address_different(&dst, &src); + ct_test(pTest, status); + /* test for different address */ + dst.port = src.port; + for (i = 0; i < sizeof(src.address); i++) { + dst.address[i] = 0; + status = bvlc6_address_different(&dst, &src); + ct_test(pTest, status); + dst.address[i] = 1 + i; + } +} + +static void test_BVLC6_Address_Get_Set( + Test * pTest) +{ + uint16_t i = 0; + BACNET_IP6_ADDRESS src = {{0}}; + uint16_t group = 1; + uint16_t test_group = 0; + bool status = false; + + for (i = 0; i < 16; i++) { + status = bvlc6_address_set(&src, + group, 0, 0, 0, 0, 0, 0, 0); + ct_test(pTest, status); + status = bvlc6_address_get(&src, + &test_group, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + ct_test(pTest, status); + ct_test(pTest, group == test_group); + group = group<<1; + } +} + +static void test_BVLC6_VMAC_Address_Get_Set( + Test * pTest) +{ + uint16_t i = 0; + BACNET_ADDRESS addr; + uint32_t device_id = 1; + uint32_t test_device_id = 0; + bool status = false; + + for (i = 0; i < 24; i++) { + status = bvlc6_vmac_address_set(&addr, device_id); + ct_test(pTest, status); + ct_test(pTest, addr.mac_len == 3); + ct_test(pTest, addr.net == 0); + ct_test(pTest, addr.len == 0); + status = bvlc6_vmac_address_get(&addr, &test_device_id); + ct_test(pTest, status); + ct_test(pTest, device_id == test_device_id); + device_id = device_id<<1; + } +} + +void test_BVLC6( + Test * pTest) +{ + bool rc; + + /* individual tests */ + rc = ct_addTestFunction(pTest, test_BVLC6_Result); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Original_Unicast_NPDU); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Original_Broadcast_NPDU); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Address_Resolution); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Forwarded_Address_Resolution); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Address_Resolution_Ack); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Virtual_Address_Resolution); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Virtual_Address_Resolution_Ack); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Forwarded_NPDU); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Register_Foreign_Device); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Delete_Foreign_Device); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Secure_BVLL); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Distribute_Broadcast_To_Network); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Address_Copy); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_Address_Get_Set); + assert(rc); + rc = ct_addTestFunction(pTest, test_BVLC6_VMAC_Address_Get_Set); + assert(rc); +} + +#ifdef TEST_BVLC6 +int main( + void) +{ + Test *pTest; + + pTest = ct_create("BACnet Virtual Link Control IP/v6", NULL); + test_BVLC6(pTest); + /* configure output */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BBMD */ +#endif /* TEST */ diff --git a/src/vmac.c b/src/vmac.c index f0ade996..13a3b0e2 100644 --- a/src/vmac.c +++ b/src/vmac.c @@ -1,326 +1,326 @@ -/*####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 -#include -#include -#include -#include -#include "config.h" -#include "bacdef.h" -#include "keylist.h" -/* me! */ -#include "vmac.h" - -/** @file - Handle VMAC address binding */ - -/* This module is used to handle the virtual MAC address binding that */ -/* occurs in BACnet for ZigBee or IPv6. */ - -/* Key List for storing the object data sorted by instance number */ -static OS_Keylist VMAC_List; - -/** - * Returns the number of VMAC in the list - */ -unsigned int VMAC_Count(void) -{ - return (unsigned int)Keylist_Count(VMAC_List); -} - -/** - * Adds a VMAC to the list - * - * @param device_id - BACnet device object instance number - * @param src - BACnet/IPv6 address - * - * @return true if the device ID and MAC are added - */ -bool VMAC_Add(uint32_t device_id, struct vmac_data *src) -{ - bool status = false; - struct vmac_data *pVMAC = NULL; - int index = 0; - size_t i = 0; - - pVMAC = Keylist_Data(VMAC_List, device_id); - if (!pVMAC) { - pVMAC = calloc(1, sizeof(struct vmac_data)); - if (pVMAC) { - /* copy the MAC into the data store */ - for (i = 0; i < sizeof(pVMAC->mac); i++) { - if (i < src->mac_len) { - pVMAC->mac[i] = src->mac[i]; - } else { - break; - } - } - pVMAC->mac_len = src->mac_len; - index = Keylist_Data_Add(VMAC_List, device_id, pVMAC); - if (index >= 0) { - status = true; - printf("VMAC %u added.\n", (unsigned int)device_id); - } - } - } - - return status; -} - -/** - * Finds a VMAC in the list by seeking the Device ID, and deletes it. - * - * @param device_id - BACnet device object instance number - * - * @return pointer to the VMAC data from the list - be sure to free() it! - */ -bool VMAC_Delete(uint32_t device_id) -{ - bool status = false; - struct vmac_data *pVMAC; - - pVMAC = Keylist_Data_Delete(VMAC_List, device_id); - if (pVMAC) { - free(pVMAC); - status = true; - } - - return status; -} - -/** - * Finds a VMAC in the list by seeking the Device ID. - * - * @param device_id - BACnet device object instance number - * - * @return pointer to the VMAC data from the list - */ -struct vmac_data *VMAC_Find_By_Key(uint32_t device_id) -{ - return Keylist_Data(VMAC_List, device_id); -} - -/** Compare the VMAC address - * - * @param vmac1 - VMAC address that will be compared to vmac2 - * @param vmac2 - VMAC address that will be compared to vmac1 - * - * @return true if the addresses are different - */ -bool VMAC_Different( - struct vmac_data *vmac1, - struct vmac_data *vmac2) -{ - bool status = false; - unsigned int i = 0; - unsigned int mac_len = VMAC_MAC_MAX; - - if (vmac1 && vmac2) { - if (vmac1->mac_len != vmac2->mac_len) { - status = true; - } else { - if (vmac1->mac_len < mac_len) { - mac_len = (unsigned int)vmac1->mac_len; - } - for (i = 0; i < mac_len; i++) { - if (vmac1->mac[i] != vmac2->mac[i]) { - status = true; - } - } - } - } - - return status; -} - -/** Compare the VMAC address - * - * @param vmac1 - VMAC address that will be compared to vmac2 - * @param vmac2 - VMAC address that will be compared to vmac1 - * - * @return true if the addresses are the same - */ -bool VMAC_Match( - struct vmac_data *vmac1, - struct vmac_data *vmac2) -{ - bool status = false; - unsigned int i = 0; - unsigned int mac_len = VMAC_MAC_MAX; - - if (vmac1 && vmac2 && vmac1->mac_len) { - status = true; - if (vmac1->mac_len != vmac2->mac_len) { - status = false; - } else { - if (vmac1->mac_len < mac_len) { - mac_len = (unsigned int)vmac1->mac_len; - } - for (i = 0; i < mac_len; i++) { - if (vmac1->mac[i] != vmac2->mac[i]) { - status = false; - } - } - } - } - - return status; -} - -/** - * Finds a VMAC in the list by seeking a matching VMAC address - * - * @param vmac - VMAC address that will be sought - * @param device_id - BACnet device object instance number - * - * @return true if the VMAC address was found - */ -bool VMAC_Find_By_Data(struct vmac_data *vmac, uint32_t *device_id) -{ - bool status = false; - struct vmac_data *list_vmac; - int count = 0; - int index = 0; - - count = Keylist_Count(VMAC_List); - while (count) { - index = count - 1; - list_vmac = Keylist_Data_Index(VMAC_List, index); - if (list_vmac) { - if (VMAC_Match(vmac, list_vmac)) { - if (device_id) { - *device_id = Keylist_Key(VMAC_List, index); - } - status = true; - break; - } - } - count--; - } - - return status; -} - -/** - * Cleans up the memory used by the VMAC list data - */ -void VMAC_Cleanup(void) -{ - struct vmac_data *pVMAC; - - if (VMAC_List) { - do { - pVMAC = Keylist_Data_Pop(VMAC_List); - if (pVMAC) { - free(pVMAC); - } - } while (pVMAC); - Keylist_Delete(VMAC_List); - VMAC_List = NULL; - } -} - -/** - * Initializes the VMAC list data - */ -void VMAC_Init(void) -{ - VMAC_List = Keylist_Create(); - if (VMAC_List) { - atexit(VMAC_Cleanup); - printf("VMAC List initialized.\n"); - } -} - -#ifdef TEST -#include -#include -#include "ctest.h" - -void testVMAC( - Test * pTest) -{ - uint32_t device_id = 123; - uint32_t test_device_id = 0; - struct vmac_data test_vmac_data; - struct vmac_data *pVMAC; - unsigned int i = 0; - bool status = false; - - VMAC_Init(); - for (i = 0; i < VMAC_MAC_MAX; i++) { - test_vmac_data.mac[i] = 1 + i; - } - test_vmac_data.mac_len = VMAC_MAC_MAX; - status = VMAC_Add(device_id, &test_vmac_data); - ct_test(pTest, status); - pVMAC = VMAC_Find_By_Key(0); - ct_test(pTest, pVMAC == NULL); - pVMAC = VMAC_Find_By_Key(device_id); - ct_test(pTest, pVMAC); - status = VMAC_Different(pVMAC, &test_vmac_data); - ct_test(pTest, !status); - status = VMAC_Match(pVMAC, &test_vmac_data); - ct_test(pTest, status); - status = VMAC_Find_By_Data(&test_vmac_data, &test_device_id); - ct_test(pTest, status); - ct_test(pTest, test_device_id == device_id); - status = VMAC_Delete(device_id); - ct_test(pTest, status); - pVMAC = VMAC_Find_By_Key(device_id); - ct_test(pTest, pVMAC == NULL); - VMAC_Cleanup(); -} - -#ifdef TEST_VMAC -int main( - void) -{ - Test *pTest; - bool rc; - - pTest = ct_create("BACnet VMAC", NULL); - /* individual tests */ - rc = ct_addTestFunction(pTest, testVMAC); - assert(rc); - - ct_setStream(pTest, stdout); - ct_run(pTest); - (void) ct_report(pTest); - ct_destroy(pTest); - - return 0; -} -#endif -#endif +/*####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 +#include +#include +#include +#include +#include "config.h" +#include "bacdef.h" +#include "keylist.h" +/* me! */ +#include "vmac.h" + +/** @file + Handle VMAC address binding */ + +/* This module is used to handle the virtual MAC address binding that */ +/* occurs in BACnet for ZigBee or IPv6. */ + +/* Key List for storing the object data sorted by instance number */ +static OS_Keylist VMAC_List; + +/** + * Returns the number of VMAC in the list + */ +unsigned int VMAC_Count(void) +{ + return (unsigned int)Keylist_Count(VMAC_List); +} + +/** + * Adds a VMAC to the list + * + * @param device_id - BACnet device object instance number + * @param src - BACnet/IPv6 address + * + * @return true if the device ID and MAC are added + */ +bool VMAC_Add(uint32_t device_id, struct vmac_data *src) +{ + bool status = false; + struct vmac_data *pVMAC = NULL; + int index = 0; + size_t i = 0; + + pVMAC = Keylist_Data(VMAC_List, device_id); + if (!pVMAC) { + pVMAC = calloc(1, sizeof(struct vmac_data)); + if (pVMAC) { + /* copy the MAC into the data store */ + for (i = 0; i < sizeof(pVMAC->mac); i++) { + if (i < src->mac_len) { + pVMAC->mac[i] = src->mac[i]; + } else { + break; + } + } + pVMAC->mac_len = src->mac_len; + index = Keylist_Data_Add(VMAC_List, device_id, pVMAC); + if (index >= 0) { + status = true; + printf("VMAC %u added.\n", (unsigned int)device_id); + } + } + } + + return status; +} + +/** + * Finds a VMAC in the list by seeking the Device ID, and deletes it. + * + * @param device_id - BACnet device object instance number + * + * @return pointer to the VMAC data from the list - be sure to free() it! + */ +bool VMAC_Delete(uint32_t device_id) +{ + bool status = false; + struct vmac_data *pVMAC; + + pVMAC = Keylist_Data_Delete(VMAC_List, device_id); + if (pVMAC) { + free(pVMAC); + status = true; + } + + return status; +} + +/** + * Finds a VMAC in the list by seeking the Device ID. + * + * @param device_id - BACnet device object instance number + * + * @return pointer to the VMAC data from the list + */ +struct vmac_data *VMAC_Find_By_Key(uint32_t device_id) +{ + return Keylist_Data(VMAC_List, device_id); +} + +/** Compare the VMAC address + * + * @param vmac1 - VMAC address that will be compared to vmac2 + * @param vmac2 - VMAC address that will be compared to vmac1 + * + * @return true if the addresses are different + */ +bool VMAC_Different( + struct vmac_data *vmac1, + struct vmac_data *vmac2) +{ + bool status = false; + unsigned int i = 0; + unsigned int mac_len = VMAC_MAC_MAX; + + if (vmac1 && vmac2) { + if (vmac1->mac_len != vmac2->mac_len) { + status = true; + } else { + if (vmac1->mac_len < mac_len) { + mac_len = (unsigned int)vmac1->mac_len; + } + for (i = 0; i < mac_len; i++) { + if (vmac1->mac[i] != vmac2->mac[i]) { + status = true; + } + } + } + } + + return status; +} + +/** Compare the VMAC address + * + * @param vmac1 - VMAC address that will be compared to vmac2 + * @param vmac2 - VMAC address that will be compared to vmac1 + * + * @return true if the addresses are the same + */ +bool VMAC_Match( + struct vmac_data *vmac1, + struct vmac_data *vmac2) +{ + bool status = false; + unsigned int i = 0; + unsigned int mac_len = VMAC_MAC_MAX; + + if (vmac1 && vmac2 && vmac1->mac_len) { + status = true; + if (vmac1->mac_len != vmac2->mac_len) { + status = false; + } else { + if (vmac1->mac_len < mac_len) { + mac_len = (unsigned int)vmac1->mac_len; + } + for (i = 0; i < mac_len; i++) { + if (vmac1->mac[i] != vmac2->mac[i]) { + status = false; + } + } + } + } + + return status; +} + +/** + * Finds a VMAC in the list by seeking a matching VMAC address + * + * @param vmac - VMAC address that will be sought + * @param device_id - BACnet device object instance number + * + * @return true if the VMAC address was found + */ +bool VMAC_Find_By_Data(struct vmac_data *vmac, uint32_t *device_id) +{ + bool status = false; + struct vmac_data *list_vmac; + int count = 0; + int index = 0; + + count = Keylist_Count(VMAC_List); + while (count) { + index = count - 1; + list_vmac = Keylist_Data_Index(VMAC_List, index); + if (list_vmac) { + if (VMAC_Match(vmac, list_vmac)) { + if (device_id) { + *device_id = Keylist_Key(VMAC_List, index); + } + status = true; + break; + } + } + count--; + } + + return status; +} + +/** + * Cleans up the memory used by the VMAC list data + */ +void VMAC_Cleanup(void) +{ + struct vmac_data *pVMAC; + + if (VMAC_List) { + do { + pVMAC = Keylist_Data_Pop(VMAC_List); + if (pVMAC) { + free(pVMAC); + } + } while (pVMAC); + Keylist_Delete(VMAC_List); + VMAC_List = NULL; + } +} + +/** + * Initializes the VMAC list data + */ +void VMAC_Init(void) +{ + VMAC_List = Keylist_Create(); + if (VMAC_List) { + atexit(VMAC_Cleanup); + printf("VMAC List initialized.\n"); + } +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testVMAC( + Test * pTest) +{ + uint32_t device_id = 123; + uint32_t test_device_id = 0; + struct vmac_data test_vmac_data; + struct vmac_data *pVMAC; + unsigned int i = 0; + bool status = false; + + VMAC_Init(); + for (i = 0; i < VMAC_MAC_MAX; i++) { + test_vmac_data.mac[i] = 1 + i; + } + test_vmac_data.mac_len = VMAC_MAC_MAX; + status = VMAC_Add(device_id, &test_vmac_data); + ct_test(pTest, status); + pVMAC = VMAC_Find_By_Key(0); + ct_test(pTest, pVMAC == NULL); + pVMAC = VMAC_Find_By_Key(device_id); + ct_test(pTest, pVMAC); + status = VMAC_Different(pVMAC, &test_vmac_data); + ct_test(pTest, !status); + status = VMAC_Match(pVMAC, &test_vmac_data); + ct_test(pTest, status); + status = VMAC_Find_By_Data(&test_vmac_data, &test_device_id); + ct_test(pTest, status); + ct_test(pTest, test_device_id == device_id); + status = VMAC_Delete(device_id); + ct_test(pTest, status); + pVMAC = VMAC_Find_By_Key(device_id); + ct_test(pTest, pVMAC == NULL); + VMAC_Cleanup(); +} + +#ifdef TEST_VMAC +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet VMAC", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testVMAC); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif +#endif