Finite State Machine for Security

A generic FSM has been added to DDS Security Core component to realize authentication handshake process.
The list of the states and the transitions are given in the creation and the FSM is started with a start call.
Passing arguments to transition funstions is possible.
Timeout transitions are possible.

Signed-off-by: Kurtulus Oksuztepe <kurtulus.oksuztepe@adlinktech.com>
This commit is contained in:
Kurtulus Oksuztepe 2019-12-06 16:17:36 +01:00 committed by eboasson
parent 9481a75e9d
commit 0b1804e039
9 changed files with 1814 additions and 2 deletions

View file

@ -140,11 +140,20 @@ ddsrt_nonnull((1,2,3,4));
/**
* @brief Retrieve integer representation of the given thread id.
*
* @returns The integer representation of the given thread.
* @returns The integer representation of the current thread.
*/
DDS_EXPORT ddsrt_tid_t
ddsrt_gettid(void);
/**
* @brief Retrieve integer representation of the given thread id.
*
* @returns The integer representation of the given thread.
*/
DDS_EXPORT ddsrt_tid_t
ddsrt_gettid_for_thread( ddsrt_thread_t thread);
/**
* @brief Return thread ID of the calling thread.
*

View file

@ -83,6 +83,13 @@ ddsrt_gettid(void)
return status.xTaskNumber;
}
DDS_EXPORT ddsrt_tid_t
ddsrt_gettid_for_thread( ddsrt_thread_t thread)
{
return (ddsrt_tid_t)thread.task;
}
ddsrt_thread_t
ddsrt_thread_self(void)
{

View file

@ -346,6 +346,14 @@ ddsrt_gettid(void)
return tid;
}
ddsrt_tid_t
ddsrt_gettid_for_thread( ddsrt_thread_t thread)
{
return (ddsrt_tid_t) thread.v;
}
ddsrt_thread_t
ddsrt_thread_self(void)
{

View file

@ -173,6 +173,14 @@ ddsrt_gettid(void)
return GetCurrentThreadId();
}
ddsrt_tid_t
ddsrt_gettid_for_thread( ddsrt_thread_t thread)
{
return (ddsrt_tid_t) thread.tid;
}
ddsrt_thread_t
ddsrt_thread_self(
void)

View file

@ -14,6 +14,7 @@ PREPEND(srcs_security_core "${CMAKE_CURRENT_LIST_DIR}/src"
dds_security_utils.c
dds_security_plugins.c
shared_secret.c
dds_security_fsm.c
)
PREPEND(hdrs_public_security_core "${CMAKE_CURRENT_LIST_DIR}/include/security/core"
@ -21,6 +22,7 @@ PREPEND(hdrs_public_security_core "${CMAKE_CURRENT_LIST_DIR}/include/security/co
dds_security_types.h
dds_security_utils.h
dds_security_plugins.h
dds_security_fsm.h
shared_secret.h
)
@ -52,4 +54,4 @@ install(
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/dds/security/core/"
COMPONENT dev)
add_subdirectory(tests/plugin_loading)
add_subdirectory(tests)

View file

@ -0,0 +1,224 @@
/*
* Copyright(c) 2006 to 2019 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#ifndef DDS_SECURITY_FSM_H
#define DDS_SECURITY_FSM_H
#include "dds/ddsrt/time.h"
#include "dds/ddsrt/threads.h"
#if defined (__cplusplus)
extern "C" {
#endif
#define DDS_SECURITY_FSM_EVENT_AUTO (-1)
#define DDS_SECURITY_FSM_EVENT_TIMEOUT (-2)
typedef enum {
DDS_SECURITY_FSM_DEBUG_ACT_DISPATCH,
DDS_SECURITY_FSM_DEBUG_ACT_DISPATCH_DIRECT,
DDS_SECURITY_FSM_DEBUG_ACT_HANDLING
} DDS_SECURITY_FSM_DEBUG_ACT;
struct dds_security_fsm;
struct dds_security_fsm_context;
typedef ddsrt_thread_t (*dds_security_fsm_thread_create_func)(const char *name, ddsrt_thread_routine_t f, void *arg);
typedef void (*dds_security_fsm_thread_destroy_func)( ddsrt_thread_t tid);
/**
* Template for user-defined state methods.
* It is allowed to call dds_security_fsm_dispatch() from within a dispatch function.
*/
typedef void (*dds_security_fsm_action)(struct dds_security_fsm *fsm, void *arg);
/**
* State struct
*
* - func : optional user defined function, invoked by when reaching this state
* - timeout : optional timeout which is controlled by the fsm
*/
typedef struct dds_security_fsm_state {
const dds_security_fsm_action func;
dds_duration_t timeout;
} dds_security_fsm_state;
/**
* Template for user-defined debug methods.
* It'll be called for every dispatched event, regardless of which state it
* is in (which is also provided).
* This can be used to get extra information about the behaviour of the
* state machine.
* It is not allowed to call any fsm API functions from within this
* debug callback.
*/
typedef void (*dds_security_fsm_debug)(struct dds_security_fsm *fsm, DDS_SECURITY_FSM_DEBUG_ACT act,
const dds_security_fsm_state *current, int event_id, void *arg);
/**
* Transition definitions
*
* begin : start state (to transition from)
* event_id : indicate the event responsible for the transition
* func : user defined function, invoked during transition
* end : end state (to transition to)
*/
typedef struct dds_security_fsm_transition {
const dds_security_fsm_state *begin;
const int event_id;
const dds_security_fsm_action func;
const dds_security_fsm_state *end;
} dds_security_fsm_transition;
/**
* Create a new fsm context
* Creates an fsm context. The fsm context manages the global state of the fsm's created within
* this context. The fsm context uses a number of threads to control the state machined allocated
* to this context. A thread create callback has to be provided to created the threads in the
* context of the caller.
*
* @param thr_create_func a callback function used to created the threads used to manage
* the allocated state machines
*
* @return Returns the new fsm context on success. Null on failure.
*/
DDS_EXPORT struct dds_security_fsm_context *
dds_security_fsm_context_create( dds_security_fsm_thread_create_func thr_create_func);
/**
* Destroys a fsm context
* The function clears the fsm context and stops the associated threads. The thread destroy
* function is called to allow the caller to free resources associated with the threads and
* to wait for the threads to exit.
*
* @param context the context to be destroyed
* @param thr_destroy_func a callback function used to wait a thread to terminate
* the allocated state machine
*/
DDS_EXPORT void
dds_security_fsm_context_destroy(struct dds_security_fsm_context *context,
dds_security_fsm_thread_destroy_func thr_destroy_func);
/**
* Create a new fsm
* Initializes a new fsm. Fsm does not start.
*
* @param transitions array of transitions which the defines the functioning of the state machine
* @param size number of transitions
* @param arg Extra data to pass to the fsm. Will be passed to all user defined callback
* methods.
*
* @return Returns the new created state machine on success. Null on failure.
*/
DDS_EXPORT struct dds_security_fsm *
dds_security_fsm_create(struct dds_security_fsm_context *context,
const dds_security_fsm_transition *transitions, int size, void *arg);
/**
* Start a fsm
* Starts the fsm, start with the firs transition
*
* @param fsm fsm to start.
*/
DDS_EXPORT void
dds_security_fsm_start(struct dds_security_fsm *fsm);
/**
* Set an overall timeout for the given state machine
* Will be monitoring the overall timeout of the given state machine,
* invoking a user defined callback when the given timeout expires.
* Timeout will be aborted upon a cleanup of the state machine.
*
* @param fsm fsm to set the overall timeout for
* @param func user defined function which is called when the
* overall timeout expires.
* @param timeout indicates the overall timeout
*/
DDS_EXPORT void
dds_security_fsm_set_timeout(struct dds_security_fsm *fsm, dds_security_fsm_action func, dds_time_t timeout);
/**
* Set an debug callback for the given state machine.
*
* @param fsm fsm to set the overall timeout for
* @param func user defined function which is called for every
* event, whether being dispatched or actually
* handled.
*/
DDS_EXPORT void
dds_security_fsm_set_debug(struct dds_security_fsm *fsm, dds_security_fsm_debug func);
/**
* Dispatches the next event
* Assignment for the state machine to transisiton to the next state.
*
* @param fsm The state machine
* @param event_id Indicate where to transisition to (outcome of current state)
*/
DDS_EXPORT void
dds_security_fsm_dispatch(struct dds_security_fsm *fsm, int32_t event_id);
/**
* Dispatches the next event with priority
* Assignment for the state machine to transisiton to the next state.
* This event will be placed at the top of the event list.
*
* @param fsm The state machine
* @param event_id Indicate where to transisition to (outcome of current state)
*/
DDS_EXPORT void
dds_security_fsm_dispatch_direct(struct dds_security_fsm *fsm, int32_t event_id);
/**
* Retrieve the current state of a given state machine
*
* @param fsm The state machine
*
* @return The current state of the given state machine
*/
DDS_EXPORT const dds_security_fsm_state*
dds_security_fsm_current_state(struct dds_security_fsm *fsm);
/**
* Clean the given state machine
* Cleaning up the given state machine. This will abort all timeouts for
* this state machine and remove all events from the internals.
*
* @param fsm The state machine to clean.
*/
DDS_EXPORT void
dds_security_fsm_cleanup(struct dds_security_fsm *fsm);
/**
* Freeing the state machine.
* Stops all running timeouts and events and cleaning all memory
* related to this machine.
*
* When calling this from another thread, then it may block until
* a possible concurrent event has finished. After this call, the
* fsm may not be used anymore.
*
* When in the fsm action callback function context, this will
* not block. It will garbage collect when the event has been
* handled.
*
* @param fsm The state machine to free
*/
DDS_EXPORT void /* Implicit cleanup. */
dds_security_fsm_free(struct dds_security_fsm *fsm);
#if defined (__cplusplus)
}
#endif
#endif /* DDS_SECURITY_FSM_H */

