diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bda13a6..cb51f777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,6 @@ The git repositories are hosted at the following sites: ### Security ### Added - -* Added segmentation support for server devices for some services. - Configure BACNET_SEGMENTATION_ENABLED=1 to include in the library, - and adjust BACNET_MAX_SEGMENTS_ACCEPTED for maximum number of segments. (#974) - ### Changed * Changed basic object API for units properties to use BACNET_ENGINEERING_UNITS diff --git a/CMakeLists.txt b/CMakeLists.txt index 3514f6f8..cb5c0df3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,11 +84,6 @@ option( "compile with secure-connect support" OFF) -option( - BACNET_SEGMENTATION_ENABLED - "enable segmentation" - ON) - if(NOT (BACDL_ETHERNET OR BACDL_MSTP OR BACDL_ARCNET OR @@ -677,8 +672,6 @@ add_library(${PROJECT_NAME} src/bacnet/rp.h src/bacnet/rpm.c src/bacnet/rpm.h - $<$:src/bacnet/segmentack.c> - $<$:src/bacnet/segmentack.h> src/bacnet/timer_value.c src/bacnet/timer_value.h src/bacnet/timestamp.c @@ -730,7 +723,6 @@ target_compile_definitions( $<$:BACDL_NONE> $<$:BACNET_PROPERTY_LISTS=1> $<$:BAC_ROUTING> - $<$:BACNET_SEGMENTATION_ENABLED> $<$>:BACNET_STACK_STATIC_DEFINE> PRIVATE PRINT_ENABLED=1) @@ -1283,4 +1275,3 @@ message(STATUS "BACNET: BACDL_ARCNET:...................\"${BACDL_ARCNET}\"") message(STATUS "BACNET: BACDL_MSTP:.....................\"${BACDL_MSTP}\"") message(STATUS "BACNET: BACDL_ZIGBEE:...................\"${BACDL_ZIGBEE}\"") message(STATUS "BACNET: BACDL_ETHERNET:.................\"${BACDL_ETHERNET}\"") -message(STATUS "BACNET: BACNET_SEGMENTATION_ENABLED:....\"${BACNET_SEGMENTATION_ENABLED}\"") diff --git a/Makefile b/Makefile index 8e8e6d23..442c5728 100644 --- a/Makefile +++ b/Makefile @@ -224,10 +224,6 @@ server-discover: server-mini: $(MAKE) LEGACY=true NOTIFY=false -s -C apps $@ -.PHONY: server-segmentation -server-segmentation: - $(MAKE) LEGACY=true SEGMENT=true -s -C apps server - .PHONY: sc-hub sc-hub: $(MAKE) LEGACY=true BACDL=bsc -s -C apps $@ diff --git a/apps/Makefile b/apps/Makefile index 6aa43e92..bfe60035 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -223,11 +223,6 @@ ifeq (${LEGACY},true) BACNET_DEFINES += -DBACNET_STACK_DEPRECATED_DISABLE endif -ifeq (${SEGMENT},true) -# enable segmentation support -BACNET_DEFINES += -DBACNET_SEGMENTATION_ENABLED=1 -endif - ifeq (${NOTIFY},false) # disable intrinsic reporting else diff --git a/apps/server/main.c b/apps/server/main.c index 960cddc0..249997a6 100644 --- a/apps/server/main.c +++ b/apps/server/main.c @@ -174,20 +174,6 @@ static void Init_Service_Handlers(void) (unsigned)object_data.object_instance); } } -#if BACNET_SEGMENTATION_ENABLED - printf("Segmentation is enabled.\n"); - /* create extra object to stress the object list */ - for (i = 0; i < 500; i++) { - object_data.object_instance = BACNET_MAX_INSTANCE; - object_data.object_type = OBJECT_ANALOG_INPUT; - if (Device_Create_Object(&object_data)) { - printf( - "Created object %s-%u\n", - bactext_object_type_name(object_data.object_type), - (unsigned)object_data.object_instance); - } - } -#endif /* we need to handle who-is to support dynamic device binding */ apdu_set_unconfirmed_handler( SERVICE_UNCONFIRMED_WHO_IS, handler_who_is_who_am_i_unicast); diff --git a/ports/atmega328/device.c b/ports/atmega328/device.c index ad01a4a2..dca50848 100644 --- a/ports/atmega328/device.c +++ b/ports/atmega328/device.c @@ -58,11 +58,6 @@ bool Device_Set_Object_Instance_Number(uint32_t object_id) return status; } -BACNET_SEGMENTATION Device_Segmentation_Supported(void) -{ - return SEGMENTATION_NONE; -} - bool Device_Valid_Object_Instance_Number(uint32_t object_id) { /* BACnet allows for a wildcard instance number */ diff --git a/src/bacnet/apdu.h b/src/bacnet/apdu.h index 502dddd6..057470cf 100644 --- a/src/bacnet/apdu.h +++ b/src/bacnet/apdu.h @@ -32,21 +32,6 @@ typedef struct _confirmed_service_ack_data { uint8_t proposed_window_number; } BACNET_CONFIRMED_SERVICE_ACK_DATA; -typedef struct BACnet_Apdu_Fixed_Header { - /* pdu type Confirmed Request or Complex ACK */ - uint8_t pdu_type; - union { - /* Data for pdu type PDU_TYPE_CONFIRMED_SERVICE_REQUEST */ - struct _confirmed_service_data request_data; - /* Data for pdu type PDU_TYPE_COMPLEX_ACK */ - struct _confirmed_service_ack_data ack_data; - /* Common data for both types */ - struct _confirmed_service_ack_data common_data; - } service_data; - /* Service number */ - uint8_t service_choice; -} BACNET_APDU_FIXED_HEADER; - uint8_t apdu_network_priority(void); void apdu_network_priority_set(uint8_t pri); diff --git a/src/bacnet/bacdef.h b/src/bacnet/bacdef.h index f6b43d50..aa35e05b 100644 --- a/src/bacnet/bacdef.h +++ b/src/bacnet/bacdef.h @@ -212,24 +212,8 @@ typedef struct BACnet_Object_Id { uint32_t instance; } BACNET_OBJECT_ID; -#if !defined(BACNET_MAX_SEGMENTS_ACCEPTED) -#if BACNET_SEGMENTATION_ENABLED -/* note: BACNET_MAX_SEGMENTS_ACCEPTED can be 1..255. - ASDU in this library is usually sized for 16-bit at 65535 max. - Therefore, the default here is limited to avoid overflow warnings. */ -#define BACNET_MAX_SEGMENTS_ACCEPTED 32 -#else -#define BACNET_MAX_SEGMENTS_ACCEPTED 1 -#endif -#endif -#if !defined(MAX_APDU) -#define MAX_APDU 1476 -#endif #define MAX_NPDU (1 + 1 + 2 + 1 + MAX_MAC_LEN + 2 + 1 + MAX_MAC_LEN + 1 + 1 + 2) #define MAX_PDU (MAX_APDU + MAX_NPDU) -/* Application Service Data Unit (ASDU) that has not yet been segmented - into a protocol data unit (PDU) by the lower layer. */ -#define MAX_ASDU ((MAX_APDU * BACNET_MAX_SEGMENTS_ACCEPTED) + MAX_NPDU) #define BACNET_ID_VALUE(bacnet_object_instance, bacnet_object_type) \ ((((bacnet_object_type) & BACNET_MAX_OBJECT) << BACNET_INSTANCE_BITS) | \ diff --git a/src/bacnet/basic/binding/address.c b/src/bacnet/basic/binding/address.c index 1f092bc9..25669f03 100644 --- a/src/bacnet/basic/binding/address.c +++ b/src/bacnet/basic/binding/address.c @@ -45,10 +45,6 @@ static struct Address_Cache_Entry { uint8_t Flags; uint32_t device_id; unsigned max_apdu; -#if BACNET_SEGMENTATION_ENABLED - uint8_t segmentation; - uint16_t maxsegments; -#endif BACNET_ADDRESS address; uint32_t TimeToLive; } Address_Cache[MAX_ADDRESS_CACHE]; @@ -350,16 +346,9 @@ void address_set_device_TTL( * @param device_id Device-Id * @param max_apdu Pointer to a variable, taking the maximum APDU size. * @param src Pointer to address structure for return. - * @param segmentation Pointer to a variable, taking the BACNET_SEGMENTATION - * flag. - * @param maxsegments Pointer to a variable, taking the maximum segments. */ -bool address_segment_get_by_device( - uint32_t device_id, - unsigned *max_apdu, - BACNET_ADDRESS *src, - uint8_t *segmentation, - uint16_t *maxsegments) +bool address_get_by_device( + uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src) { struct Address_Cache_Entry *pMatch; bool found = false; /* return value */ @@ -375,20 +364,6 @@ bool address_segment_get_by_device( if (max_apdu) { *max_apdu = pMatch->max_apdu; } - if (segmentation) { -#if BACNET_SEGMENTATION_ENABLED - *segmentation = pMatch->segmentation; -#else - *segmentation = SEGMENTATION_NONE; -#endif - } - if (maxsegments) { -#if BACNET_SEGMENTATION_ENABLED - *maxsegments = pMatch->maxsegments; -#else - *maxsegments = 1; -#endif - } /* Prove we found it */ found = true; } @@ -400,19 +375,6 @@ bool address_segment_get_by_device( return found; } -/** - * @brief Return the cached addresBACNET_STACK_EXPORT -s for the given device-id - * @param device_id Device-Id - * @param max_apdu Pointer to a variable, taking the maximum APDU size. - * @param src Pointer to address structure for return. - */ -bool address_get_by_device( - uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src) -{ - return address_segment_get_by_device(device_id, max_apdu, src, NULL, NULL); -} - /** * Find a device id from a given MAC address. * diff --git a/src/bacnet/basic/binding/address.h b/src/bacnet/basic/binding/address.h index 692a1a9b..059dc1c4 100644 --- a/src/bacnet/basic/binding/address.h +++ b/src/bacnet/basic/binding/address.h @@ -43,14 +43,6 @@ BACNET_STACK_EXPORT bool address_get_by_device( uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src); -BACNET_STACK_EXPORT -bool address_segment_get_by_device( - uint32_t device_id, - unsigned *max_apdu, - BACNET_ADDRESS *src, - uint8_t *segmentation, - uint16_t *maxsegments); - BACNET_STACK_EXPORT bool address_get_by_index( unsigned index, diff --git a/src/bacnet/basic/object/device.c b/src/bacnet/basic/object/device.c index e9719fb1..e044b320 100644 --- a/src/bacnet/basic/object/device.c +++ b/src/bacnet/basic/object/device.c @@ -1145,11 +1145,7 @@ uint8_t Device_Protocol_Revision(void) BACNET_SEGMENTATION Device_Segmentation_Supported(void) { -#if BACNET_SEGMENTATION_ENABLED - return SEGMENTATION_BOTH; -#else return SEGMENTATION_NONE; -#endif } /** @@ -1620,12 +1616,8 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata) rpdata->object_instance, rpdata->array_index, Device_Object_List_Element_Encode, count, apdu, apdu_max); if (apdu_len == BACNET_STATUS_ABORT) { -#if BACNET_SEGMENTATION_ENABLED - rpdata->error_code = ERROR_CODE_ABORT_BUFFER_OVERFLOW; -#else rpdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; -#endif } else if (apdu_len == BACNET_STATUS_ERROR) { rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; diff --git a/src/bacnet/basic/service/h_apdu.c b/src/bacnet/basic/service/h_apdu.c index 2bfef566..1e0d0325 100644 --- a/src/bacnet/basic/service/h_apdu.c +++ b/src/bacnet/basic/service/h_apdu.c @@ -26,10 +26,6 @@ static uint16_t Timeout_Milliseconds = 3000; /* Number of APDU Retries */ static uint8_t Number_Of_Retries = 3; static uint8_t Local_Network_Priority; /* Fixing test 10.1.2 Network priority */ -#if BACNET_SEGMENTATION_ENABLED -/* APDU Segment Timeout in Milliseconds */ -static uint16_t Segment_Timeout_Milliseconds = 5000; -#endif /* a simple table for crossing the services supported */ static BACNET_SERVICES_SUPPORTED @@ -556,109 +552,6 @@ static bool apdu_unconfirmed_dcc_disabled(uint8_t service_choice) return status; } -/* Invoke special handler for confirmed service */ -static void invoke_confirmed_service_service_request( - BACNET_ADDRESS *src, - BACNET_CONFIRMED_SERVICE_DATA *service_data, - uint8_t service_choice, - uint8_t *service_request, - uint32_t service_request_len) -{ - if (apdu_confirmed_dcc_disabled(service_choice)) { - /* When network communications are completely disabled, - only DeviceCommunicationControl and ReinitializeDevice - APDUs shall be processed and no messages shall be - initiated. */ - return; - } - if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) && - (Confirmed_Function[service_choice])) { - Confirmed_Function[service_choice]( - service_request, service_request_len, src, service_data); - } else if (Unrecognized_Service_Handler) { - Unrecognized_Service_Handler( - service_request, service_request_len, src, service_data); - } -} - -#if BACNET_SEGMENTATION_ENABLED -/** Handler for messages with segmentation : - - store new packet if sequence number is ok - - NACK packet if sequence number is not ok - - call the final functions with reassembled data when last packet ok is - received -*/ -static void apdu_handler_confirmed_service_segment( - BACNET_ADDRESS *src, - uint8_t *apdu, /* APDU data */ - uint32_t apdu_len) -{ - BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; - uint8_t internal_service_id = 0; - uint8_t service_choice = 0; - uint8_t *service_request = NULL; - uint16_t service_request_len = 0; - uint32_t len = 0; /* counts where we are in PDU */ - bool segment_ok; - - len = apdu_decode_confirmed_service_request( - &apdu[0], /* APDU data */ - apdu_len, &service_data, &service_choice, &service_request, - &service_request_len); - if (len == 0) { - /* service data unable to be decoded - simply drop */ - return; - } - /* new segment : memorize it */ - segment_ok = tsm_set_segmented_confirmed_service_received( - src, &service_data, &internal_service_id, &service_request, - &service_request_len); - /* last segment */ - if (segment_ok && !service_data.more_follows) { - /* Clear peer information */ - tsm_clear_peer_id(internal_service_id); - /* Invoke service handler */ - invoke_confirmed_service_service_request( - src, &service_data, service_choice, service_request, - service_request_len); - /* We must free invoke_id, and associated data */ - tsm_free_invoke_id_check(internal_service_id, NULL, true); - } -} -#endif - -/* Handler for normal message without segmentation, or segmented complete - * message reassembled all-in-one */ -static void apdu_handler_confirmed_service( - BACNET_ADDRESS *src, - uint8_t *apdu, /* APDU data */ - uint32_t apdu_len) -{ - BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; - uint8_t service_choice = 0; - uint8_t *service_request = NULL; - uint16_t service_request_len = 0; - uint32_t len = 0; /* counts where we are in PDU */ - - len = apdu_decode_confirmed_service_request( - &apdu[0], /* APDU data */ - apdu_len, &service_data, &service_choice, &service_request, - &service_request_len); - if (len == 0) { - /* service data unable to be decoded - simply drop */ - return; - } -#if BACNET_SEGMENTATION_ENABLED - /* Check for unexpected request is received in active TSM state */ - if (tsm_is_invalid_apdu_in_this_state(src, &service_data)) { - return; - } -#endif - invoke_confirmed_service_service_request( - src, &service_data, service_choice, service_request, - service_request_len); -} - /** Process the APDU header and invoke the appropriate service handler * to manage the received request. * Almost all requests and ACKs invoke this function. @@ -674,15 +567,11 @@ void apdu_handler( uint16_t apdu_len) { BACNET_PDU_TYPE pdu_type; + BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; uint8_t service_choice = 0; uint8_t *service_request = NULL; uint16_t service_request_len = 0; int len = 0; /* counts where we are in PDU */ -#if BACNET_SEGMENTATION_ENABLED - uint8_t sequence_number = 0; - uint8_t actual_window_size = 0; - bool nak = false; -#endif #if !BACNET_SVC_SERVER uint8_t invoke_id = 0; BACNET_CONFIRMED_SERVICE_ACK_DATA service_ack_data = { 0 }; @@ -701,14 +590,27 @@ void apdu_handler( pdu_type = apdu[0] & 0xF0; switch (pdu_type) { case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: - /* segmented_message_reception ? */ -#if BACNET_SEGMENTATION_ENABLED - if (apdu[0] & BIT(3)) { - apdu_handler_confirmed_service_segment(src, apdu, apdu_len); - } else -#endif - { - apdu_handler_confirmed_service(src, apdu, apdu_len); + len = apdu_decode_confirmed_service_request( + apdu, apdu_len, &service_data, &service_choice, + &service_request, &service_request_len); + if (len == 0) { + /* service data unable to be decoded - simply drop */ + break; + } + if (apdu_confirmed_dcc_disabled(service_choice)) { + /* When network communications are completely disabled, + only DeviceCommunicationControl and ReinitializeDevice + APDUs shall be processed and no messages shall be + initiated. */ + break; + } + if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) && + (Confirmed_Function[service_choice])) { + Confirmed_Function[service_choice]( + service_request, service_request_len, src, &service_data); + } else if (Unrecognized_Service_Handler) { + Unrecognized_Service_Handler( + service_request, service_request_len, src, &service_data); } break; case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: @@ -783,24 +685,9 @@ void apdu_handler( } break; case PDU_TYPE_SEGMENT_ACK: -#if !BACNET_SEGMENTATION_ENABLED /* FIXME: what about a denial of service attack here? we could check src to see if that matched the tsm */ tsm_free_invoke_id(invoke_id); -#else - if (apdu_len < 4) { - break; - } - server = apdu[0] & 0x01; - nak = apdu[0] & 0x02; - invoke_id = apdu[1]; - sequence_number = apdu[2]; - actual_window_size = apdu[3]; - /* we care because we support segmented message sending */ - tsm_segmentack_received( - invoke_id, sequence_number, actual_window_size, nak, server, - src); -#endif break; case PDU_TYPE_ERROR: if (apdu_len < 3) { @@ -827,12 +714,7 @@ void apdu_handler( (BACNET_ERROR_CODE)error_code); } } -#if BACNET_SEGMENTATION_ENABLED - /*Release the data*/ - tsm_free_invoke_id_segmentation(src, invoke_id); -#else tsm_free_invoke_id(invoke_id); -#endif break; case PDU_TYPE_REJECT: if (apdu_len < 3) { @@ -843,12 +725,7 @@ void apdu_handler( if (Reject_Function) { Reject_Function(src, invoke_id, reason); } -#if BACNET_SEGMENTATION_ENABLED - /*Release the data*/ - tsm_free_invoke_id_segmentation(src, invoke_id); -#else tsm_free_invoke_id(invoke_id); -#endif break; case PDU_TYPE_ABORT: if (apdu_len < 3) { @@ -857,156 +734,13 @@ void apdu_handler( server = apdu[0] & 0x01; invoke_id = apdu[1]; reason = apdu[2]; - if (!server) { - /*AbortPDU_Received*/ - if (Abort_Function) { - Abort_Function(src, invoke_id, reason, server); - } + if (Abort_Function) { + Abort_Function(src, invoke_id, reason, server); } -#if BACNET_SEGMENTATION_ENABLED - else { - /*SendAbort*/ - tsm_abort_pdu_send(invoke_id, src, reason, server); - } - /*Release the data*/ - tsm_free_invoke_id_segmentation(src, invoke_id); -#else tsm_free_invoke_id(invoke_id); -#endif break; #endif default: break; } } - -#if BACNET_SEGMENTATION_ENABLED -/*Return the APDU segment timeout*/ -uint16_t apdu_segment_timeout(void) -{ - return Segment_Timeout_Milliseconds; -} - -/*Set the APDU segment timeout*/ -void apdu_segment_timeout_set(uint16_t milliseconds) -{ - Segment_Timeout_Milliseconds = milliseconds; -} - -/** Process the APDU header and invoke the appropriate service handler - * to manage the received request. - * Almost all requests and ACKs invoke this function. - * @ingroup MISCHNDLR - * - * @param apdu [in] The apdu portion of the response, to be sent - * @param fixed_pdu_header [in] The apdu header for the response - * @return apdu_length[out] The length of the apdu header - */ -int apdu_encode_fixed_header( - uint8_t *apdu, BACNET_APDU_FIXED_HEADER *fixed_pdu_header) -{ - int apdu_len = 0; - switch (fixed_pdu_header->pdu_type) { - case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: - apdu[apdu_len++] = fixed_pdu_header->pdu_type - /* flag 'SA' if we accept many segments */ - | (fixed_pdu_header->service_data.request_data - .segmented_response_accepted - ? 0x02 - : 0x00) - /* flag 'MOR' if we more segments are coming */ - | (fixed_pdu_header->service_data.request_data.more_follows - ? 0x04 - : 0x00) - /* flag 'SEG' if we more segments are coming */ - | (fixed_pdu_header->service_data.request_data.segmented_message - ? 0x08 - : 0x00); - apdu[apdu_len++] = encode_max_segs_max_apdu( - fixed_pdu_header->service_data.request_data.max_segs, - fixed_pdu_header->service_data.request_data.max_resp); - apdu[apdu_len++] = - fixed_pdu_header->service_data.request_data.invoke_id; - /* extra data for segmented messages sending */ - if (fixed_pdu_header->service_data.request_data.segmented_message) { - /* packet sequence number */ - apdu[apdu_len++] = - fixed_pdu_header->service_data.request_data.sequence_number; - /* window size proposal */ - apdu[apdu_len++] = fixed_pdu_header->service_data.request_data - .proposed_window_number; - } - /* service choice */ - apdu[apdu_len++] = fixed_pdu_header->service_choice; - break; - case PDU_TYPE_COMPLEX_ACK: - apdu[apdu_len++] = fixed_pdu_header->pdu_type - /* flag 'MOR' if we more segments are coming */ - | (fixed_pdu_header->service_data.ack_data.more_follows ? 0x04 - : 0x00) - /* flag 'SEG' if we more segments are coming */ - | (fixed_pdu_header->service_data.ack_data.segmented_message - ? 0x08 - : 0x00); - apdu[apdu_len++] = - fixed_pdu_header->service_data.ack_data.invoke_id; - /* extra data for segmented messages sending */ - if (fixed_pdu_header->service_data.ack_data.segmented_message) { - /* packet sequence number */ - apdu[apdu_len++] = - fixed_pdu_header->service_data.ack_data.sequence_number; - /* window size proposal */ - apdu[apdu_len++] = fixed_pdu_header->service_data.ack_data - .proposed_window_number; - } - /* service choice */ - apdu[apdu_len++] = fixed_pdu_header->service_choice; - break; - default: - break; - } - return apdu_len; -} - -/** Handler to assign the header fields to the response - * The response can be segmented/unsegmented - * - * @param fixed_pdu_header [in] The apdu header of the response, to be sent. - * @param pdu_type [in] The pdu_type of the response. - * @param invoke_id [in] The invoike_id of the response - * @param service[in] The service choice for which the response has to be - * processed - * @param max_apdu[in] The maximum apdu length - */ -void apdu_init_fixed_header( - BACNET_APDU_FIXED_HEADER *fixed_pdu_header, - uint8_t pdu_type, - uint8_t invoke_id, - uint8_t service, - int max_apdu) -{ - fixed_pdu_header->pdu_type = pdu_type; - - fixed_pdu_header->service_data.common_data.invoke_id = invoke_id; - fixed_pdu_header->service_data.common_data.more_follows = false; - fixed_pdu_header->service_data.common_data.proposed_window_number = 0; - fixed_pdu_header->service_data.common_data.sequence_number = 0; - fixed_pdu_header->service_data.common_data.segmented_message = false; - switch (pdu_type) { - case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: - fixed_pdu_header->service_data.request_data.max_segs = - BACNET_MAX_SEGMENTS_ACCEPTED; - /* allow to specify a lower APDU size : support arbitrary reduction - * of APDU packets between peers */ - fixed_pdu_header->service_data.request_data.max_resp = - max_apdu < MAX_APDU ? max_apdu : MAX_APDU; - fixed_pdu_header->service_data.request_data - .segmented_response_accepted = BACNET_MAX_SEGMENTS_ACCEPTED > 1; - break; - case PDU_TYPE_COMPLEX_ACK: - default: - break; - } - fixed_pdu_header->service_choice = service; -} -#endif diff --git a/src/bacnet/basic/service/h_apdu.h b/src/bacnet/basic/service/h_apdu.h index 4f9db9ee..549942ca 100644 --- a/src/bacnet/basic/service/h_apdu.h +++ b/src/bacnet/basic/service/h_apdu.h @@ -162,24 +162,6 @@ void apdu_handler( uint8_t *apdu, /* APDU data */ uint16_t pdu_len); /* for confirmed messages */ -BACNET_STACK_EXPORT -uint16_t apdu_segment_timeout(void); - -BACNET_STACK_EXPORT -void apdu_segment_timeout_set(uint16_t milliseconds); - -BACNET_STACK_EXPORT -int apdu_encode_fixed_header( - uint8_t *apdu, BACNET_APDU_FIXED_HEADER *fixed_pdu_header); - -BACNET_STACK_EXPORT -void apdu_init_fixed_header( - BACNET_APDU_FIXED_HEADER *fixed_pdu_header, - uint8_t pdu_type, - uint8_t invoke_id, - uint8_t service, - int max_apdu); - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/bacnet/basic/service/h_dcc.c b/src/bacnet/basic/service/h_dcc.c index 8a65efff..bc0e0fe6 100644 --- a/src/bacnet/basic/service/h_dcc.c +++ b/src/bacnet/basic/service/h_dcc.c @@ -108,9 +108,7 @@ void handler_device_communication_control( debug_print("DeviceCommunicationControl: " "Missing Required Parameter. Sending Reject!\n"); goto DCC_FAILURE; - } -#if !BACNET_SEGMENTATION_ENABLED - else if (service_data->segmented_message) { + } else if (service_data->segmented_message) { len = abort_encode_apdu( &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); @@ -118,7 +116,6 @@ void handler_device_communication_control( "Sending Abort - segmented message.\n"); goto DCC_FAILURE; } -#endif /* decode the service request only */ len = dcc_decode_service_request( service_request, service_len, &timeDuration, &state, &password); diff --git a/src/bacnet/basic/service/h_rd.c b/src/bacnet/basic/service/h_rd.c index 5112bde6..334a0453 100644 --- a/src/bacnet/basic/service/h_rd.c +++ b/src/bacnet/basic/service/h_rd.c @@ -71,16 +71,13 @@ void handler_reinitialize_device( debug_print("ReinitializeDevice: Missing Required Parameter. " "Sending Reject!\n"); goto RD_ABORT; - } -#if !BACNET_SEGMENTATION_ENABLED - else if (service_data->segmented_message) { + } else if (service_data->segmented_message) { len = abort_encode_apdu( &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); debug_print("ReinitializeDevice: Sending Abort - segmented message.\n"); goto RD_ABORT; } -#endif /* decode the service request only */ len = rd_decode_service_request( service_request, service_len, &rd_data.state, &rd_data.password); diff --git a/src/bacnet/basic/service/h_rp.c b/src/bacnet/basic/service/h_rp.c index a7bc87ee..f4f17f73 100644 --- a/src/bacnet/basic/service/h_rp.c +++ b/src/bacnet/basic/service/h_rp.c @@ -30,12 +30,6 @@ #include "bacnet/basic/sys/debug.h" #include "bacnet/datalink/datalink.h" -#if BACNET_SEGMENTATION_ENABLED -#define BACNET_RP_BUFFER_OVERFLOW ERROR_CODE_ABORT_BUFFER_OVERFLOW -#else -#define BACNET_RP_BUFFER_OVERFLOW ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED -#endif - /** @file h_rp.c Handles Read Property requests. */ /** Handler for a ReadProperty Service request. @@ -44,7 +38,7 @@ * by a call to apdu_set_confirmed_handler(). * This handler builds a response packet, which is * - an Abort if - * - the message is segmented and segmentation is not supported + * - the message is segmented * - if decoding fails * - if the response would be too large * - the result from Device_Read_Property(), if it succeeds @@ -65,7 +59,6 @@ void handler_read_property( { BACNET_READ_PROPERTY_DATA rpdata; int len = 0; - int max_resp = 0; int pdu_len = 0; int apdu_len = -1; int npdu_len = -1; @@ -73,13 +66,9 @@ void handler_read_property( bool error = true; /* assume that there is an error */ int bytes_sent = 0; BACNET_ADDRESS my_address; -#if BACNET_SEGMENTATION_ENABLED - BACNET_APDU_FIXED_HEADER apdu_fixed_header; - int apdu_header_len = 3; -#endif /* configure default error code as an abort since it is common */ - rpdata.error_code = BACNET_RP_BUFFER_OVERFLOW; + rpdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; /* encode the NPDU portion of the packet */ datalink_get_my_address(&my_address); npdu_encode_npdu_data(&npdu_data, false, service_data->priority); @@ -93,12 +82,10 @@ void handler_read_property( len = BACNET_STATUS_REJECT; rpdata.error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; debug_print("RP: Missing Required Parameter. Sending Reject!\n"); -#if !BACNET_SEGMENTATION_ENABLED } else if (service_data->segmented_message) { /* we don't support segmentation - send an abort */ len = BACNET_STATUS_ABORT; debug_print("RP: Segmented message. Sending Abort!\n"); -#endif } else { len = rp_decode_service_request(service_request, service_len, &rpdata); if (len <= 0) { @@ -129,13 +116,6 @@ void handler_read_property( rpdata.object_instance = Network_Port_Index_To_Instance(0); } #endif - -#if BACNET_SEGMENTATION_ENABLED - apdu_init_fixed_header( - &apdu_fixed_header, PDU_TYPE_COMPLEX_ACK, - service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, - service_data->max_resp); -#endif apdu_len = rp_ack_encode_apdu_init( &Handler_Transmit_Buffer[npdu_len], service_data->invoke_id, &rpdata); @@ -154,25 +134,7 @@ void handler_read_property( len = rp_ack_encode_apdu_object_property_end( &Handler_Transmit_Buffer[npdu_len + apdu_len]); apdu_len += len; - /* pick the smaller response packet: ours or theirs */ - max_resp = min(service_data->max_resp, MAX_APDU); - if (apdu_len > max_resp) { -#if BACNET_SEGMENTATION_ENABLED - if (service_data->segmented_response_accepted) { - npdu_encode_npdu_data( - &npdu_data, true, MESSAGE_PRIORITY_NORMAL); - npdu_len = npdu_encode_pdu( - &Handler_Transmit_Buffer[0], src, &my_address, - &npdu_data); - - tsm_set_complexack_transaction( - src, &npdu_data, &apdu_fixed_header, service_data, - &Handler_Transmit_Buffer - [npdu_len + apdu_header_len], - (apdu_len - apdu_header_len)); - return; - } -#else + if (apdu_len > service_data->max_resp) { /* too big for the sender - send an abort! Setting of error code needed here as read property processing may have overridden the default set at start @@ -181,7 +143,6 @@ void handler_read_property( ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; len = BACNET_STATUS_ABORT; debug_print("RP: Message too large.\n"); -#endif } else { debug_print("RP: Sending Ack!\n"); error = false; diff --git a/src/bacnet/basic/service/h_rpm.c b/src/bacnet/basic/service/h_rpm.c index c9991f24..59d9d162 100644 --- a/src/bacnet/basic/service/h_rpm.c +++ b/src/bacnet/basic/service/h_rpm.c @@ -12,7 +12,6 @@ #include #include #include -#include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" /* BACnet Stack API */ @@ -34,17 +33,7 @@ #include "bacnet/basic/sys/debug.h" #include "bacnet/datalink/datalink.h" -/* Smaller single threaded implementations prefer a - single buffer for encoding each property from the RPM request. */ -#ifndef BACNET_RPM_PROPERTY_BUFFER_USE_CSTACK -static uint8_t RPM_Prop_Buffer[MAX_ASDU - MAX_NPDU] = { 0 }; -#endif - -#if BACNET_SEGMENTATION_ENABLED -#define BACNET_RPM_BUFFER_OVERFLOW ERROR_CODE_ABORT_BUFFER_OVERFLOW -#else -#define BACNET_RPM_BUFFER_OVERFLOW ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED -#endif +static uint8_t Temp_Buf[MAX_APDU] = { 0 }; /** * @brief Fetches the lists of properties (array of BACNET_PROPERTY_ID's) for @@ -135,15 +124,12 @@ static int RPM_Encode_Property( size_t copy_len = 0; int apdu_len = 0; BACNET_READ_PROPERTY_DATA rpdata; -#ifdef BACNET_RPM_PROPERTY_BUFFER_USE_CSTACK - uint8_t RPM_Prop_Buffer[MAX_ASDU - MAX_NPDU] = { 0 }; -#endif len = rpm_ack_encode_apdu_object_property( - &RPM_Prop_Buffer[0], rpmdata->object_property, rpmdata->array_index); - copy_len = memcopy(&apdu[0], &RPM_Prop_Buffer[0], offset, len, max_apdu); + &Temp_Buf[0], rpmdata->object_property, rpmdata->array_index); + copy_len = memcopy(&apdu[0], &Temp_Buf[0], offset, len, max_apdu); if (copy_len == 0) { - rpmdata->error_code = BACNET_RPM_BUFFER_OVERFLOW; + rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; return BACNET_STATUS_ABORT; } apdu_len += len; @@ -153,8 +139,8 @@ static int RPM_Encode_Property( rpdata.object_instance = rpmdata->object_instance; rpdata.object_property = rpmdata->object_property; rpdata.array_index = rpmdata->array_index; - rpdata.application_data = &RPM_Prop_Buffer[0]; - rpdata.application_data_len = sizeof(RPM_Prop_Buffer); + rpdata.application_data = &Temp_Buf[0]; + rpdata.application_data_len = sizeof(Temp_Buf); if ((rpmdata->object_property == PROP_ALL) || (rpmdata->object_property == PROP_REQUIRED) || @@ -175,21 +161,21 @@ static int RPM_Encode_Property( } /* error was returned - encode that for the response */ len = rpm_ack_encode_apdu_object_property_error( - &RPM_Prop_Buffer[0], rpdata.error_class, rpdata.error_code); - copy_len = memcopy( - &apdu[0], &RPM_Prop_Buffer[0], offset + apdu_len, len, max_apdu); + &Temp_Buf[0], rpdata.error_class, rpdata.error_code); + copy_len = + memcopy(&apdu[0], &Temp_Buf[0], offset + apdu_len, len, max_apdu); if (copy_len == 0) { - rpmdata->error_code = BACNET_RPM_BUFFER_OVERFLOW; + rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; return BACNET_STATUS_ABORT; } } else if ((offset + apdu_len + 1 + len + 1) < max_apdu) { /* enough room to fit the property value and tags */ len = rpm_ack_encode_apdu_object_property_value( - &apdu[offset + apdu_len], &RPM_Prop_Buffer[0], len); + &apdu[offset + apdu_len], &Temp_Buf[0], len); } else { /* not enough room - abort! */ - rpmdata->error_code = BACNET_RPM_BUFFER_OVERFLOW; + rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; return BACNET_STATUS_ABORT; } apdu_len += len; @@ -203,7 +189,7 @@ static int RPM_Encode_Property( * by a call to apdu_set_confirmed_handler(). * This handler builds a response packet, which is * - an Abort if - * - the message is segmented and SEGMENTATION_NONE + * - the message is segmented * - if decoding fails * - if the response would be too large * - the result from each included read request, if it succeeds @@ -234,14 +220,6 @@ void handler_read_property_multiple( int apdu_len = 0; int npdu_len = 0; int error = 0; - int max_resp = 0; -#if BACNET_SEGMENTATION_ENABLED - BACNET_APDU_FIXED_HEADER apdu_fixed_header; - int apdu_header_len = 3; -#endif -#ifdef BACNET_RPM_PROPERTY_BUFFER_USE_CSTACK - uint8_t RPM_Prop_Buffer[MAX_ASDU - MAX_NPDU] = { 0 }; -#endif if (service_data) { datalink_get_my_address(&my_address); @@ -252,12 +230,10 @@ void handler_read_property_multiple( rpmdata.error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER; error = BACNET_STATUS_REJECT; debug_print("RPM: Missing Required Parameter. Sending Reject!\n"); -#if !BACNET_SEGMENTATION_ENABLED } else if (service_data->segmented_message) { rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; debug_print("RPM: Segmented message. Sending Abort!\r\n"); -#endif } else { /* decode apdu request & encode apdu reply encode complex ack, invoke id, service choice */ @@ -303,14 +279,14 @@ void handler_read_property_multiple( } #endif /* Stick this object id into the reply - if it will fit */ - len = rpm_ack_encode_apdu_object_begin( - &RPM_Prop_Buffer[0], &rpmdata); + len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata); copy_len = memcopy( - &Handler_Transmit_Buffer[npdu_len], &RPM_Prop_Buffer[0], - apdu_len, len, sizeof(RPM_Prop_Buffer)); + &Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len, + len, MAX_APDU); if (copy_len == 0) { debug_print("RPM: Response too big!\n"); - rpmdata.error_code = BACNET_RPM_BUFFER_OVERFLOW; + rpmdata.error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; berror = true; break; @@ -345,9 +321,7 @@ void handler_read_property_multiple( rpmdata.object_type, rpmdata.object_instance)) { len = RPM_Encode_Property( &Handler_Transmit_Buffer[npdu_len], - (uint16_t)apdu_len, - sizeof(Handler_Transmit_Buffer) - npdu_len, - &rpmdata); + (uint16_t)apdu_len, MAX_APDU, &rpmdata); if (len > 0) { apdu_len += len; } else { @@ -362,18 +336,18 @@ void handler_read_property_multiple( /* No array index options for this special property. Encode error for this object property response */ len = rpm_ack_encode_apdu_object_property( - &RPM_Prop_Buffer[0], rpmdata.object_property, + &Temp_Buf[0], rpmdata.object_property, rpmdata.array_index); copy_len = memcopy( &Handler_Transmit_Buffer[npdu_len], - &RPM_Prop_Buffer[0], apdu_len, len, - sizeof(RPM_Prop_Buffer)); + &Temp_Buf[0], apdu_len, len, MAX_APDU); if (copy_len == 0) { debug_print( "RPM: Too full to encode property!\n"); - rpmdata.error_code = BACNET_RPM_BUFFER_OVERFLOW; + rpmdata.error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; /* The berror flag ensures that both loops will be broken! */ @@ -383,17 +357,17 @@ void handler_read_property_multiple( apdu_len += len; len = rpm_ack_encode_apdu_object_property_error( - &RPM_Prop_Buffer[0], ERROR_CLASS_PROPERTY, + &Temp_Buf[0], ERROR_CLASS_PROPERTY, ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY); copy_len = memcopy( &Handler_Transmit_Buffer[npdu_len], - &RPM_Prop_Buffer[0], apdu_len, len, - sizeof(RPM_Prop_Buffer)); + &Temp_Buf[0], apdu_len, len, MAX_APDU); if (copy_len == 0) { debug_print("RPM: Too full to encode error!\n"); - rpmdata.error_code = BACNET_RPM_BUFFER_OVERFLOW; + rpmdata.error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; /* The berror flag ensures that both loops will be broken! */ @@ -422,10 +396,7 @@ void handler_read_property_multiple( rpmdata.object_instance)) { len = RPM_Encode_Property( &Handler_Transmit_Buffer[npdu_len], - (uint16_t)apdu_len, - sizeof(Handler_Transmit_Buffer) - - npdu_len, - &rpmdata); + (uint16_t)apdu_len, MAX_APDU, &rpmdata); if (len > 0) { apdu_len += len; } else { @@ -447,10 +418,7 @@ void handler_read_property_multiple( special_object_property, index); len = RPM_Encode_Property( &Handler_Transmit_Buffer[npdu_len], - (uint16_t)apdu_len, - sizeof(Handler_Transmit_Buffer) - - npdu_len, - &rpmdata); + (uint16_t)apdu_len, MAX_APDU, &rpmdata); if (len > 0) { apdu_len += len; } else { @@ -469,9 +437,7 @@ void handler_read_property_multiple( /* handle an individual property */ len = RPM_Encode_Property( &Handler_Transmit_Buffer[npdu_len], - (uint16_t)apdu_len, - sizeof(Handler_Transmit_Buffer) - npdu_len, - &rpmdata); + (uint16_t)apdu_len, MAX_APDU, &rpmdata); if (len > 0) { apdu_len += len; } else { @@ -490,16 +456,15 @@ void handler_read_property_multiple( /* Reached end of property list so cap the result list */ decode_len++; - len = - rpm_ack_encode_apdu_object_end(&RPM_Prop_Buffer[0]); + len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]); copy_len = memcopy( - &Handler_Transmit_Buffer[npdu_len], - &RPM_Prop_Buffer[0], apdu_len, len, - sizeof(Handler_Transmit_Buffer) - npdu_len); + &Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], + apdu_len, len, MAX_APDU); if (copy_len == 0) { debug_print( "RPM: Too full to encode object end!\n"); - rpmdata.error_code = BACNET_RPM_BUFFER_OVERFLOW; + rpmdata.error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; /* The berror flag ensures that both loops will be broken! */ @@ -522,36 +487,12 @@ void handler_read_property_multiple( } /* If not having an error so far, check the remaining space. */ if (!berror) { - max_resp = min(service_data->max_resp, MAX_APDU); - if (apdu_len > max_resp) { -#if BACNET_SEGMENTATION_ENABLED - if (service_data->segmented_response_accepted) { - apdu_init_fixed_header( - &apdu_fixed_header, PDU_TYPE_COMPLEX_ACK, - service_data->invoke_id, - SERVICE_CONFIRMED_READ_PROP_MULTIPLE, - service_data->max_resp); - - npdu_encode_npdu_data( - &npdu_data, true, MESSAGE_PRIORITY_NORMAL); - npdu_len = npdu_encode_pdu( - &Handler_Transmit_Buffer[0], src, &my_address, - &npdu_data); - - tsm_set_complexack_transaction( - src, &npdu_data, &apdu_fixed_header, service_data, - &Handler_Transmit_Buffer - [npdu_len + apdu_header_len], - (apdu_len - apdu_header_len)); - return; - } -#else + if (apdu_len > service_data->max_resp) { /* too big for the sender - send an abort */ rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; error = BACNET_STATUS_ABORT; debug_print("RPM: Message too large. Sending Abort!\n"); -#endif } } } diff --git a/src/bacnet/basic/service/h_wp.c b/src/bacnet/basic/service/h_wp.c index c0cc446d..ca3b097e 100644 --- a/src/bacnet/basic/service/h_wp.c +++ b/src/bacnet/basic/service/h_wp.c @@ -107,16 +107,13 @@ void handler_write_property( REJECT_REASON_MISSING_REQUIRED_PARAMETER); debug_print("WP: Missing Required Parameter. Sending Reject!\n"); bcontinue = false; - } -#if !BACNET_SEGMENTATION_ENABLED - else if (service_data->segmented_message) { + } else if (service_data->segmented_message) { len = abort_encode_apdu( &Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); debug_print("WP: Segmented message. Sending Abort!\n"); bcontinue = false; } -#endif if (bcontinue) { /* decode the service request only */ len = wp_decode_service_request(service_request, service_len, &wp_data); diff --git a/src/bacnet/basic/service/h_wpm.c b/src/bacnet/basic/service/h_wpm.c index d0bdd8d1..c40ac968 100644 --- a/src/bacnet/basic/service/h_wpm.c +++ b/src/bacnet/basic/service/h_wpm.c @@ -141,13 +141,11 @@ void handler_write_property_multiple( len = BACNET_STATUS_REJECT; debug_print("WPM: Missing Required Parameter. " "Sending Reject!\n"); -#if !BACNET_SEGMENTATION_ENABLED } else if (service_data->segmented_message) { wp_data.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; len = BACNET_STATUS_ABORT; debug_print("WPM: Segmented message. " "Sending Abort!\n"); -#endif } else { /* first time - detect malformed request before writing any data */ len = write_property_multiple_decode( diff --git a/src/bacnet/basic/service/s_iam.c b/src/bacnet/basic/service/s_iam.c index 76a7cb6b..6b7399b8 100644 --- a/src/bacnet/basic/service/s_iam.c +++ b/src/bacnet/basic/service/s_iam.c @@ -85,7 +85,7 @@ int iam_encode_pdu( /* encode the APDU portion of the packet */ len = iam_encode_apdu( &buffer[pdu_len], Device_Object_Instance_Number(), MAX_APDU, - Device_Segmentation_Supported(), Device_Vendor_Identifier()); + SEGMENTATION_NONE, Device_Vendor_Identifier()); pdu_len += len; return pdu_len; @@ -158,7 +158,7 @@ int iam_unicast_encode_pdu( /* encode the APDU portion of the packet */ apdu_len = iam_encode_apdu( &buffer[npdu_len], Device_Object_Instance_Number(), MAX_APDU, - Device_Segmentation_Supported(), Device_Vendor_Identifier()); + SEGMENTATION_NONE, Device_Vendor_Identifier()); pdu_len = npdu_len + apdu_len; return pdu_len; diff --git a/src/bacnet/basic/tsm/tsm.c b/src/bacnet/basic/tsm/tsm.c index 85d0e5ff..43b3b85c 100644 --- a/src/bacnet/basic/tsm/tsm.c +++ b/src/bacnet/basic/tsm/tsm.c @@ -20,32 +20,10 @@ #include "bacnet/datalink/datalink.h" #include "bacnet/basic/services.h" #include "bacnet/basic/binding/address.h" -#if BACNET_SEGMENTATION_ENABLED -#include -#include "bacnet/segmentack.h" -#include "bacnet/abort.h" -#include "bacnet/basic/sys/platform.h" - -#ifndef BACNET_SEGMENTATION_WINDOW_SIZE_DEFAULT -#define BACNET_SEGMENTATION_WINDOW_SIZE_DEFAULT 32 -#endif - -/* for confirmed segmented messages, this is the number of peer - segmented requests we can stand at the same time. */ -#if !defined(BACNET_SEGMENTATION_TSM_PEERS_MAX) -#define BACNET_SEGMENTATION_TSM_PEERS_MAX 16 -#endif - -/*Number of Duplicate Segments Received. */ -static uint8_t Duplicate_Count = 0; - -/* Indirection of state machine data with peer unique id values */ -static BACNET_TSM_INDIRECT_DATA TSM_Peer_Ids[BACNET_SEGMENTATION_TSM_PEERS_MAX]; -#endif /** @file tsm.c BACnet Transaction State Machine operations */ /* FIXME: modify basic service handlers to use TSM rather than this buffer! */ -uint8_t Handler_Transmit_Buffer[MAX_ASDU]; +uint8_t Handler_Transmit_Buffer[MAX_PDU]; #if (MAX_TSM_TRANSACTIONS) /* Really only needed for segmented messages */ @@ -222,30 +200,6 @@ uint8_t tsm_next_free_invokeID(void) return invokeID; } -/** - * @brief Copy new data to current APDU sending blob data - * @param data [in] The TSM data - * @param bdata [in] The data to copy - * @param data_len [in] The length of the data - */ -static void tsm_blob_data_copy( - BACNET_TSM_DATA *data, const uint8_t *bdata, uint32_t data_len) -{ -#if BACNET_SEGMENTATION_ENABLED - if (data->apdu) { - free(data->apdu); - } - data->apdu = NULL; - data->apdu = calloc(1, data_len); - if (!data->apdu) { - DEBUG_FPRINTF(stderr, "TSM: failed to allocate %d bytes\n", data_len); - return; - } -#endif - memcpy(data->apdu, bdata, data_len); - data->apdu_len = data_len; -} - /** Set for an unsegmented transaction * the state to await confirmation. * @@ -262,6 +216,7 @@ void tsm_set_confirmed_unsegmented_transaction( const uint8_t *apdu, uint16_t apdu_len) { + uint16_t j = 0; uint8_t index; BACNET_TSM_DATA *plist; @@ -275,7 +230,10 @@ void tsm_set_confirmed_unsegmented_transaction( /* start the timer */ plist->RequestTimer = apdu_timeout(); /* copy the data */ - tsm_blob_data_copy(plist, apdu, apdu_len); + for (j = 0; j < apdu_len; j++) { + plist->apdu[j] = apdu[j]; + } + plist->apdu_len = apdu_len; npdu_copy_data(&plist->npdu_data, ndpu_data); bacnet_address_copy(&plist->dest, dest); } @@ -312,13 +270,13 @@ bool tsm_get_transaction_pdu( index = tsm_find_invokeID_index(invokeID); /* how much checking is needed? state? dest match? just invokeID? */ if (index < MAX_TSM_TRANSACTIONS) { - /* FIXME: should we free the transaction so it doesn't timeout? */ + /* FIXME: we may want to free the transaction so it doesn't timeout + */ /* retrieve the transaction */ plist = &TSM_List[index]; - if (plist->apdu_len > min(MAX_ASDU, UINT16_MAX)) { - *apdu_len = min(MAX_ASDU, UINT16_MAX); - } else { - *apdu_len = (uint16_t)plist->apdu_len; + *apdu_len = (uint16_t)plist->apdu_len; + if (*apdu_len > MAX_PDU) { + *apdu_len = MAX_PDU; } for (j = 0; j < *apdu_len; j++) { apdu[j] = plist->apdu[j]; @@ -332,6 +290,57 @@ bool tsm_get_transaction_pdu( return found; } +/** Called once a millisecond or slower. + * This function calls the handler for a + * timeout 'Timeout_Function', if necessary. + * + * @param milliseconds - Count of milliseconds passed, since the last call. + */ +void tsm_timer_milliseconds(uint16_t milliseconds) +{ + unsigned i = 0; /* counter */ + int bytes_sent = 0; + + BACNET_TSM_DATA *plist = &TSM_List[0]; + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++, plist++) { + if (plist->state == TSM_STATE_AWAIT_CONFIRMATION) { + if (plist->RequestTimer > milliseconds) { + plist->RequestTimer -= milliseconds; + } else { + plist->RequestTimer = 0; + } + /* AWAIT_CONFIRMATION */ + if (plist->RequestTimer == 0) { + if (plist->RetryCount < apdu_retries()) { + plist->RequestTimer = apdu_timeout(); + plist->RetryCount++; + bytes_sent = datalink_send_pdu( + &plist->dest, &plist->npdu_data, &plist->apdu[0], + plist->apdu_len); + DEBUG_PRINTF( + "invoke-id[%u] Retry %u of %u after %ums\n", + plist->InvokeID, plist->RetryCount, apdu_retries(), + plist->RequestTimer); + if (bytes_sent <= 0) { + debug_perror("invoke-id[%u] Failed to Send Retry"); + } + } else { + /* note: the invoke id has not been cleared yet + and this indicates a failed message: + IDLE and a valid invoke id */ + plist->state = TSM_STATE_IDLE; + if (plist->InvokeID != 0) { + if (Timeout_Function) { + Timeout_Function(plist->InvokeID); + } + } + } + } + } + } +} + /** Frees the invokeID and sets its state to IDLE * * @param invokeID Invoke-ID @@ -367,8 +376,7 @@ bool tsm_invoke_id_free(uint8_t invokeID) return status; } -/** - * @brief Check if a confirmation failed to arrive for the message associated +/** See if we failed get a confirmation for the message associated * with this invoke ID. * @param invokeID [in] The invokeID to be checked, normally of last message * sent. @@ -391,1192 +399,4 @@ bool tsm_invoke_id_failed(uint8_t invokeID) return status; } - -#if BACNET_SEGMENTATION_ENABLED -/** - * @brief Send a Segment Ack PDU - * @param dest [in] The destination address - * @param negativeack [in] true if negative ack, false if positive ack - * @param server [in] true if server, false if client - * @param invoke_id [in] The invoke ID - * @param sequence_number [in] The sequence number - * @param actual_window_size [in] The actual window size - */ -static void tsm_segmentack_pdu_send( - BACNET_ADDRESS *dest, - bool negativeack, - bool server, - uint8_t invoke_id, - uint8_t sequence_number, - uint8_t actual_window_size) -{ - int pdu_len = 0; - BACNET_NPDU_DATA npdu_data; - int bytes_sent; - BACNET_ADDRESS my_address; - int apdu_len = 0; - int npdu_len = 0; - uint8_t Transmit_Buffer[MAX_PDU] = { 0 }; - datalink_get_my_address(&my_address); - npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); - npdu_len = - npdu_encode_pdu(&Transmit_Buffer[0], dest, &my_address, &npdu_data); - apdu_len = segmentack_encode_apdu( - &Transmit_Buffer[npdu_len], negativeack, server, invoke_id, - sequence_number, actual_window_size); - pdu_len = apdu_len + npdu_len; - bytes_sent = - datalink_send_pdu(dest, &npdu_data, &Transmit_Buffer[0], pdu_len); - DEBUG_FPRINTF(stderr, "bytes sent=%d\n", bytes_sent); -} - -/** - * @brief determine the theoretical size of an apdu fixed header - * @param header [in] The fixed header - * @param segmented [in] true if segmented, false if unsegmented - * @return the estimated size of the header - */ -static uint32_t -tsm_apdu_header_typical_size(BACNET_APDU_FIXED_HEADER *header, bool segmented) -{ - int segmented_ack = 5; - int unsegmented_ack = 3; - int segmented_request = 6; - int unsegmented_request = 4; - switch (header->pdu_type) { - case PDU_TYPE_COMPLEX_ACK: - return segmented ? segmented_ack : unsegmented_ack; - case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: - return segmented ? segmented_request : unsegmented_request; - default: - break; - } - return unsegmented_ack; -} - -/** - * @brief Free the allocated blob data - * @param data [in] The TSM data - */ -static void tsm_blob_free(BACNET_TSM_DATA *data) -{ - /* Free received data blobs */ - if (data->apdu_blob) { - free(data->apdu_blob); - } - data->apdu_blob = NULL; - data->apdu_blob_allocated = 0; - data->apdu_blob_size = 0; - /* Free sent data blobs */ - if (data->apdu) { - free(data->apdu); - } - data->apdu = NULL; - data->apdu_len = 0; -} - -/** - * @param Reset blob current size, but keep allocated data - * @param data [in] The TSM data - */ -static void tsm_blob_reset(BACNET_TSM_DATA *data) -{ - data->apdu_blob_size = 0; -} - -/** - * @brief Pad the data blob by allocating new data if necessary - * while keeping existing bytes. - * @param data [in] The TSM data - * @param allocation_unit [in] The size of the data to allocate - */ -static void tsm_blob_pad(BACNET_TSM_DATA *data, uint32_t allocation_unit) -{ - if (!allocation_unit) { /* NOP */ - return; - } - /* allocation needed ? */ - if ((!data->apdu_blob) || (!data->apdu_blob_allocated) || - ((allocation_unit + data->apdu_blob_size) > - data->apdu_blob_allocated)) { - /* stupid idiot allocation algorithm : space allocated shall augment - * exponentially */ - /* (nb: here there may be extra space remaining) */ - uint8_t *apdu_new_blob = - calloc(1, data->apdu_blob_allocated + allocation_unit); - if (!apdu_new_blob) { - DEBUG_FPRINTF( - stderr, "TSM: failed to allocate %d bytes\n", - data->apdu_blob_allocated + allocation_unit); - return; - } - /* recopy old data */ - if (data->apdu_blob_size) { - memcpy(apdu_new_blob, data->apdu_blob, data->apdu_blob_size); - } - /* new values */ - if (data->apdu_blob) { - free(data->apdu_blob); - } - data->apdu_blob = apdu_new_blob; - data->apdu_blob_allocated = data->apdu_blob_allocated + allocation_unit; - } -} - -/** - * Add new data to current blob (allocate extra space if necessary) - * @param data [in] The TSM data - * @param bdata [in] The data to add - * @param data_len [in] The length of the data - */ -static void -tsm_blob_data_add(BACNET_TSM_DATA *data, uint8_t *bdata, uint32_t data_len) -{ - tsm_blob_pad(data, data_len); - memcpy(&data->apdu_blob[data->apdu_blob_size], bdata, data_len); - data->apdu_blob_size += data_len; -} - -/** - * @brief Get the current blob data - * @param data [in] The TSM data - * @param data_len [out] The length of the data - * @return Pointer to the blob data - */ -static uint8_t *tsm_blob_data_get(BACNET_TSM_DATA *data, uint16_t *data_len) -{ - *data_len = data->apdu_blob_size; - return data->apdu_blob; -} - -/** - * @brief Get the Nth packet data to send in a segmented operation, - * or get the only data packet in unsegmented world. - * @param data [in] The TSM data - * @param segment_number [in] The segment number to get - * @param data_len [out] The length of the data - * @return Pointer to the data segment - */ -static uint8_t *tsm_blob_data_segment_get( - BACNET_TSM_DATA *data, int segment_number, uint32_t *data_len) -{ - /* Data is split in N blocks of, at maximum, ( APDU_MAX - APDU_HEADER ) - * bytes */ - bool segmented = - data->apdu_fixed_header.service_data.common_data.segmented_message; - int header_size = - tsm_apdu_header_typical_size(&data->apdu_fixed_header, segmented); - int block_request_size = data->apdu_maximum_length - header_size; - int data_position = segment_number * block_request_size; - int remaining_size = (int)data->apdu_len - data_position; - *data_len = (uint32_t)max(0, min(remaining_size, block_request_size)); - return data->apdu + data_position; -} - -/** - * @brief Clear TSM Peer data - * @param InternalInvokeID [in] The internal invoke ID - */ -void tsm_clear_peer_id(uint8_t InternalInvokeID) -{ - int ix; - - /* look for a matching internal invoke ID */ - for (ix = 0; ix < BACNET_SEGMENTATION_TSM_PEERS_MAX; ix++) { - /* see if it matches the internal number */ - if (TSM_Peer_Ids[ix].InternalInvokeID == InternalInvokeID) { - TSM_Peer_Ids[ix].InternalInvokeID = 0; - } - } -} - -/** - * @brief frees the invokeID and sets its state to IDLE - * @param invokeID [in] The invokeID to be checked, normally of last message - * sent. - * @param peer_address [in] The peer address to check against - * @param cleanup [in] If true, free the blob data - */ -void tsm_free_invoke_id_check( - uint8_t invokeID, BACNET_ADDRESS *peer_address, bool cleanup) -{ - uint8_t index; - - index = tsm_find_invokeID_index(invokeID); - - if ((index < MAX_TSM_TRANSACTIONS) && - (!peer_address || address_match(peer_address, &TSM_List[index].dest))) { - /* check "double-free" cases */ - TSM_List[index].state = TSM_STATE_IDLE; - /* Clear Peer data, if any. Lookup with our internal ID status. */ - tsm_clear_peer_id(invokeID); - /* flag slot as "unused" */ - TSM_List[index].InvokeID = 0; - - if (cleanup) { - /* Release segmented data */ - tsm_blob_free(&TSM_List[index]); - } - } -} - -/** - * @brief Finds (optionally creates) an existing peer data - * @param src [in] The source address - * @param invokeID [in] The invoke ID - * @param createPeerId [in] Create a new peer ID if not found - * @return Pointer to the peer data or NULL if not found - */ -static BACNET_TSM_INDIRECT_DATA * -tsm_get_peer_id_data(BACNET_ADDRESS *src, uint8_t invokeID, bool createPeerId) -{ - int ix; - int index; - int free_ix_found = -1; - BACNET_TSM_INDIRECT_DATA *item = NULL; - - /* look for an empty slot, or a matching (address,peer invoke ID) */ - for (ix = 0; ix < BACNET_SEGMENTATION_TSM_PEERS_MAX && !item; ix++) { - /* not free : see if it matches */ - if (TSM_Peer_Ids[ix].InternalInvokeID != 0) { - if (invokeID == TSM_Peer_Ids[ix].PeerInvokeID && - address_match(src, &TSM_Peer_Ids[ix].PeerAddress)) { - item = &TSM_Peer_Ids[ix]; - } - } else if (free_ix_found < 0) { - /* mark free slot found */ - free_ix_found = ix; - } - } - - /* create new data */ - if ((!item) && createPeerId && (free_ix_found > -1)) { - /* memorize peer data */ - TSM_Peer_Ids[free_ix_found].PeerInvokeID = invokeID; - TSM_Peer_Ids[free_ix_found].PeerAddress = *src; - /* create an internal TSM slot (with internal invokeID number which is - * not relevant) */ - TSM_Peer_Ids[free_ix_found].InternalInvokeID = tsm_next_free_invokeID(); - index = tsm_find_invokeID_index( - TSM_Peer_Ids[free_ix_found].InternalInvokeID); - if (index < MAX_TSM_TRANSACTIONS) { - /* explicitly memorize peer InvokeID */ - TSM_List[index].InvokeID = - TSM_Peer_Ids[free_ix_found].InternalInvokeID; - TSM_List[index].dest = *src; - item = &TSM_Peer_Ids[free_ix_found]; - } else { - /* problem : reset slot (NULL returned) */ - TSM_Peer_Ids[free_ix_found].InternalInvokeID = 0; - } - } - - return item; -} - -/** - * @brief Associates a Peer address and invoke ID with our TSM - * @param src [in] The source address - * @param invokeID [in] The invoke ID - * @return A local InvokeID unique number, 0 in case of error. - */ -uint8_t tsm_get_peer_id(BACNET_ADDRESS *src, uint8_t invokeID) -{ - BACNET_TSM_INDIRECT_DATA *peer_data; - peer_data = tsm_get_peer_id_data(src, invokeID, true); - if (peer_data) { - return peer_data->InternalInvokeID; - } - return 0; -} - -/** - * @brief Check if the segment is a duplicate - * @param tsm_data [in] The TSM data - * @param seqA [in] The sequence number of the segment - * @param first_sequence_number [in] The first sequence number in the window - * @param last_sequence_number [in] The last sequence number in the window - * @return true if the segment is a duplicate - */ -static bool DuplicateInWindow( - BACNET_TSM_DATA *tsm_data, - uint8_t seqA, - uint32_t first_sequence_number, - uint32_t last_sequence_number) -{ - uint8_t received_count = - (last_sequence_number - first_sequence_number) % 256; - if (received_count > tsm_data->ActualWindowSize) { - return false; - } else if ((seqA - first_sequence_number) % 256 <= received_count) { - return true; - } else if ( - (received_count == 0) && - ((first_sequence_number - seqA) % 256 <= tsm_data->ActualWindowSize)) { - return true; - } else { - return false; - } -} - -/** - * @brief Check if the segment is a duplicate - * @param index [in] The index of the TSM - * @param service_data [in] The service data - * @param src [in] The source address - * @return true if the segment is a duplicate - */ -static bool tsm_duplicate_segment_received( - uint8_t index, - BACNET_CONFIRMED_SERVICE_DATA *service_data, - BACNET_ADDRESS *src) -{ - BACNET_NPDU_DATA npdu_data; - bool isDuplicate = false; - uint8_t Ndup = TSM_List[index].ActualWindowSize; - - if (Duplicate_Count < Ndup) { - /* DuplicateSegmentReceived */ - TSM_List[index].SegmentTimer = apdu_segment_timeout(); - Duplicate_Count++; - isDuplicate = true; - } else if (Duplicate_Count == Ndup) { - /* TooManyDuplicateSegmentsReceived */ - npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); - tsm_segmentack_pdu_send( - src, true, true, service_data->invoke_id, - TSM_List[index].LastSequenceNumber, - TSM_List[index].ActualWindowSize); - TSM_List[index].SegmentTimer = apdu_segment_timeout(); - TSM_List[index].InitialSequenceNumber = - TSM_List[index].LastSequenceNumber; - Duplicate_Count = 0; - isDuplicate = true; - } - - return isDuplicate; -} - -/** - * @brief send an Abort-PDU message because of incorrect segment/PDU received - * @param invoke_id [in] The invokeID to be checked, normally of last message - * sent. - * @param dest [in] The destination address - * @param reason [in] The reason for the abort - * @param server [in] true if the server, false if the client - */ -void tsm_abort_pdu_send( - uint8_t invoke_id, BACNET_ADDRESS *dest, uint8_t reason, bool server) -{ - int pdu_len = 0; - BACNET_NPDU_DATA npdu_data; - BACNET_ADDRESS my_address; - int apdu_len = 0; - int npdu_len = 0; - uint8_t Transmit_Buffer[MAX_PDU] = { 0 }; - - datalink_get_my_address(&my_address); - npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); - npdu_len = - npdu_encode_pdu(&Transmit_Buffer[0], dest, &my_address, &npdu_data); - apdu_len = abort_encode_apdu( - &Transmit_Buffer[npdu_len], invoke_id, reason, server); - pdu_len = apdu_len + npdu_len; - (void)datalink_send_pdu(dest, &npdu_data, &Transmit_Buffer[0], pdu_len); -} - -/** - * @brief We received a segment of a ConfirmedService packet, - * check TSM state and reassemble the full packet - * @param src [in] Source address - * @param service_data [in] Service data - * @param internal_invoke_id [out] Internal invoke ID - * @param pservice_request [in/out] Service request buffer - * @param pservice_request_len [in/out] Service request length - * @return true if the segment was received and processed - */ -bool tsm_set_segmented_confirmed_service_received( - BACNET_ADDRESS *src, - BACNET_CONFIRMED_SERVICE_DATA *service_data, - uint8_t *internal_invoke_id, - uint8_t **pservice_request, - uint16_t *pservice_request_len) -{ - uint8_t index; - uint8_t *service_request = *pservice_request; - uint32_t service_request_len = *pservice_request_len; - bool result = false; - bool ack_needed = false; - uint8_t internal_service_id = tsm_get_peer_id(src, service_data->invoke_id); - *internal_invoke_id = internal_service_id; - if (!internal_service_id) { - /* failed : could not allocate enough slot for this transaction */ - tsm_abort_pdu_send( - service_data->invoke_id, src, - ABORT_REASON_PREEMPTED_BY_HIGHER_PRIORITY_TASK, true); - - /* We must free invoke_id ! */ - tsm_free_invoke_id_check(internal_service_id, NULL, true); - return false; - } - index = tsm_find_invokeID_index(internal_service_id); - if (index >= MAX_TSM_TRANSACTIONS) { /* shall not fail */ - tsm_abort_pdu_send( - service_data->invoke_id, src, ABORT_REASON_OTHER, true); - return false; - } - /* check states */ - switch (TSM_List[index].state) { - /* Initial state: ConfirmedSegmentReceived */ - case TSM_STATE_IDLE: - /* We never stay in IDLE state */ - TSM_List[index].state = TSM_STATE_SEGMENTED_REQUEST_SERVER; - /* First time : Compute Actual WindowSize */ - /* we automatically accept the proposed window size */ - TSM_List[index].ActualWindowSize = - TSM_List[index].ProposedWindowSize = - service_data->proposed_window_number; - /* Init sequence numbers */ - TSM_List[index].InitialSequenceNumber = 0; - TSM_List[index].LastSequenceNumber = 0; - /* resets counters */ - TSM_List[index].RetryCount = 0; - TSM_List[index].SegmentRetryCount = 0; - TSM_List[index].ReceivedSegmentsCount = 1; - /* stop unsegmented timer */ - TSM_List[index].RequestTimer = 0; /* unused */ - /* start the segmented timer */ - TSM_List[index].SegmentTimer = apdu_segment_timeout() * 4; - /* reset memorized data */ - tsm_blob_reset(&TSM_List[index]); - if (service_data->sequence_number == 0 && - (TSM_List[index].ProposedWindowSize == 0 || - TSM_List[index].ProposedWindowSize > 127)) { - /* ConfirmedSegmentedReceivedWindowSizeOutofRange */ - tsm_abort_pdu_send( - service_data->invoke_id, src, - ABORT_REASON_WINDOW_SIZE_OUT_OF_RANGE, true); - - /* We must free invoke_id ! */ - tsm_free_invoke_id_check(internal_service_id, NULL, true); - break; - } - - /* Test : sequence number MUST be 0 */ - /* UnexpectedPDU_Received */ - if (service_data->sequence_number != 0) { - /* Release data */ - tsm_blob_free(&TSM_List[index]); - /* Abort */ - tsm_abort_pdu_send( - service_data->invoke_id, src, - ABORT_REASON_INVALID_APDU_IN_THIS_STATE, true); - /* We must free invoke_id ! */ - tsm_free_invoke_id_check(internal_service_id, NULL, true); - } else { - /* Okay : memorize data */ - tsm_blob_data_add( - &TSM_List[index], service_request, service_request_len); - /* We ACK the first segment of the segmented message */ - tsm_segmentack_pdu_send( - src, false, true, service_data->invoke_id, - TSM_List[index].LastSequenceNumber, - TSM_List[index].ActualWindowSize); - } - break; - /* New segments */ - case TSM_STATE_SEGMENTED_REQUEST_SERVER: - /* reset the segment timer */ - TSM_List[index].RequestTimer = 0; /* unused */ - /* ANSI/ASHRAE 135-2008 5.4.5.2 SEGMENTED_REQUEST / Timeout */ - /* ...SegmentTimer becomes greater than Tseg times four, ... */ - TSM_List[index].SegmentTimer = apdu_segment_timeout() * 4; - /* Sequence number MUST be (LastSequenceNumber+1 modulo 256) */ - if ((service_data->sequence_number != - (uint8_t)(TSM_List[index].LastSequenceNumber + 1) % 256)) { - if (DuplicateInWindow( - &TSM_List[index], service_data->sequence_number, - (TSM_List[index].InitialSequenceNumber) % 256, - TSM_List[index].LastSequenceNumber)) { - /* DuplicateSegmentReceived */ - if (tsm_duplicate_segment_received( - index, service_data, src)) { - /* state is in TSM_STATE_SEGMENTED_REQUEST_SERVER */ - break; - } - } else { - /* Recoverable Error: SegmentReceivedOutOfOrder */ - /* ACK of last segment correctly received. */ - tsm_segmentack_pdu_send( - src, true, true, service_data->invoke_id, - TSM_List[index].LastSequenceNumber, - TSM_List[index].ActualWindowSize); - - Duplicate_Count = 0; - } - } else { - /* Count maximum segments */ - if (++TSM_List[index].ReceivedSegmentsCount > - BACNET_MAX_SEGMENTS_ACCEPTED) { - /* ABORT: SegmentReceivedOutOfSpace */ - tsm_abort_pdu_send( - service_data->invoke_id, src, - ABORT_REASON_BUFFER_OVERFLOW, true); - /* Release data */ - tsm_blob_free(&TSM_List[index]); - /* Enter IDLE state */ - TSM_List[index].state = TSM_STATE_IDLE; - /* We must free invoke_id ! */ - tsm_free_invoke_id_check(internal_service_id, NULL, true); - } else { - /* NewSegmentReceived */ - TSM_List[index].LastSequenceNumber = - service_data->sequence_number; - tsm_blob_data_add( - &TSM_List[index], service_request, service_request_len); - /* LastSegmentOfComplexACK_Received */ - if (service_data->sequence_number == - (uint8_t)(TSM_List[index].InitialSequenceNumber + - TSM_List[index].ActualWindowSize)) { - ack_needed = true; - TSM_List[index].InitialSequenceNumber = - service_data->sequence_number; - } - /* LastSegmentOfComplexACK_Received */ - if (!service_data->more_follows) { - /* Resulting segment data */ - *pservice_request = tsm_blob_data_get( - &TSM_List[index], pservice_request_len); - result = - true; /* Returns true on final segment received */ - ack_needed = true; - } - /* LastSegmentOfComplexACK_Received or - * LastSegmentOfGroupReceived */ - if (ack_needed) { - /* ACK received segment */ - tsm_segmentack_pdu_send( - src, false, true, service_data->invoke_id, - TSM_List[index].LastSequenceNumber, - TSM_List[index].ActualWindowSize); - } - } - } - break; - default: - break; - } - return result; -} - -/** - * @brief calculate how many segments will be used to send data - * in this TSM slot - * @param data - TSM data - * @return 1 : No segmentation needed, >1 segmentation needed (number of - * segments). - */ -static uint32_t tsm_apdu_max_segments_get(BACNET_TSM_DATA *data) -{ - uint32_t header_size; - uint32_t packets; - - /* Are we unsegmented ? */ - header_size = tsm_apdu_header_typical_size(&data->apdu_fixed_header, false); - if (header_size + data->apdu_len <= data->apdu_maximum_length) { - return 1; - } - - /* We are segmented : calculate how many segments to use */ - header_size = tsm_apdu_header_typical_size(&data->apdu_fixed_header, true); - - /* Number of packets to use formula : p = ( ( total_length - 1 ) / - * packet_length ) + 1; */ - packets = - ((data->apdu_len - 1) / (data->apdu_maximum_length - header_size)) + 1; - - return packets; -} - -/** - * @brief calculate the maximum APDU length - * @param dest - destination address - * @param confirmed_service_data - confirmed service data - * @param apdu_max - maximum APDU length - * @param total_max - total maximum APDU length - */ -static void tsm_apdu_transmittable_length( - BACNET_ADDRESS *dest, - BACNET_CONFIRMED_SERVICE_DATA *confirmed_service_data, - uint32_t *apdu_max, - uint32_t *total_max) -{ - uint32_t deviceId = 0; - unsigned max_apdu; - uint8_t segmentation = 0; - uint16_t maxsegments = 0; - BACNET_ADDRESS src; - - /* either we are replying to a confirmed service, so we use prompted values - ; either we are requesting a peer, so we use memorised information about - the peer device. - */ - if (confirmed_service_data) { - /* use maximum available APDU */ - *total_max = *apdu_max = - min(confirmed_service_data->max_resp, MAX_APDU); - /* segmented : compute maximum number of packets */ - if (confirmed_service_data->segmented_response_accepted) { - maxsegments = confirmed_service_data->max_segs; - /* if unspecified, try the maximum available, not just 2 segments */ - if (!maxsegments || maxsegments > 64) { - maxsegments = BACNET_MAX_SEGMENTS_ACCEPTED; - } - /* maximum size we are able to transmit */ - *total_max = - min(maxsegments, BACNET_MAX_SEGMENTS_ACCEPTED) * (*apdu_max); - } - return; - } - - if (address_get_device_id(dest, &deviceId)) { - if (address_segment_get_by_device( - deviceId, &max_apdu, &src, &segmentation, &maxsegments)) { - /* Best possible APDU size */ - *total_max = *apdu_max = min(max_apdu, MAX_APDU); - /* if device is able to receive segments */ - if (segmentation == SEGMENTATION_BOTH || - segmentation == SEGMENTATION_RECEIVE) { - /* XXX - TODO: Number of segments accepted by peer device : - If zero segments we should fallback to 2 segments. - Or Maybe we just didn't ask the device about the maximum - segments supported. - */ - if (!maxsegments) { - maxsegments = BACNET_MAX_SEGMENTS_ACCEPTED; - } - /* maximum size we are able to transmit */ - if (maxsegments) { - *total_max = - min(maxsegments, BACNET_MAX_SEGMENTS_ACCEPTED) * - (*apdu_max); - } - } - return; - } - } - *apdu_max = MAX_APDU; - *total_max = *apdu_max * BACNET_MAX_SEGMENTS_ACCEPTED; -} - -/** - * @brief room checks to prevent buffer overflows - * @param apdu_len - current APDU length - * @param max_apdu - maximum APDU length - * @param space_needed - space needed for the new data - * @return true if there is enough space, false otherwise - */ -static bool -tsm_apdu_space_available(int apdu_len, int max_apdu, int space_needed) -{ - return (apdu_len + space_needed) < max_apdu; -} - -/** - * @brief send a packet to peer - * @param tsm_data - TSM data - * @param segment_number - segment number to send - * @return number of bytes sent or -1 on error - */ -static int tsm_pdu_send(BACNET_TSM_DATA *tsm_data, uint32_t segment_number) -{ - uint8_t Transmit_Buffer[MAX_PDU] = { 0 }; - BACNET_ADDRESS my_address; - int len = 0; - int pdu_len = 0; - uint8_t *service_data = NULL; - uint32_t service_len = 0; - uint32_t total_segments = 0; - - /* Rebuild PDU */ - datalink_get_my_address(&my_address); - len = npdu_encode_pdu( - &Transmit_Buffer[pdu_len], &tsm_data->dest, &my_address, - &tsm_data->npdu_data); - if (len < 0) { - return -1; - } - pdu_len += len; - /* Header tweaks ! */ - total_segments = tsm_apdu_max_segments_get(tsm_data); - /* Index out of bounds */ - if (segment_number >= total_segments) { - return -1; - } - if (total_segments == 1) { - tsm_data->apdu_fixed_header.service_data.common_data.segmented_message = - false; - } else { - /* SEG */ - tsm_data->apdu_fixed_header.service_data.common_data.segmented_message = - true; - /* MORE */ - tsm_data->apdu_fixed_header.service_data.common_data.more_follows = - (segment_number < total_segments - 1); - /* Window size : do not modify */ - /* tsm_data->apdu_fixed_header.service_data.common_data.proposed_window_number - * = 127; */ - /* SEQ# */ - tsm_data->apdu_fixed_header.service_data.common_data.sequence_number = - segment_number; - } - /* Rebuild APDU Header */ - len = apdu_encode_fixed_header( - &Transmit_Buffer[pdu_len], &tsm_data->apdu_fixed_header); - if (len < 0) { - return -1; - } - pdu_len += len; - /* Rebuild APDU service data */ - /* gets Nth packet data */ - service_data = - tsm_blob_data_segment_get(tsm_data, segment_number, &service_len); - if (!service_data) { /* May be zero-size ! */ - return -1; - } - /* enough room ? */ - if (!tsm_apdu_space_available( - pdu_len, sizeof(Transmit_Buffer), service_len)) { - return -1; - } - memcpy(&Transmit_Buffer[pdu_len], service_data, service_len); - pdu_len += service_len; - return datalink_send_pdu( - &tsm_data->dest, &tsm_data->npdu_data, &Transmit_Buffer[0], pdu_len); -} - -/** - * @brief Process and send segmented/unsegmented complex acknoweldegement - * based on the response data length - * For unsegmented response, send the whole data - * For segmented response, send the 1st segment of response data - * @param dest - destination address - * @param npdu_data - NPDU data - * @param apdu_fixed_header - APDU fixed header - * @param confirmed_service_data - confirmed service data - * @param pdu - PDU data - * @param pdu_len - PDU length - * @return number of bytes sent or -1 on error - */ -int tsm_set_complexack_transaction( - BACNET_ADDRESS *dest, - BACNET_NPDU_DATA *npdu_data, - BACNET_APDU_FIXED_HEADER *apdu_fixed_header, - BACNET_CONFIRMED_SERVICE_DATA *confirmed_service_data, - uint8_t *pdu, - uint32_t pdu_len) -{ - uint8_t index; - int bytes_sent; - BACNET_TSM_DATA *tsm_data; - uint32_t apdu_segments; - uint8_t internal_service_id = - tsm_get_peer_id(dest, confirmed_service_data->invoke_id); - - if (!internal_service_id) { - /* failed : could not allocate enough slot for this transaction */ - tsm_abort_pdu_send( - confirmed_service_data->invoke_id, dest, - ABORT_REASON_PREEMPTED_BY_HIGHER_PRIORITY_TASK, true); - return -1; - } - index = tsm_find_invokeID_index(internal_service_id); - if (index >= MAX_TSM_TRANSACTIONS) { /* shall not fail */ - tsm_abort_pdu_send( - confirmed_service_data->invoke_id, dest, ABORT_REASON_OTHER, true); - tsm_free_invoke_id_check(internal_service_id, dest, true); - return -1; - } - tsm_data = &TSM_List[index]; - - /* Choice between a segmented or a non-segmented transaction */ - - /* fill in maximum fill values */ - tsm_apdu_transmittable_length( - dest, confirmed_service_data, &tsm_data->apdu_maximum_length, - &tsm_data->maximum_transmittable_length); - /* copy the apdu service data */ - tsm_blob_data_copy(tsm_data, &pdu[0], pdu_len); - /* copy npdu data */ - npdu_copy_data(&tsm_data->npdu_data, npdu_data); - /* copy apdu header data */ - tsm_data->apdu_fixed_header = *apdu_fixed_header; - /* destination address */ - bacnet_address_copy(&tsm_data->dest, dest); - /* absolute "retry" count : won't be reinitialized later */ - tsm_data->RetryCount = apdu_retries(); - - tsm_data->ActualWindowSize = 1; - tsm_data->ProposedWindowSize = BACNET_SEGMENTATION_WINDOW_SIZE_DEFAULT; - tsm_data->InitialSequenceNumber = 0; - tsm_data->SentAllSegments = false; - - /* Choice between a segmented or a non-segmented transaction */ - if (1 == (apdu_segments = tsm_apdu_max_segments_get(tsm_data))) { - /* UNSEGMENTED MODE : Free transaction afterwards */ - bytes_sent = tsm_pdu_send(tsm_data, 0); - if (bytes_sent > 0) { - tsm_free_invoke_id_check(internal_service_id, dest, true); - } - } else { - /* SEGMENTED-MODE */ - /* Take into account the fact that the APDU header is repeated on every - * segment */ - if (pdu_len + - apdu_segments * - tsm_apdu_header_typical_size(apdu_fixed_header, true) > - tsm_data->maximum_transmittable_length) { - /* Too much data : we cannot send that much, or the API cannot - * receive that much ! */ - tsm_blob_free(&TSM_List[index]); - /* Abort */ - tsm_abort_pdu_send( - confirmed_service_data->invoke_id, dest, - ABORT_REASON_BUFFER_OVERFLOW, true); - bytes_sent = -2; - } else { - /* Window size proposal */ - tsm_data->apdu_fixed_header.service_data.common_data - .proposed_window_number = tsm_data->ProposedWindowSize; - /* assign the transaction */ - tsm_data->state = TSM_STATE_SEGMENTED_RESPONSE_SERVER; - tsm_data->SegmentRetryCount = apdu_retries(); - /* start the timer */ - tsm_data->RequestTimer = 0; - tsm_data->SegmentTimer = apdu_segment_timeout(); - /* Send first packet */ - bytes_sent = tsm_pdu_send(tsm_data, 0); - } - } - /* If we cannot initiate, free transaction so we don't wait on a timeout to - realize it has failed. Caller don't free invoke ID : we must clear it - now. */ - if (bytes_sent <= 0) { - tsm_free_invoke_id_check(internal_service_id, dest, true); - } - return bytes_sent; -} - -/** - * @brief Sends PDU segments either until the window is full or - * until the last segment of a message has been sent. - * @param tsm_data - TSM data - * @param sequence_number - sequence number of the segment - */ -static void FillWindow(BACNET_TSM_DATA *tsm_data, uint32_t sequence_number) -{ - uint32_t ix; - uint32_t total_segments = tsm_apdu_max_segments_get(tsm_data); - for (ix = 0; (ix < tsm_data->ActualWindowSize) && - (sequence_number + ix < total_segments); - ix++) { - tsm_pdu_send(tsm_data, sequence_number + ix); - } - /* sent all segments ? */ - if (ix + sequence_number >= total_segments) { - tsm_data->SentAllSegments = true; - } -} - -/** - * @brief Check if the sequence number is in the window - * @param data - TSM data - * @param seqA - sequence number A - * @param seqB - sequence number B - * @return true if the sequence number is in the window - */ -static bool InWindow(BACNET_TSM_DATA *data, uint8_t seqA, uint8_t seqB) -{ - uint8_t requiredWindowSize = seqA - seqB; - return requiredWindowSize < data->ActualWindowSize; -} - -/** - * @brief Process the received segment ack and send - * the next segment accordingly if available - * @param invoke_id - invoke ID of the peer - * @param sequence_number - sequence number of the segment - * @param actual_window_size - actual window size - * @param nak - nak flag - * @param server - true if server - * @param src - BACnet address of the peer - */ -void tsm_segmentack_received( - uint8_t invoke_id, - uint8_t sequence_number, - uint8_t actual_window_size, - bool nak, - bool server, - BACNET_ADDRESS *src) -{ - uint8_t index; - uint32_t big_segment_number; - uint8_t window; - bool some_segment_remains; - BACNET_TSM_INDIRECT_DATA *peer_data; - - (void)nak; - - /* bad invoke number from server peer (we never use 0) */ - if (server && !invoke_id) { - return; - } - /* Peer invoke id number : translate to our internal numbers */ - if (!server) { - peer_data = tsm_get_peer_id_data(src, invoke_id, false); - if (!peer_data) { - /* failed : unknown message */ - return; - } - /* now we use our internal number */ - invoke_id = peer_data->InternalInvokeID; - } - /* Find an active TSM slot that matches the Segment-Ack */ - index = tsm_find_invokeID_index(invoke_id); - if (index >= MAX_TSM_TRANSACTIONS) { - return; - } - - /* Almost the same code for segment handling between segmented requests and - * responses */ - if (!server && - TSM_List[index].state == TSM_STATE_SEGMENTED_RESPONSE_SERVER) { - /* DuplicateAck_Received */ - if (!InWindow( - &TSM_List[index], sequence_number, - TSM_List[index].InitialSequenceNumber)) { - /* Restart timer */ - TSM_List[index].SegmentTimer = apdu_segment_timeout(); - } else { - /* total segment number (not modulo 256) */ - window = sequence_number - - (uint8_t)TSM_List[index].InitialSequenceNumber; - big_segment_number = TSM_List[index].InitialSequenceNumber + window; - - /* 1..N segment number < number of segments ? */ - some_segment_remains = (big_segment_number + 1) < - tsm_apdu_max_segments_get(&TSM_List[index]); - if (some_segment_remains) { - /* NewAck_Received : do we have a segment remaining to send */ - TSM_List[index].InitialSequenceNumber = big_segment_number + 1; - TSM_List[index].ActualWindowSize = actual_window_size; - TSM_List[index].SegmentRetryCount = apdu_retries(); - TSM_List[index].SegmentTimer = apdu_segment_timeout(); - FillWindow( - &TSM_List[index], TSM_List[index].InitialSequenceNumber); - TSM_List[index].SegmentTimer = apdu_segment_timeout(); - } else { - /* FinalAck_Received */ - TSM_List[index].SegmentTimer = 0; - if (TSM_List[index].state == - TSM_STATE_SEGMENTED_RESPONSE_SERVER) { - /* Response : end communications */ - /* Release data */ - TSM_List[index].state = TSM_STATE_IDLE; - /* Completely free data */ - tsm_free_invoke_id_check(invoke_id, NULL, true); - } else { - /* Request : Wait confirmation */ - TSM_List[index].RequestTimer = apdu_timeout(); - TSM_List[index].state = TSM_STATE_AWAIT_CONFIRMATION; - } - } - } - } else { - /* UnexpectedPDU_Received */ - /* Release data */ - tsm_blob_free(&TSM_List[index]); - /* Abort */ - tsm_abort_pdu_send( - invoke_id, src, ABORT_REASON_INVALID_APDU_IN_THIS_STATE, true); - /* We must free invoke_id ! */ - tsm_free_invoke_id_check(invoke_id, NULL, true); - } -} - -/** - * @brief Check unexpected PDU is received in active TSM state other - * than idle state for server - * @param src - BACnet address of the peer - * @param service_data - BACnet confirmed service data - * @return true if the unexpected PDU is received in active TSM state - */ -bool tsm_is_invalid_apdu_in_this_state( - BACNET_ADDRESS *src, BACNET_CONFIRMED_SERVICE_DATA *service_data) -{ - uint8_t index = 0; - bool status = false; - BACNET_TSM_INDIRECT_DATA *peer_data; - peer_data = tsm_get_peer_id_data(src, service_data->invoke_id, false); - if (peer_data) { - index = tsm_find_invokeID_index(peer_data->InternalInvokeID); - if (index < MAX_TSM_TRANSACTIONS) { - BACNET_TSM_DATA *plist = &TSM_List[index]; - if ((plist->state == TSM_STATE_SEGMENTED_RESPONSE_SERVER) || - (plist->state == TSM_STATE_SEGMENTED_REQUEST_SERVER)) { - tsm_abort_pdu_send( - service_data->invoke_id, src, - ABORT_REASON_INVALID_APDU_IN_THIS_STATE, true); - tsm_free_invoke_id_check(plist->InvokeID, src, true); - status = true; - } - } - } - return status; -} - -/** - * @brief frees the invokeID for segmented messages - * @param src - BACnet address of the peer - * @param invoke_id - invoke ID to free - */ -void tsm_free_invoke_id_segmentation(BACNET_ADDRESS *src, uint8_t invoke_id) -{ - uint8_t peer_id = 0; - peer_id = tsm_get_peer_id(src, invoke_id); - /* Peer_id = 0 refers to free slot */ - if (peer_id) { - tsm_free_invoke_id_check(peer_id, src, true); - } -} -#endif - -/** - * @brief Called once a millisecond or slower. - * This function calls the handler for a - * timeout 'Timeout_Function', if necessary. - * - * @note Only supports segmentation for Server by - * implementing two states: - * TSM_STATE_SEGMENTED_RESPONSE_SERVER - * TSM_STATE_SEGMENTED_REQUEST_SERVER - * Client segmentation is not supported at this time. - * - * @param milliseconds - Count of milliseconds passed, since the last call. - */ -void tsm_timer_milliseconds(uint16_t milliseconds) -{ - unsigned i = 0; /* counter */ - BACNET_TSM_DATA *plist = &TSM_List[0]; - - for (i = 0; i < MAX_TSM_TRANSACTIONS; i++, plist++) { - if (plist->state == TSM_STATE_AWAIT_CONFIRMATION) { - if (plist->RequestTimer > milliseconds) { - plist->RequestTimer -= milliseconds; - } else { - plist->RequestTimer = 0; - } - /* AWAIT_CONFIRMATION */ - if (plist->RequestTimer == 0) { - if (plist->RetryCount < apdu_retries()) { - plist->RequestTimer = apdu_timeout(); - plist->RetryCount++; - datalink_send_pdu( - &plist->dest, &plist->npdu_data, &plist->apdu[0], - plist->apdu_len); - DEBUG_PRINTF( - "invoke-id[%u] Retry %u of %u after %ums\n", - plist->InvokeID, plist->RetryCount, apdu_retries(), - plist->RequestTimer); - } else { - /* note: the invoke id has not been cleared yet - and this indicates a failed message: - IDLE and a valid invoke id */ - plist->state = TSM_STATE_IDLE; - if (plist->InvokeID != 0) { - if (Timeout_Function) { - Timeout_Function(plist->InvokeID); - } - } - } - } - } - if (plist->state == TSM_STATE_AWAIT_RESPONSE) { - /* 5.4.5.3 AWAIT_RESPONSE - In the AWAIT_RESPONSE state, the device waits for the - local application program to respond to a BACnet-Confirmed- - Request-PDU. See Clause 9.8 for specific considerations - in MS/TP networks.*/ - /* If RequestTimer becomes greater than Tout, - then issue an N-UNITDATA.request with - 'data_expecting_reply' = FALSE to transmit - a BACnet-Abort-PDU with 'server' = TRUE and - 'abort-reason' = APPLICATION_EXCEEDED_REPLY_TIME; - send ABORT.indication with 'server' = TRUE and - 'abort-reason' = APPLICATION_EXCEEDED_REPLY_TIME - to the local application program; and - enter the IDLE state. */ - /* note: this TSM implementation doesn't do this state */ - } -#if BACNET_SEGMENTATION_ENABLED - if (plist->state == TSM_STATE_SEGMENTED_RESPONSE_SERVER) { - /* RequestTimer stopped in this state */ - if (plist->SegmentTimer > milliseconds) { - plist->SegmentTimer -= milliseconds; - } else { - plist->SegmentTimer = 0; - } - /* timeout. retry? */ - if (plist->SegmentTimer == 0) { - plist->SegmentRetryCount--; - plist->SegmentTimer = apdu_segment_timeout(); - if (plist->SegmentRetryCount) { - /* Re-send PDU data */ - FillWindow(plist, plist->InitialSequenceNumber); - } else { - /* Reached max retries, Clear Peer data */ - tsm_clear_peer_id(plist->InvokeID); - /* Release segmented data */ - tsm_blob_free(&TSM_List[i]); - - /* flag slot as "unused" */ - plist->InvokeID = 0; - /* set to IDLE state */ - plist->state = TSM_STATE_IDLE; - } - } - } - if ((plist->state == TSM_STATE_SEGMENTED_REQUEST_SERVER) || - (plist->state == TSM_STATE_SEGMENTED_CONFIRMATION)) { - /* 5.4.5.2 SEGMENTED_REQUEST - In the SEGMENTED_REQUEST state, the device waits for segments - of a BACnet-Confirmed-Request-PDU. */ - /* 5.4.4.4 SEGMENTED_CONF - In the SEGMENTED_CONF state, the device waits for one or - more segments in response to a BACnet-SegmentACK-PDU.*/ - /* RequestTimer stopped in this state */ - if (plist->SegmentTimer > milliseconds) { - plist->SegmentTimer -= milliseconds; - } else { - plist->SegmentTimer = 0; - } - /* Timeout - If SegmentTimer becomes greater than Tseg times four, - then stop SegmentTimer and enter the IDLE state. */ - if (plist->SegmentTimer == 0) { - /* Clear Peer data, if any. Lookup with our internal ID - status. */ - tsm_clear_peer_id(plist->InvokeID); - /* Release segmented data */ - tsm_blob_free(plist); - - /* flag slot as "unused" */ - plist->InvokeID = 0; - /* free all memory associated */ - plist->state = TSM_STATE_IDLE; - } - } -#endif - } -} #endif diff --git a/src/bacnet/basic/tsm/tsm.h b/src/bacnet/basic/tsm/tsm.h index b94d3473..e7aef5b6 100644 --- a/src/bacnet/basic/tsm/tsm.h +++ b/src/bacnet/basic/tsm/tsm.h @@ -15,9 +15,6 @@ #include "bacnet/bacdef.h" /* BACnet Stack API */ #include "bacnet/npdu.h" -#if BACNET_SEGMENTATION_ENABLED -#include "bacnet/apdu.h" -#endif /* note: TSM functionality is optional - only needed if we are doing client requests */ @@ -27,7 +24,7 @@ extern "C" { #endif /* __cplusplus */ /* FIXME: modify basic service handlers to use TSM rather than this buffer! */ -BACNET_STACK_EXPORT extern uint8_t Handler_Transmit_Buffer[MAX_ASDU]; +BACNET_STACK_EXPORT extern uint8_t Handler_Transmit_Buffer[MAX_PDU]; #ifdef __cplusplus } @@ -40,47 +37,31 @@ typedef enum { TSM_STATE_IDLE, TSM_STATE_AWAIT_CONFIRMATION, TSM_STATE_AWAIT_RESPONSE, - TSM_STATE_SEGMENTED_REQUEST_SERVER, - TSM_STATE_SEGMENTED_CONFIRMATION, - TSM_STATE_SEGMENTED_RESPONSE_SERVER + TSM_STATE_SEGMENTED_REQUEST, + TSM_STATE_SEGMENTED_CONFIRMATION } BACNET_TSM_STATE; -#if BACNET_SEGMENTATION_ENABLED -/* Indirect data state : */ -typedef struct BACnet_TSM_Indirect_Data { - /* the address we received data from */ - BACNET_ADDRESS PeerAddress; - /* the peer unique id */ - uint8_t PeerInvokeID; - /* the unique id to use within our internal states. - zero means : "unused slot". */ - uint8_t InternalInvokeID; -} BACNET_TSM_INDIRECT_DATA; -#endif - /* 5.4.1 Variables And Parameters */ /* The following variables are defined for each instance of */ /* Transaction State Machine: */ typedef struct BACnet_TSM_Data { /* used to count APDU retries */ uint8_t RetryCount; -#if BACNET_SEGMENTATION_ENABLED /* used to count segment retries */ - uint8_t SegmentRetryCount; + /*uint8_t SegmentRetryCount; */ /* used to control APDU retries and the acceptance of server replies */ - bool SentAllSegments; + /*bool SentAllSegments; */ /* stores the sequence number of the last segment received in order */ - uint8_t LastSequenceNumber; + /*uint8_t LastSequenceNumber; */ /* stores the sequence number of the first segment of */ /* a sequence of segments that fill a window */ - uint8_t InitialSequenceNumber; + /*uint8_t InitialSequenceNumber; */ /* stores the current window size */ - uint8_t ActualWindowSize; + /*uint8_t ActualWindowSize; */ /* stores the window size proposed by the segment sender */ - uint8_t ProposedWindowSize; + /*uint8_t ProposedWindowSize; */ /* used to perform timeout on PDU segments */ - uint16_t SegmentTimer; -#endif + /*uint8_t SegmentTimer; */ /* used to perform timeout on Confirmed Requests */ /* in milliseconds */ uint16_t RequestTimer; @@ -92,28 +73,9 @@ typedef struct BACnet_TSM_Data { BACNET_ADDRESS dest; /* the network layer info */ BACNET_NPDU_DATA npdu_data; - unsigned apdu_len; -#if BACNET_SEGMENTATION_ENABLED - /* APDU header information */ - BACNET_APDU_FIXED_HEADER apdu_fixed_header; - /* calculated max APDU length / packet */ - uint32_t apdu_maximum_length; - /* calculated max APDU length / total */ - uint32_t maximum_transmittable_length; - /* Multiple APDU segments blob memorized here */ - uint8_t *apdu_blob; - /* Size of allocated Multiple APDU segments blob */ - uint32_t apdu_blob_allocated; - /* Size of data within the multiple APDU segments blob */ - uint32_t apdu_blob_size; - /* Count received segments (prevents D.O.S.) */ - uint32_t ReceivedSegmentsCount; - /* copy of the APDU, should we need to send it again */ - uint8_t *apdu; -#else /* copy of the APDU, should we need to send it again */ uint8_t apdu[MAX_PDU]; -#endif + unsigned apdu_len; } BACNET_TSM_DATA; typedef void (*tsm_timeout_function)(uint8_t invoke_id); @@ -161,59 +123,6 @@ bool tsm_invoke_id_free(uint8_t invokeID); BACNET_STACK_EXPORT bool tsm_invoke_id_failed(uint8_t invokeID); -#if BACNET_SEGMENTATION_ENABLED -/** Clear TSM Peer data */ -BACNET_STACK_EXPORT -void tsm_clear_peer_id(uint8_t InternalInvokeID); - -/* frees the invokeID and sets its state to IDLE */ -BACNET_STACK_EXPORT -void tsm_free_invoke_id_check( - uint8_t invokeID, BACNET_ADDRESS *peer_address, bool cleanup); - -/* Associates a Peer address and invoke ID with our TSM */ -BACNET_STACK_EXPORT -uint8_t tsm_get_peer_id(BACNET_ADDRESS *src, uint8_t invokeID); - -BACNET_STACK_EXPORT -bool tsm_set_segmented_confirmed_service_received( - BACNET_ADDRESS *src, - BACNET_CONFIRMED_SERVICE_DATA *service_data, - uint8_t *internal_invoke_id, - uint8_t **pservice_request, /* IN/OUT */ - uint16_t *pservice_request_len /* IN/OUT */ -); - -BACNET_STACK_EXPORT -int tsm_set_complexack_transaction( - BACNET_ADDRESS *dest, - BACNET_NPDU_DATA *npdu_data, - BACNET_APDU_FIXED_HEADER *apdu_fixed_header, - BACNET_CONFIRMED_SERVICE_DATA *confirmed_service_data, - uint8_t *pdu, - uint32_t pdu_len); - -BACNET_STACK_EXPORT -void tsm_segmentack_received( - uint8_t invoke_id, - uint8_t sequence_number, - uint8_t actual_window_size, - bool nak, - bool server, - BACNET_ADDRESS *src); - -BACNET_STACK_EXPORT -bool tsm_is_invalid_apdu_in_this_state( - BACNET_ADDRESS *src, BACNET_CONFIRMED_SERVICE_DATA *service_data); - -BACNET_STACK_EXPORT -void tsm_abort_pdu_send( - uint8_t invoke_id, BACNET_ADDRESS *dest, uint8_t reason, bool server); - -BACNET_STACK_EXPORT -void tsm_free_invoke_id_segmentation(BACNET_ADDRESS *src, uint8_t invoke_id); - -#endif #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/bacnet/config.h b/src/bacnet/config.h index 27cbcd47..69c72538 100644 --- a/src/bacnet/config.h +++ b/src/bacnet/config.h @@ -164,11 +164,6 @@ #endif #endif -/* Enable or disable segmentation support in the library */ -#ifndef BACNET_SEGMENTATION_ENABLED -#define BACNET_SEGMENTATION_ENABLED 0 -#endif - /* for confirmed messages, this is the number of transactions */ /* that we hold in a queue waiting for timeout. */ /* Configure to zero if you don't want any confirmed messages */ diff --git a/src/bacnet/segmentack.c b/src/bacnet/segmentack.c deleted file mode 100644 index e768d8ec..00000000 --- a/src/bacnet/segmentack.c +++ /dev/null @@ -1,104 +0,0 @@ -/*####COPYRIGHTBEGIN#### - ------------------------------------------- - Copyright (C) 2010 Julien Bennet - - 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 "segmentack.h" - -/** Method to encode the segment ack . - * - * @param apdu[in] Pointer to the buffer for encoding. - * @param negativeack[in] Acknowedlegment for the segment. - * @param server[in] Set to True if the acknowledgment is from the server, else - * false. - * @param invoke_id[in] Invoke Id - * @param sequence_number[in] Sequence number of the segment to be acknowledged - * @param actual_window_size[in] Actual window size. - * - * @return Length of encoded data or zero on error. - */ -int segmentack_encode_apdu( - uint8_t *apdu, - bool negativeack, - bool server, - uint8_t invoke_id, - uint8_t sequence_number, - uint8_t actual_window_size) -{ - int apdu_len = 0; /* total length of the apdu, return value */ - uint8_t server_code = server ? 0x01 : 0x00; - uint8_t nak_code = negativeack ? 0x02 : 0x00; - - if (apdu) { - apdu[0] = PDU_TYPE_SEGMENT_ACK | server_code | nak_code; - apdu[1] = invoke_id; - apdu[2] = sequence_number; - apdu[3] = actual_window_size; - apdu_len = 4; - } - - return apdu_len; -} - -/** Method to decode the segment ack service request - * - * @param apdu[in] The apdu portion of the ACK reply. - * @param apdu_len[in] The total length of the apdu. - * @param invoke_id[in] Invoke Id of the request. - * @param sequence_number[in] Sequence number of the segment received. - * @param actual_window_size[in] Actual window size. - * - * @return Length of decoded data or zero on error. - */ -int segmentack_decode_service_request( - uint8_t *apdu, - unsigned apdu_len, - uint8_t *invoke_id, - uint8_t *sequence_number, - uint8_t *actual_window_size) -{ - int len = 0; - int apdu_header_size = 3; - - if (apdu_len >= apdu_header_size) { - if (invoke_id) { - *invoke_id = apdu[0]; - } - if (sequence_number) { - *sequence_number = apdu[1]; - } - if (actual_window_size) { - *actual_window_size = apdu[2]; - } - } - - return len; -} diff --git a/src/bacnet/segmentack.h b/src/bacnet/segmentack.h deleted file mode 100644 index 20eaf25a..00000000 --- a/src/bacnet/segmentack.h +++ /dev/null @@ -1,68 +0,0 @@ -/*####COPYRIGHTBEGIN#### - ------------------------------------------- - Copyright (C) 2010 Julien Bennet - - 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####*/ -#ifndef BACNET_SEGMENT_ACK_H -#define BACNET_SEGMENT_ACK_H - -#include -#include - -#include "bacenum.h" -#include "bacdcode.h" -#include "bacdef.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -BACNET_STACK_EXPORT -int segmentack_encode_apdu( - uint8_t *apdu, - bool negativeack, - bool server, - uint8_t invoke_id, - uint8_t sequence_number, - uint8_t actual_window_size); - -BACNET_STACK_EXPORT -int segmentack_decode_service_request( - uint8_t *apdu, - unsigned apdu_len, - uint8_t *invoke_id, - uint8_t *sequence_number, - uint8_t *actual_window_size); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* SEGMENT_ACK_H */