View file

@ -0,0 +1,749 @@
/*
* Copyright(c) 2006 to 2019 ADLINK Technology Limited and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
* v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
#include <string.h>
#include <assert.h>
#include "dds/security/core/dds_security_fsm.h"
#include "dds/ddsrt/threads.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsrt/heap.h"
#include "dds/ddsrt/atomics.h"
#include "dds/ddsrt/retcode.h"
#include "dds/ddsrt/time.h"
#include "dds/ddsrt/timeconv.h"
#include <stdbool.h>
typedef struct dds_security_fsm {
const dds_security_fsm_transition *transitions;
int size;
void *arg;
const dds_security_fsm_state *current;
dds_time_t current_state_endtime;
ddsrt_atomic_uint32_t ref_cnt;
dds_security_fsm_debug debug_func;
struct dds_security_fsm_context *context;
struct dds_security_fsm *next;
struct dds_security_fsm *prev;
} dds_security_fsm;
typedef struct fsm_event {
struct dds_security_fsm *fsm;
int event_id;
struct fsm_event *next;
struct fsm_event *prev;
} fsm_event;
typedef struct fsm_state_timeout {
struct dds_security_fsm *fsm;
dds_time_t endtime;
} fsm_state_timeout;
typedef struct fsm_overall_timeout {
struct dds_security_fsm *fsm;
dds_time_t endtime;
dds_security_fsm_action func;
struct fsm_overall_timeout *next;
struct fsm_overall_timeout *prev;
} fsm_overall_timeout;
typedef struct dds_security_fsm_context {
ddsrt_thread_t fsm_tid;
ddsrt_thread_t fsm_timeout_tid;
bool fsm_teardown;
fsm_event *fsm_queue;
fsm_overall_timeout *fsm_overall_timeouts;
ddsrt_mutex_t fsm_fsms_mutex;
dds_security_fsm *fsm_fsms;
fsm_state_timeout *fsm_next_state_timeout;
ddsrt_mutex_t fsm_state_timeout_mutex;
ddsrt_cond_t fsm_event_cond;
ddsrt_mutex_t fsm_event_cond_mutex;
// Overall timeout guard
ddsrt_cond_t fsm_overall_timeout_cond;
ddsrt_mutex_t fsm_overall_timeout_cond_mutex;
} dds_security_fsm_context;
static dds_security_fsm_context *fsm_context = NULL;
// Thread safe initialization of the Generic State Machine Utility
bool dds_security_fsm_initialized = false;
static ddsrt_atomic_uint32_t _fsmInitCount = DDSRT_ATOMIC_UINT32_INIT(0);
static void fsm_dispatch(struct dds_security_fsm *fsm, int event_id, int lifo) {
fsm_event *event;
dds_security_fsm_context *context;
assert(fsm);
if (fsm->size < 0) {
/* This fsm is cleaned up (but probably not freed yet).
* So, ignore the new event. */
return;
}
context = fsm->context;
assert(context);
if (fsm->debug_func) {
fsm->debug_func(fsm,
lifo ? DDS_SECURITY_FSM_DEBUG_ACT_DISPATCH_DIRECT : DDS_SECURITY_FSM_DEBUG_ACT_DISPATCH,
fsm->current, event_id, fsm->arg);
}
event = ddsrt_malloc(sizeof(fsm_event));
event->fsm = fsm;
event->event_id = event_id;
event->next = NULL;
event->prev = NULL;
if (lifo) {
/* Insert event at the top of the event list */
if (context->fsm_queue) {
context->fsm_queue->prev = event;
}
event->next = context->fsm_queue;
context->fsm_queue = event;
} else {
/* Insert FIFO event */
if (context->fsm_queue) {
fsm_event *last = context->fsm_queue;
while (last->next != NULL ) {
last = last->next;
}
last->next = event;
event->prev = last;
} else {
context->fsm_queue = event;
}
}
}
static void fsm_set_next_state_timeout(dds_security_fsm_context *context,
dds_security_fsm *ignore) {
dds_security_fsm *fsm;
ddsrt_mutex_lock(&context->fsm_event_cond_mutex);
// reset the current time
context->fsm_next_state_timeout->endtime = DDS_NEVER;
context->fsm_next_state_timeout->fsm = NULL;
fsm = context->fsm_fsms;
while (fsm) {
if ((fsm->current) && (fsm->current->timeout) && (fsm != ignore)) {
// first set the endtime of this state (if not set)
if (fsm->current_state_endtime == 0) {
fsm->current_state_endtime = ddsrt_time_add_duration(dds_time(),
fsm->current->timeout);
}
// Initialize the current endtime
if (context->fsm_next_state_timeout->fsm == NULL) {
context->fsm_next_state_timeout->endtime = fsm->current_state_endtime;
context->fsm_next_state_timeout->fsm = fsm;
} else if (fsm->current_state_endtime
< context->fsm_next_state_timeout->endtime) {
context->fsm_next_state_timeout->endtime = fsm->current_state_endtime;
context->fsm_next_state_timeout->fsm = fsm;
}
}
fsm = fsm->next;
}
ddsrt_mutex_unlock(&context->fsm_event_cond_mutex);
}
static void fsm_state_change(fsm_event *event) {
dds_security_fsm *fsm = event->fsm;
dds_security_fsm_context *context = fsm->context;
int event_id = event->event_id;
int i, j;
if (fsm->debug_func) {
fsm->debug_func(fsm, DDS_SECURITY_FSM_DEBUG_ACT_HANDLING, fsm->current, event_id,
fsm->arg);
}
for (i = 0; !context->fsm_teardown && i < fsm->size; i++) {
if ((fsm->transitions[i].begin == fsm->current)
&& (fsm->transitions[i].event_id == event_id)) {
/* Transition. */
if (fsm->transitions[i].func) {
fsm->transitions[i].func(fsm, fsm->arg);
}
/* New state. */
fsm->current = fsm->transitions[i].end;
if (fsm->current) {
if (fsm->current->func) {
fsm->current->func(fsm, fsm->arg);
}
/* Reset timeout. */
fsm->current_state_endtime = ddsrt_time_add_duration(dds_time(),
fsm->current->timeout);
/* Check if an auto transition is to be dispatched */
for (j = 0; j < fsm->size; j++) {
if ((fsm->transitions[j].begin == fsm->current)
&& (fsm->transitions[j].event_id == DDS_SECURITY_FSM_EVENT_AUTO)) {
dds_security_fsm_dispatch_direct(fsm, DDS_SECURITY_FSM_EVENT_AUTO);
}
}
}
}
}
}
static uint32_t
fsm_thread(void *a) {
dds_security_fsm_context *context = a;
dds_duration_t dur_to_wait;
dds_time_t now = DDS_TIME_INVALID;
fsm_event *event;
while (!context->fsm_teardown) {
event = NULL;
ddsrt_mutex_lock(&context->fsm_event_cond_mutex);
if (!context->fsm_queue) {
if (context->fsm_next_state_timeout->endtime == DDS_NEVER) {
dur_to_wait = DDS_NEVER;
} else {
now = dds_time();
dur_to_wait = context->fsm_next_state_timeout->endtime - now;
}
if (dur_to_wait > 0) {
if (ddsrt_cond_waitfor(&context->fsm_event_cond,
&context->fsm_event_cond_mutex, dur_to_wait) == false) {
if (context->fsm_next_state_timeout->fsm) {
/* Next timeout could have changed. */
if (context->fsm_next_state_timeout->endtime != DDS_NEVER
&& (context->fsm_next_state_timeout->endtime - now <= 0)) {
fsm_dispatch(context->fsm_next_state_timeout->fsm,
DDS_SECURITY_FSM_EVENT_TIMEOUT, 1);
}
}
}
} else {
if (context->fsm_next_state_timeout->fsm) {
fsm_dispatch(context->fsm_next_state_timeout->fsm,
DDS_SECURITY_FSM_EVENT_TIMEOUT, 1);
}
}
} else {
event = context->fsm_queue;
context->fsm_queue = context->fsm_queue->next;
if (context->fsm_queue) {
context->fsm_queue->prev = NULL;
}
ddsrt_atomic_inc32(&(event->fsm->ref_cnt));
}
ddsrt_mutex_unlock(&context->fsm_event_cond_mutex);
if (event) {
fsm_state_change(event);
if (ddsrt_atomic_dec32_nv(&(event->fsm->ref_cnt)) == 0) {
ddsrt_free(event->fsm);
}
ddsrt_free(event);
}
fsm_set_next_state_timeout(context, NULL);
}
return 0;
}
static fsm_overall_timeout *
fsm_get_first_overall_timeout(dds_security_fsm_context *context) {
fsm_overall_timeout *timeout;
fsm_overall_timeout *first_timeout;
dds_time_t first_time = DDS_NEVER;
timeout = context->fsm_overall_timeouts;
first_timeout = context->fsm_overall_timeouts;
while (timeout) {
if (timeout->endtime < first_time) {
first_time = timeout->endtime;
first_timeout = timeout;
}
timeout = timeout->next;
}
return first_timeout;
}
static void fsm_remove_overall_timeout_from_list(dds_security_fsm_context *context,
fsm_overall_timeout *timeout) {
fsm_overall_timeout *tmp_next_timeout;
fsm_overall_timeout *tmp_prev_timeout;
if (timeout) {
tmp_next_timeout = timeout->next;
tmp_prev_timeout = timeout->prev;
if (tmp_prev_timeout) {
tmp_prev_timeout->next = tmp_next_timeout;
}
if (tmp_next_timeout) {
tmp_next_timeout->prev = tmp_prev_timeout;
}
if (timeout == context->fsm_overall_timeouts) {
context->fsm_overall_timeouts = tmp_next_timeout;
}
ddsrt_free(timeout);
timeout = NULL;
}
}
static uint32_t
fsm_run_timeout(void *arg) {
dds_security_fsm_context *context = arg;
dds_return_t result;
fsm_overall_timeout *to;
dds_time_t time_to_wait;
dds_time_t now;
while (!context->fsm_teardown) {
ddsrt_mutex_lock(&context->fsm_overall_timeout_cond_mutex);
to = fsm_get_first_overall_timeout(context);
if (to) {
struct dds_security_fsm *fsm = to->fsm;
ddsrt_atomic_inc32(&(fsm->ref_cnt));
result = DDS_RETCODE_TIMEOUT;
now = dds_time();
if (to->endtime > now) {
time_to_wait = to->endtime - now;
result = ddsrt_cond_waitfor(&context->fsm_overall_timeout_cond,
&context->fsm_overall_timeout_cond_mutex, time_to_wait);
}
if (result == DDS_RETCODE_TIMEOUT) {
/* Prevent calling timeout when the fsm has been cleaned. */
dds_security_fsm_action func = to->func;
fsm_remove_overall_timeout_from_list(context, to);
if (fsm->size > 0) {
ddsrt_mutex_unlock(&context->fsm_overall_timeout_cond_mutex);
func(fsm, fsm->arg);
ddsrt_mutex_lock(&context->fsm_overall_timeout_cond_mutex);
}
}
if (ddsrt_atomic_dec32_nv(&(fsm->ref_cnt)) == 0) {
ddsrt_free(fsm);
}
} else {
ddsrt_cond_wait(&context->fsm_overall_timeout_cond,
&context->fsm_overall_timeout_cond_mutex);
}
ddsrt_mutex_unlock(&context->fsm_overall_timeout_cond_mutex);
}
return 0;
}
static void fsm_remove_fsm_list(dds_security_fsm *fsm) {
dds_security_fsm_context *context;
dds_security_fsm *tmp_next_fsm;
dds_security_fsm *tmp_prev_fsm;
if (fsm) {
context = fsm->context;
ddsrt_mutex_lock(&context->fsm_fsms_mutex);
tmp_next_fsm = fsm->next;
tmp_prev_fsm = fsm->prev;
if (tmp_prev_fsm) {
tmp_prev_fsm->next = tmp_next_fsm;
}
if (tmp_next_fsm) {
tmp_next_fsm->prev = tmp_prev_fsm;
}
if (fsm == context->fsm_fsms) {
context->fsm_fsms = tmp_next_fsm;
}
ddsrt_mutex_unlock(&context->fsm_fsms_mutex);
ddsrt_mutex_lock(&context->fsm_overall_timeout_cond_mutex);
ddsrt_cond_signal(&context->fsm_overall_timeout_cond);
ddsrt_mutex_unlock(&context->fsm_overall_timeout_cond_mutex);
}
}
static ddsrt_thread_t fsm_thread_create( const char *name,
ddsrt_thread_routine_t f, void *arg) {
ddsrt_thread_t tid;
ddsrt_threadattr_t threadAttr;
ddsrt_threadattr_init(&threadAttr);
if (ddsrt_thread_create(&tid, name, &threadAttr, f, arg) != DDS_RETCODE_OK) {
memset(&tid, 0, sizeof(ddsrt_thread_t));
}
return tid;
}
#ifdef AT_PROC_EXIT_IMPLEMENTED
static void fsm_thread_destroy( ddsrt_thread_t tid) {
uint32_t thread_result;
(void) ddsrt_thread_join( tid, &thread_result);
}
#endif
struct dds_security_fsm_context *
dds_security_fsm_context_create( dds_security_fsm_thread_create_func thr_create_func) {
struct dds_security_fsm_context *context;
context = ddsrt_malloc(sizeof(*context));
context->fsm_next_state_timeout = ddsrt_malloc(sizeof(fsm_state_timeout));
context->fsm_next_state_timeout->endtime = DDS_NEVER;
context->fsm_next_state_timeout->fsm = NULL;
context->fsm_teardown = false;
context->fsm_queue = NULL;
context->fsm_overall_timeouts = NULL;
context->fsm_fsms = NULL;
(void) ddsrt_mutex_init( &context->fsm_fsms_mutex );
// Overall timeout guard
(void) ddsrt_mutex_init( &context->fsm_overall_timeout_cond_mutex );
(void) ddsrt_cond_init( &context->fsm_overall_timeout_cond );
// State timeouts
(void) ddsrt_mutex_init(&context->fsm_state_timeout_mutex );
// Events
(void) ddsrt_mutex_init(&context->fsm_event_cond_mutex );
(void) ddsrt_cond_init(&context->fsm_event_cond );
context->fsm_tid = thr_create_func( "dds_security_fsm", fsm_thread, context);
context->fsm_timeout_tid = thr_create_func( "dds_security_fsm_timeout",
fsm_run_timeout, context);
return context;
}
void dds_security_fsm_context_destroy(dds_security_fsm_context *context,
dds_security_fsm_thread_destroy_func thr_destroy_func) {
if (context) {
context->fsm_teardown = true;
ddsrt_mutex_lock( &context->fsm_overall_timeout_cond_mutex);
ddsrt_cond_signal( &context->fsm_overall_timeout_cond);
ddsrt_mutex_unlock( &context->fsm_overall_timeout_cond_mutex);
ddsrt_mutex_lock(&context->fsm_event_cond_mutex);
ddsrt_cond_signal(&context->fsm_event_cond);
ddsrt_mutex_unlock(&context->fsm_event_cond_mutex);
thr_destroy_func( context->fsm_tid);
ddsrt_mutex_destroy(&context->fsm_event_cond_mutex);
ddsrt_cond_destroy(&context->fsm_event_cond);
thr_destroy_func( context->fsm_timeout_tid);
ddsrt_mutex_destroy(&context->fsm_fsms_mutex);
ddsrt_mutex_destroy(&context->fsm_overall_timeout_cond_mutex);
ddsrt_cond_destroy(&context->fsm_overall_timeout_cond);
ddsrt_free(context->fsm_next_state_timeout);
}
}
#ifdef AT_PROC_EXIT_IMPLEMENTED
static void fsm_fini(void) {
dds_security_fsm_context_destroy(fsm_context, NULL, fsm_thread_destroy);
/* os_osExit(); ???? */
}
#endif
static bool fsm_init_once(void) {
bool ret = true;
uint32_t initCount;
initCount = ddsrt_atomic_inc32_nv(&_fsmInitCount);
if (initCount == 1) {
assert( dds_security_fsm_initialized == false );
/* ddsrt_osInit(); ??? */
fsm_context = dds_security_fsm_context_create( fsm_thread_create);
if (fsm_context) {
/* os_procAtExit( fsm_fini ); ??? */
dds_security_fsm_initialized = true;
} else {
ret = false;
}
} else {
if (dds_security_fsm_initialized == false) {
/* Another thread is currently initializing the fsm. Since
* both results (osr_fsm and osr_timeout) should be ddsrt_resultSuccess
* a sleep is performed, to ensure that (if succeeded) successive
* init calls will also actually pass.
*/
dds_sleepfor( DDS_MSECS( 100 ));
}
if (dds_security_fsm_initialized == false) {
/* Initialization did not succeed, undo increment and return error */
initCount = ddsrt_atomic_dec32_nv(&_fsmInitCount);
ret = false;
}
}
return ret;
}
static int /* 1 = ok, other = error */
fsm_validate(const dds_security_fsm_transition *transitions, int size) {
int i;
for (i = 0; i < size; i++) {
/* It needs to have a start. */
if ((transitions[i].begin == NULL )
&& (transitions[i].event_id == DDS_SECURITY_FSM_EVENT_AUTO)) {
return 1;
}
}
return 0;
}
struct dds_security_fsm *
dds_security_fsm_create(struct dds_security_fsm_context *context,
const dds_security_fsm_transition *transitions, int size, void *arg) {
struct dds_security_fsm* fsm = NULL;
struct dds_security_fsm_context *ctx = NULL;
assert(transitions);
assert(size > 0);
if (context == NULL) {
if (fsm_init_once()) {
ctx = fsm_context;
}
} else {
ctx = context;
}
if (ctx) {
if (fsm_validate(transitions, size) == 1) {
fsm = ddsrt_malloc(sizeof(struct dds_security_fsm));
fsm->transitions = transitions;
fsm->size = size;
fsm->arg = arg;
fsm->current = NULL;
fsm->debug_func = NULL;
fsm->next = NULL;
fsm->prev = NULL;
fsm->context = ctx;
ddsrt_atomic_st32( &fsm->ref_cnt, 1 );
fsm->current_state_endtime = 0;
ddsrt_mutex_lock(&fsm->context->fsm_fsms_mutex);
if (fsm->context->fsm_fsms) {
dds_security_fsm *last = fsm->context->fsm_fsms;
while (last->next != NULL ) {
last = last->next;
}
last->next = fsm;
fsm->prev = last;
} else {
fsm->context->fsm_fsms = fsm;
}
ddsrt_mutex_unlock(&fsm->context->fsm_fsms_mutex);
}
}
return fsm;
}
void dds_security_fsm_start(struct dds_security_fsm *fsm) {
assert(fsm);
dds_security_fsm_dispatch(fsm, DDS_SECURITY_FSM_EVENT_AUTO);
}
void dds_security_fsm_set_timeout(struct dds_security_fsm *fsm, dds_security_fsm_action func,
dds_time_t timeout) {
fsm_overall_timeout *to;
dds_security_fsm_context *context;
assert(fsm);
context = fsm->context;
assert(context);
to = ddsrt_malloc(sizeof(fsm_overall_timeout));
to->fsm = fsm;
to->func = func;
to->endtime = ddsrt_time_add_duration( dds_time(), timeout);
to->next = NULL;
to->prev = NULL;
ddsrt_mutex_lock(&context->fsm_overall_timeout_cond_mutex);
if (context->fsm_overall_timeouts) {
fsm_overall_timeout *last = context->fsm_overall_timeouts;
while (last->next != NULL ) {
last = last->next;
}
last->next = to;
to->prev = last;
} else {
context->fsm_overall_timeouts = to;
}
ddsrt_cond_signal(&context->fsm_overall_timeout_cond);
ddsrt_mutex_unlock(&context->fsm_overall_timeout_cond_mutex);
}
void dds_security_fsm_set_debug(struct dds_security_fsm *fsm, dds_security_fsm_debug func) {
dds_security_fsm_context *context;
assert(fsm);
context = fsm->context;
assert(context);
ddsrt_mutex_lock(&context->fsm_overall_timeout_cond_mutex);
fsm->debug_func = func;
ddsrt_mutex_unlock(&context->fsm_overall_timeout_cond_mutex);
}
void dds_security_fsm_dispatch(struct dds_security_fsm *fsm, int32_t event_id) {
dds_security_fsm_context *context;
assert(fsm);
context = fsm->context;
assert(context);
ddsrt_mutex_lock(&context->fsm_event_cond_mutex);
fsm_dispatch(fsm, event_id, 0);
ddsrt_cond_signal(&context->fsm_event_cond);
ddsrt_mutex_unlock(&context->fsm_event_cond_mutex);
}
void dds_security_fsm_dispatch_direct(struct dds_security_fsm *fsm, int32_t event_id) {
dds_security_fsm_context *context;
assert(fsm);
context = fsm->context;
assert(context);
ddsrt_mutex_lock(&context->fsm_event_cond_mutex);
fsm_dispatch(fsm, event_id, 1);
ddsrt_cond_signal(&context->fsm_event_cond);
ddsrt_mutex_unlock(&context->fsm_event_cond_mutex);
}
const dds_security_fsm_state*
dds_security_fsm_current_state(struct dds_security_fsm *fsm) {
assert(fsm);
return fsm->current;
}
void dds_security_fsm_cleanup(struct dds_security_fsm *fsm) {
dds_security_fsm_context *context;
fsm_event *event;
fsm_event *tmp_prev_event;
fsm_event *tmp_next_event;
fsm_overall_timeout *timeout;
assert(fsm);
context = fsm->context;
assert(context);
// Signal the timeout thread.
// First hold to lock to the overall timeout list
// so that the next timeout can't be determined until
// we've done removing the overall timeout of this fsm
// Signal the thread so that it's not using timeout structs
ddsrt_mutex_lock(&context->fsm_overall_timeout_cond_mutex);
ddsrt_cond_signal(&context->fsm_overall_timeout_cond);
timeout = context->fsm_overall_timeouts;
// Search the overall timeout of this fsm
while (timeout) {
if (timeout->fsm == fsm) {
break;
}
timeout = timeout->next;
}
fsm_remove_overall_timeout_from_list(context, timeout);
ddsrt_mutex_unlock(&context->fsm_overall_timeout_cond_mutex);
/* The current fsm could be the one that would trigger a possible timeout.
* Reset the state timeout and make sure it's not the current fsm. */
fsm_set_next_state_timeout(context, fsm);
/* Now, remove all possible events from the queue related to the fsm. */
ddsrt_mutex_lock(&context->fsm_event_cond_mutex);
event = context->fsm_queue;
while (event) {
if (event->fsm == fsm) {
tmp_next_event = event->next;
tmp_prev_event = event->prev;
if (tmp_prev_event) {
tmp_prev_event->next = tmp_next_event;
}
if (tmp_next_event) {
tmp_next_event->prev = tmp_prev_event;
}
if (event == context->fsm_queue) {
context->fsm_queue = tmp_next_event;
}
ddsrt_free(event);
event = tmp_next_event;
} else {
event = event->next;
}
}
ddsrt_cond_signal(&context->fsm_event_cond);
ddsrt_mutex_unlock(&context->fsm_event_cond_mutex);
}
void dds_security_fsm_free(struct dds_security_fsm *fsm) {
ddsrt_tid_t self = ddsrt_gettid_for_thread( ddsrt_thread_self() );
dds_security_fsm_context *context;
assert(fsm);
context = fsm->context;
assert(context);
/* Indicate termination. */
fsm->size = -1;
/* Cleanup stuff. */
dds_security_fsm_cleanup(fsm);
fsm_remove_fsm_list(fsm);
/* Is this being freed from the FSM context? */
if ((self == ddsrt_gettid_for_thread( context->fsm_tid ) )
|| (self == ddsrt_gettid_for_thread( context->fsm_timeout_tid ) ) ) {
/* Yes.
* Just reduce the reference count and let the garbage collection be
* done by the FSM context after event handling. */
ddsrt_atomic_dec32(&(fsm->ref_cnt));
} else {
/* No.
* Block the outside thread until a possible concurrent event
* has being handled. */
while (ddsrt_atomic_ld32( &(fsm->ref_cnt)) > 1) {
/* Currently, an event is still being handled for this FSM. */
dds_sleepfor( 10 * DDS_NSECS_IN_MSEC );
}
/* We have the only reference, so it's safe to free the FSM. */
ddsrt_free(fsm);
}
}

View file

@ -0,0 +1,39 @@
#
# Copyright(c) 2006 to 2019 ADLINK Technology Limited and others
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
# v. 1.0 which is available at
# http://www.eclipse.org/org/documents/edl-v10.php.
#
# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#
include (GenerateExportHeader)
include (CUnit)
set(security_core_test_sources
"tc_fsm.c"
)
add_definitions(-DDDSI_INCLUDE_SECURITY)
add_cunit_executable(cunit_security_core ${security_core_test_sources})
target_include_directories(
cunit_security_core PRIVATE
"$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/src/include/>"
"$<BUILD_INTERFACE:$<TARGET_PROPERTY:ddsrt,INTERFACE_INCLUDE_DIRECTORIES>>"
"$<BUILD_INTERFACE:$<TARGET_PROPERTY:ddsc,INTERFACE_INCLUDE_DIRECTORIES>>"
"$<BUILD_INTERFACE:$<TARGET_PROPERTY:security_core,INTERFACE_INCLUDE_DIRECTORIES>>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/../../../../core/ddsi/include>"
)
target_link_libraries(cunit_security_core PRIVATE ddsc security_api)
target_include_directories(cunit_security_core PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
# configure_file("config_env.h.in" "config_env.h")

View file

@ -0,0 +1,766 @@
#include "dds/security/core/dds_security_fsm.h"
#include "dds/ddsrt/sync.h"
#include "dds/ddsrt/misc.h"
#include <stdio.h>
#include <stdbool.h>
#include "CUnit/CUnit.h"
#include "CUnit/Test.h"
#define CHECK_BIT(var, pos) ((var) & (1<<(pos)))
#define FSM_AUTH_ARG 10
#define DB_TC_PRINT_DEBUG (false)
static struct dds_security_fsm *fsm_auth;
static struct dds_security_fsm *fsm_test;
static struct dds_security_fsm *fsm_timeout;
static struct dds_security_fsm *fsm_timeout2;
static struct dds_security_fsm *fsm_timeout3;
static uint32_t visited_auth = 0;
static uint32_t visited_test = 0;
static uint32_t visited_timeout = 0;
uint32_t correct_fsm = 0;
uint32_t correct_arg = 0;
uint32_t correct_fsm_timeout = 0;
uint32_t correct_arg_timeout = 0;
static ddsrt_cond_t stop_timeout_cond;
static ddsrt_mutex_t stop_timeout_cond_mutex;
static uint32_t stop_timeout_cond_cnt = 0;
static int validate_remote_identity_first = 1;
static int begin_handshake_reply_first = 1;
static int do_stuff_counter = 0;
static int do_other_stuff_counter = 0;
/*
* Authentication State Machine properties and methods
*/
typedef enum {
VALIDATION_PENDING_RETRY,
VALIDATION_FAILED,
VALIDATION_OK,
VALIDATION_OK_FINAL_MESSAGE,
VALIDATION_PENDING_HANDSHAKE_MESSAGE,
VALIDATION_PENDING_HANDSHAKE_REQUEST,
PluginReturn_MAX
} PluginReturn;
static PluginReturn validate_remote_identity(void) {
if (DB_TC_PRINT_DEBUG) {
printf("validate_remote_identity - %d\n", validate_remote_identity_first);
}
if (validate_remote_identity_first) {
validate_remote_identity_first = 0;
return VALIDATION_PENDING_RETRY;
}
return VALIDATION_PENDING_HANDSHAKE_MESSAGE;
}
static PluginReturn begin_handshake_reply(void) {
if (DB_TC_PRINT_DEBUG) {
printf("begin_handshake_reply - %d\n", begin_handshake_reply_first);
}
if (begin_handshake_reply_first) {
begin_handshake_reply_first = 0;
return VALIDATION_PENDING_RETRY;
}
return VALIDATION_OK_FINAL_MESSAGE;
}
static PluginReturn get_shared_secret(void) {
return VALIDATION_OK;
}
/* State actions. */
static void fsm_validate_remote_identity(struct dds_security_fsm *fsm, void *arg) {
PluginReturn ret;
DDSRT_UNUSED_ARG(arg);
ret = validate_remote_identity();
if (DB_TC_PRINT_DEBUG) {
printf("[%p] State %s (ret %d)\n", fsm, __FUNCTION__, (int) ret);
}
dds_security_fsm_dispatch(fsm, (int32_t) ret);
}
static void fsm_begin_handshake_reply(struct dds_security_fsm *fsm, void *arg) {
PluginReturn ret;
DDSRT_UNUSED_ARG(arg);
ret = begin_handshake_reply();
if (ret == VALIDATION_OK_FINAL_MESSAGE) {
ret = get_shared_secret();
}
if (DB_TC_PRINT_DEBUG) {
printf("[%p] State %s (ret %d)\n", fsm, __FUNCTION__, (int) ret);
}
dds_security_fsm_dispatch(fsm, (int32_t) ret);
}
/* A few states from the handshake state-machine. */
static dds_security_fsm_state StateValidateRemoteIdentity = {
fsm_validate_remote_identity, 0};
static dds_security_fsm_state StateValRemIdentityRetryWait = {NULL, 100000000};
static dds_security_fsm_state StateHandshakeInitMessageWait = {NULL, 0};
static dds_security_fsm_state StateBeginHandshakeReply = {fsm_begin_handshake_reply, 0};
static dds_security_fsm_state StateBeginHsReplyWait = {NULL, 100000000};
static void a(struct dds_security_fsm *fsm, void *arg) {
int *fsm_arg;
if (DB_TC_PRINT_DEBUG) {
printf("[%p] Transition %s\n", fsm, __FUNCTION__);
}
if (arg != NULL) {
fsm_arg = (int *) arg;
if (*fsm_arg == FSM_AUTH_ARG) {
correct_arg = 1;
} else {
correct_arg = 0;
}
}
if (fsm == fsm_auth) {
correct_fsm = 1;
} else {
correct_fsm = 0;
}
visited_auth |= 1UL << 0;
}
static void b(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG)
printf("[%p] Transition %s\n", fsm, __FUNCTION__);
visited_auth |= 1UL << 1;
}
static void c(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG)
printf("[%p] Transition %s\n", fsm, __FUNCTION__);
visited_auth |= 1UL << 2;
}
static void d(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG)
printf("[%p] Transition %s\n", fsm, __FUNCTION__);
visited_auth |= 1UL << 3;
}
static void e(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG)
printf("[%p] Transition %s\n", fsm, __FUNCTION__);
visited_auth |= 1UL << 4;
}
static void f(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG)
printf("[%p] Transition %s\n", fsm, __FUNCTION__);
visited_auth |= 1UL << 5;
}
static void g(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG)
printf("[%p] Transition %s\n", fsm, __FUNCTION__);
visited_auth |= 1UL << 6;
}
static void h(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG)
printf("[%p] Transition %s\n", fsm, __FUNCTION__);
visited_auth |= 1UL << 7;
}
#define SHM_MSG_RECEIVED (PluginReturn_MAX + 1)
/*
* .--.
* |##|--------------------------------------.
* '--' a() v
* .----------------------------------------------------.
* | StateValidateRemoteIdentity |
* |----------------------------------------------------|
* .------>| fsm_validate_remote_identity() |
* | | - dispatch VALIDATION_PENDING_RETRY |
* 100ms| | - dispatch VALIDATION_PENDING_HANDSHAKE_MESSAGE |
* d() | '----------------------------------------------------'
* | VALIDATION_PENDING_RETRY| | VALIDATION_PENDING_HANDSHAKE_MESSAGE
* | b() | | c()
* | | |
* .------------------------------. | | .-------------------------------.
* | StateValRemIdentityRetryWait | | | | StateHandshakeInitMessageWait |
* |------------------------------|<----------' '------>|-------------------------------|
* '------------------------------' '-------------------------------'
* SHM_MSG_RECEIVED |
* e() |
* v
* .----------------------------------------.
* VALIDATION_PENDING_RETRY | StateBeginHandshakeReply |
* f() |----------------------------------------|
* .-------------------------| fsm_begin_handshake_reply() |
* | | - dispatch VALIDATION_PENDING_RETRY |
* v | - dispatch VALIDATION_OK |
* .-----------------------. 100ms '----------------------------------------'
* | StateBeginHsReplyWait | h() ^ VALIDATION_OK |
* |-----------------------|-----------------------' g() |
* '-----------------------' v
* .-.
* '-'
*/
dds_security_fsm_transition HandshakeTransistions[] =
{{NULL, DDS_SECURITY_FSM_EVENT_AUTO, a, &StateValidateRemoteIdentity}, // NULL state is the start state
{&StateValidateRemoteIdentity, VALIDATION_PENDING_RETRY, b,
&StateValRemIdentityRetryWait},
{&StateValidateRemoteIdentity,
VALIDATION_PENDING_HANDSHAKE_MESSAGE, c,
&StateHandshakeInitMessageWait},
{&StateValRemIdentityRetryWait,
DDS_SECURITY_FSM_EVENT_TIMEOUT, d, &StateValidateRemoteIdentity},
{
&StateHandshakeInitMessageWait, SHM_MSG_RECEIVED, e,
&StateBeginHandshakeReply},
{&StateBeginHandshakeReply,
VALIDATION_PENDING_RETRY, f, &StateBeginHsReplyWait},
{
&StateBeginHandshakeReply, VALIDATION_OK, g, NULL}, // Reaching NULL means end of state-diagram
{&StateBeginHsReplyWait, DDS_SECURITY_FSM_EVENT_TIMEOUT, h,
&StateBeginHandshakeReply},};
/*
* Example State Machine properties and methods
*/
typedef enum {
eventX, eventY, eventZ,
} test_events;
/* The functions called from the state-machine. */
static void doStart(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG)
printf("Transition %s\n", __FUNCTION__);
visited_test |= 1UL << 0;
}
static void doRestart(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG)
printf("Transition %s\n", __FUNCTION__);
visited_test |= 1UL << 1;
}
static void doEventStuff(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG)
printf("Transition %s\n", __FUNCTION__);
visited_test |= 1UL << 4;
}
static void doStuff(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG) {
printf("Transition %s - %d\n", __FUNCTION__, do_stuff_counter);
}
visited_test |= 1UL << 2;
if (do_stuff_counter == 0) {
dds_security_fsm_dispatch(fsm, eventZ);
} else if (do_stuff_counter == 2) {
dds_security_fsm_dispatch(fsm, eventY);
}
++do_stuff_counter;
}
static void doOtherStuff(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG) {
printf("Transition %s - %d\n", __FUNCTION__, do_other_stuff_counter);
}
visited_test |= 1UL << 3;
if (do_other_stuff_counter == 0) {
dds_security_fsm_dispatch(fsm, DDS_SECURITY_FSM_EVENT_AUTO);
}
if (do_other_stuff_counter == 1) {
dds_security_fsm_dispatch(fsm, eventY);
} else if (do_other_stuff_counter == 2) {
dds_security_fsm_dispatch(fsm, eventX);
}
++do_other_stuff_counter;
}
dds_security_fsm_state StateA = {doStuff, 0};
dds_security_fsm_state StateB = {doStuff, 100000000};
dds_security_fsm_state StateC = {NULL, 0};
dds_security_fsm_state StateD = {doOtherStuff, 0};
dds_security_fsm_transition transitions[] = {
{NULL, DDS_SECURITY_FSM_EVENT_AUTO, doStart, &StateA}, // NULL state is the start state
{&StateA, eventZ, NULL, &StateB},
{&StateA, eventY, doOtherStuff, &StateC},
{&StateB, eventX, NULL, NULL}, // Reaching NULL means end of state-diagram
{&StateB, eventZ, doRestart, &StateA},
{&StateC, DDS_SECURITY_FSM_EVENT_AUTO,
doEventStuff, &StateD},
{&StateD, eventY, doEventStuff, &StateD},
{
&StateD, eventX, doStuff, NULL}, // Reaching NULL means end of state-diagram
};
/*
* Timeout State Machine properties and methods
*/
typedef enum {
eventToTimeout, eventToEnd,
} timeout_events;
/* The functions callld from the state-machine. */
static void doInterupt(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG) {
printf("Transition %s\n", __FUNCTION__);
}
visited_timeout |= 1UL << 0;
}
static void doTimeout(struct dds_security_fsm *fsm, void *arg) {
dds_duration_t delay4 = 4 * DDS_NSECS_IN_SEC;
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG) {
printf("Transition >>>> %s %d\n", __FUNCTION__, stop_timeout_cond_cnt);
}
visited_timeout |= 1UL << 1;
stop_timeout_cond_cnt++;
ddsrt_mutex_lock(&stop_timeout_cond_mutex);
(void) ddsrt_cond_waitfor(&stop_timeout_cond, &stop_timeout_cond_mutex,
delay4);
ddsrt_mutex_unlock(&stop_timeout_cond_mutex);
stop_timeout_cond_cnt--;
if (DB_TC_PRINT_DEBUG) {
printf("Transition <<<< %s %d\n", __FUNCTION__, stop_timeout_cond_cnt);
}
dds_security_fsm_dispatch(fsm, eventToTimeout);
}
static void TimeoutCallback(struct dds_security_fsm *fsm, void *arg) {
int *fsm_arg;
if (DB_TC_PRINT_DEBUG) {
printf("TimeoutCallback\n");
}
visited_timeout |= 1UL << 2;
if (arg != NULL) {
fsm_arg = (int *) arg;
if (*fsm_arg == FSM_AUTH_ARG) {
correct_arg_timeout = 1;
} else {
correct_arg_timeout = 0;
}
}
if (fsm == fsm_timeout) {
correct_fsm_timeout = 1;
} else {
correct_fsm_timeout = 0;
}
}
static void TimeoutCallback2(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
if (DB_TC_PRINT_DEBUG) {
printf("TimeoutCallback2\n");
}
visited_timeout |= 1UL << 3;
}
dds_security_fsm_state StateTimeout = {doTimeout, 0};
dds_security_fsm_state StateInterupt = {doInterupt, 0};
dds_security_fsm_transition timeout_transitions[] = {{NULL, DDS_SECURITY_FSM_EVENT_AUTO, NULL,
&StateTimeout}, // NULL state is the start state
{&StateTimeout, eventToTimeout, NULL, &StateInterupt},
{&StateInterupt,
eventToEnd, NULL, NULL}, // Reaching NULL means end of state-diagram
};
static dds_time_t time0 = 0;
static dds_time_t time1 = 0;
static dds_time_t time2 = 0;
static dds_time_t time3 = 0;
static void StateParTime1(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
time1 = dds_time();
}
static void StateParTime2(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
time2 = dds_time();
}
static void StateParTime3(struct dds_security_fsm *fsm, void *arg) {
DDSRT_UNUSED_ARG(fsm);
DDSRT_UNUSED_ARG(arg);
time3 = dds_time();
}
dds_security_fsm_state StateParTimeout1 = {NULL, DDS_NSECS_IN_SEC};
dds_security_fsm_state StateParTimeout2 = {NULL, 2 * DDS_NSECS_IN_SEC};
dds_security_fsm_state StateParTimeout3 = {NULL, DDS_NSECS_IN_SEC};
dds_security_fsm_transition par_timeout_transitions_1[] = {{NULL, DDS_SECURITY_FSM_EVENT_AUTO,
NULL, &StateParTimeout1}, // NULL state is the start state
{&StateParTimeout1, DDS_SECURITY_FSM_EVENT_TIMEOUT, &StateParTime1, NULL}, // Reaching NULL means end of state-diagram
};
dds_security_fsm_transition par_timeout_transitions_2[] = {{NULL, DDS_SECURITY_FSM_EVENT_AUTO,
NULL, &StateParTimeout2}, // NULL state is the start state
{&StateParTimeout2, DDS_SECURITY_FSM_EVENT_TIMEOUT, &StateParTime2, NULL}, // Reaching NULL means end of state-diagram
};
dds_security_fsm_transition par_timeout_transitions_3[] = {{NULL, DDS_SECURITY_FSM_EVENT_AUTO,
NULL, &StateParTimeout3}, // NULL state is the start state
{&StateParTimeout3, DDS_SECURITY_FSM_EVENT_TIMEOUT, &StateParTime3, NULL}, // Reaching NULL means end of state-diagram
};
int fsm_arg = FSM_AUTH_ARG;
dds_time_t delay1 = DDS_NSECS_IN_SEC;
dds_time_t delay2 = 2 * DDS_NSECS_IN_SEC;
dds_time_t delay30 = 30 * DDS_NSECS_IN_SEC;
int timeout;
static const dds_duration_t msec100 = 100 * DDS_NSECS_IN_MSEC;
static void init_testcase(void) {
(void) ddsrt_mutex_init(&stop_timeout_cond_mutex);
(void) ddsrt_cond_init(&stop_timeout_cond);
}
static void fini_testcase(void) {
ddsrt_cond_destroy(&stop_timeout_cond);
ddsrt_mutex_destroy(&stop_timeout_cond_mutex);
}
CU_Test(ddssec_fsm, create, .init = init_testcase, .fini = fini_testcase) {
/*
* Test single running state machine
* Check creation of a single State Machine
*/
fsm_auth = dds_security_fsm_create(NULL, HandshakeTransistions,
sizeof(HandshakeTransistions) / sizeof(HandshakeTransistions[0]),
&fsm_arg);
CU_ASSERT_FATAL(fsm_auth != NULL)
// set a delay that doesn't expire. Should be terminate when fsm is freed.
dds_security_fsm_set_timeout(fsm_auth, TimeoutCallback, delay30);
dds_security_fsm_start(fsm_auth);
/**
* Check the result of one running State Machine
*/
// Wait for the last state to occur
timeout = 100; /* 10 sec */
while ((dds_security_fsm_current_state(fsm_auth) != &StateHandshakeInitMessageWait)
&& (timeout > 0)) {
dds_sleepfor(msec100);
timeout--;
}
CU_ASSERT(timeout > 0);
dds_security_fsm_dispatch(fsm_auth, SHM_MSG_RECEIVED);
timeout = 100; /* 10 sec */
while ((dds_security_fsm_current_state(fsm_auth) != NULL) && (timeout > 0)) {
dds_sleepfor(msec100);
timeout--;
}
CU_ASSERT(timeout > 0);
CU_ASSERT(
CHECK_BIT(visited_auth, 0) && CHECK_BIT(visited_auth, 1) && CHECK_BIT(visited_auth, 2) &&
CHECK_BIT(visited_auth, 3) && CHECK_BIT(visited_auth, 4) && CHECK_BIT(visited_auth, 5) &&
CHECK_BIT(visited_auth, 6) && CHECK_BIT(visited_auth, 7));
/*
* "Check correct callback parameter passing (from fsm to user defined methods) ");
*/
CU_ASSERT(correct_arg && correct_fsm);
dds_security_fsm_free(fsm_auth);
/* Check whether timeout callback has NOT been invoked */
CU_ASSERT(visited_timeout == 0);
}
/*
* Test multiple (2) running state machines
*/
CU_Test(ddssec_fsm, multiple, .init = init_testcase, .fini = fini_testcase) {
/*Check creation of multiple (2) State Machines*/
validate_remote_identity_first = 0;
begin_handshake_reply_first = 0;
visited_auth = 0;
fsm_auth = dds_security_fsm_create(NULL, HandshakeTransistions,
sizeof(HandshakeTransistions) / sizeof(HandshakeTransistions[0]), NULL);
fsm_test = dds_security_fsm_create(NULL, transitions,
sizeof(transitions) / sizeof(transitions[0]), NULL);
CU_ASSERT_FALSE(fsm_auth == NULL || fsm_test == NULL);
dds_security_fsm_start(fsm_auth);
dds_security_fsm_start(fsm_test);
/*Check the results of multiple running State Machines */
// Wait for the last state to occur
timeout = 100; /* 10 sec */
while ((dds_security_fsm_current_state(fsm_auth) != &StateHandshakeInitMessageWait)
&& (timeout > 0)) {
dds_sleepfor(100 * DDS_NSECS_IN_MSEC);
timeout--;
}
CU_ASSERT_FATAL(timeout > 0);
timeout = 100; /* 10 sec */
dds_security_fsm_dispatch(fsm_auth, SHM_MSG_RECEIVED);
while ((dds_security_fsm_current_state(fsm_auth) != NULL) && (timeout > 0)) {
dds_sleepfor(100 * DDS_NSECS_IN_MSEC);
timeout--;
}
CU_ASSERT_FATAL(timeout > 0);
// not all bits are set since we're running the state machine a second time
CU_ASSERT_FATAL(
CHECK_BIT(visited_auth, 0) && !CHECK_BIT(visited_auth, 1) && CHECK_BIT(visited_auth, 2) &&
!CHECK_BIT(visited_auth, 3) && CHECK_BIT(visited_auth, 4) && !CHECK_BIT(visited_auth, 5) &&
CHECK_BIT(visited_auth, 6) && !CHECK_BIT(visited_auth, 7));
timeout = 100; /* 10 sec */
// Wait for the last state to occur
while ((dds_security_fsm_current_state(fsm_test) != NULL) && timeout > 0) {
dds_sleepfor(100 * DDS_NSECS_IN_MSEC);
timeout--;
}
CU_ASSERT_FATAL(timeout > 0);
CU_ASSERT_FATAL(
CHECK_BIT(visited_test, 0) && CHECK_BIT(visited_test, 1) && CHECK_BIT(visited_test, 2) &&
CHECK_BIT(visited_test, 3));
dds_security_fsm_free(fsm_auth);
dds_security_fsm_free(fsm_test);
}
/**
* Check creation of State Machine for timeout purposes
*/
CU_Test(ddssec_fsm, timeout, .init = init_testcase, .fini = fini_testcase) {
/*
* Test timeout monitoring of state machines
*/
fsm_timeout = dds_security_fsm_create(NULL, timeout_transitions,
sizeof(timeout_transitions) / sizeof(timeout_transitions[0]), &fsm_arg);
CU_ASSERT(fsm_timeout != NULL);
dds_security_fsm_set_timeout(fsm_timeout, TimeoutCallback, delay1);
dds_security_fsm_start(fsm_timeout);
/*Check the result of the running State Machine for timeout purposes*/
// Wait for the last state to occur
timeout = 100; /* 10 sec */
while ((dds_security_fsm_current_state(fsm_timeout) != &StateInterupt) && (timeout > 0)) {
dds_sleepfor(100 * DDS_NSECS_IN_MSEC);
timeout--;
}
CU_ASSERT(timeout > 0);
CU_ASSERT(
CHECK_BIT(visited_timeout, 0) && CHECK_BIT(visited_timeout, 1) && CHECK_BIT(visited_timeout, 2));
CU_ASSERT(correct_arg_timeout && correct_fsm_timeout);
dds_security_fsm_free(fsm_timeout);
}
/**
* Check the double global timeout
*/
CU_Test(ddssec_fsm, double_timeout, .init = init_testcase, .fini = fini_testcase) {
visited_timeout = 0;
fsm_timeout = dds_security_fsm_create(NULL, timeout_transitions,
sizeof(timeout_transitions) / sizeof(timeout_transitions[0]), &fsm_arg);
CU_ASSERT(fsm_timeout != NULL);
fsm_timeout2 = dds_security_fsm_create(NULL, timeout_transitions,
sizeof(timeout_transitions) / sizeof(timeout_transitions[0]), &fsm_arg);
CU_ASSERT(fsm_timeout2 != NULL);
dds_security_fsm_set_timeout(fsm_timeout, TimeoutCallback, delay1);
dds_security_fsm_set_timeout(fsm_timeout2, TimeoutCallback2, delay2);
dds_security_fsm_start(fsm_timeout);
dds_security_fsm_start(fsm_timeout2);
timeout = 100; /* 10 sec */
while ((CHECK_BIT(visited_timeout, 2) == 0) && (timeout > 0)) {
dds_sleepfor(100 * DDS_NSECS_IN_MSEC);
timeout--;
}
CU_ASSERT(CHECK_BIT(visited_timeout, 2));
dds_security_fsm_cleanup(fsm_timeout);
timeout = 100; /* 10 sec */
while ((CHECK_BIT(visited_timeout, 3) == 0) && (timeout > 0)) {
dds_sleepfor(100 * DDS_NSECS_IN_MSEC);
timeout--;
}
CU_ASSERT(CHECK_BIT(visited_timeout, 3));
dds_security_fsm_free(fsm_timeout);
dds_security_fsm_free(fsm_timeout2);
ddsrt_mutex_lock(&stop_timeout_cond_mutex);
ddsrt_cond_signal(&stop_timeout_cond);
ddsrt_mutex_unlock(&stop_timeout_cond_mutex);
}
/**
* Check parallel state timeouts
*/
CU_Test(ddssec_fsm, parallel_timeout, .init = init_testcase, .fini = fini_testcase) {
visited_timeout = 0;
fsm_timeout = dds_security_fsm_create(NULL, par_timeout_transitions_1,
sizeof(par_timeout_transitions_1) / sizeof(par_timeout_transitions_1[0]),
&fsm_arg);
CU_ASSERT(fsm_timeout != NULL);
fsm_timeout2 = dds_security_fsm_create(NULL, par_timeout_transitions_2,
sizeof(par_timeout_transitions_2) / sizeof(par_timeout_transitions_2[0]),
&fsm_arg);
CU_ASSERT(fsm_timeout2 != NULL);
fsm_timeout3 = dds_security_fsm_create(NULL, par_timeout_transitions_3,
sizeof(par_timeout_transitions_3) / sizeof(par_timeout_transitions_3[0]),
&fsm_arg);
CU_ASSERT(fsm_timeout3 != NULL);
dds_duration_t delta1;
dds_duration_t delta2;
dds_duration_t delta3;
time0 = dds_time();
dds_security_fsm_start(fsm_timeout);
dds_security_fsm_start(fsm_timeout2);
dds_security_fsm_start(fsm_timeout3);
/* Wait for both to end. */
timeout = 100; /* 10 sec */
/* First, they have to be started. */
while (((dds_security_fsm_current_state(fsm_timeout) == NULL)
|| (dds_security_fsm_current_state(fsm_timeout2) == NULL)
|| (dds_security_fsm_current_state(fsm_timeout3) == NULL)) && (timeout > 0)) {
dds_sleepfor(100 * DDS_NSECS_IN_MSEC);
timeout--;
}
/* Then, they have to have ended. */
while (((dds_security_fsm_current_state(fsm_timeout) != NULL)
|| (dds_security_fsm_current_state(fsm_timeout2) != NULL)
|| (dds_security_fsm_current_state(fsm_timeout3) != NULL)) && (timeout > 0)) {
dds_sleepfor(100 * DDS_NSECS_IN_MSEC);
timeout--;
}
/*
* There should be about 1 second difference between all times:
* time1 = time0 + 1
* time2 = time0 + 2
* time3 = time0 + 1
*/
delta1 = time1 - time0;
delta2 = time2 - time0;
delta3 = time3 - time0;
printf("Time0 %" PRId64 "\n", time0);
printf("Time1 %" PRId64 "\n", time1);
printf("Time2 %" PRId64 "\n", time2);
printf("Time3 %" PRId64 "\n", time3);
printf("Delta1 %" PRId64 "\n", delta1);
printf("Delta2 %" PRId64 "\n", delta2);
printf("Delta3 %" PRId64 "\n", delta3);
CU_ASSERT(delta1 > 750 * DDS_NSECS_IN_MSEC);
CU_ASSERT(delta1 < 1250 * DDS_NSECS_IN_MSEC);
CU_ASSERT(delta2 > 1750 * DDS_NSECS_IN_MSEC);
CU_ASSERT(delta2 < 2250 * DDS_NSECS_IN_MSEC);
CU_ASSERT(delta3 > 750 * DDS_NSECS_IN_MSEC);
CU_ASSERT(delta3 < 1250 * DDS_NSECS_IN_MSEC);
dds_security_fsm_free(fsm_timeout);
dds_security_fsm_free(fsm_timeout2);
dds_security_fsm_free(fsm_timeout3);
}
/**
* Delete with event timeout
*/
CU_Test(ddssec_fsm, delete_with_timeout, .init = init_testcase, .fini = fini_testcase) {
fsm_timeout = dds_security_fsm_create(NULL, timeout_transitions,
sizeof(timeout_transitions) / sizeof(timeout_transitions[0]), &fsm_arg);
CU_ASSERT (fsm_timeout != NULL);
visited_timeout = 0;
dds_security_fsm_start(fsm_timeout);
/* Wait until we're in the timeout function. */
timeout = 100; /* 10 sec */
while ((visited_timeout == 0) && (timeout > 0)) {
dds_sleepfor(100 * DDS_NSECS_IN_MSEC);
timeout--;
}
dds_security_fsm_free(fsm_timeout);
dds_sleepfor(100 * DDS_NSECS_IN_MSEC);
/* Just for safety to be certain that the condition isn't used anymore before destroying it. */
while (stop_timeout_cond_cnt > 0) {
dds_time_t d = DDS_NSECS_IN_SEC;
ddsrt_cond_signal(&stop_timeout_cond);
dds_sleepfor(d);
}
}