diff options
Diffstat (limited to 'libccnx-transport-rta/ccnx/transport/transport_rta')
136 files changed, 27831 insertions, 0 deletions
diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_Command.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_Command.c new file mode 100644 index 00000000..4e0e91d0 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_Command.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implements the single wrapper for commands sent over the command channel + * + */ + +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> +#include <parc/algol/parc_DisplayIndented.h> + +#include <ccnx/transport/transport_rta/commands/rta_Command.h> + +/* + * Internal definition of command types + */ +typedef enum { + RtaCommandType_Unknown = 0, + RtaCommandType_CreateProtocolStack, + RtaCommandType_OpenConnection, + RtaCommandType_CloseConnection, + RtaCommandType_DestroyProtocolStack, + RtaCommandType_ShutdownFramework, + RtaCommandType_TransmitStatistics, + RtaCommandType_Last +} _RtaCommandType; + +struct rta_command { + _RtaCommandType type; + + union { + RtaCommandCloseConnection *closeConnection; + RtaCommandOpenConnection *openConnection; + RtaCommandCreateProtocolStack *createStack; + RtaCommandDestroyProtocolStack *destroyStack; + RtaCommandTransmitStatistics *transmitStats; + + // shutdown framework has no value it will be NULL + // Statistics has no value + void *noValue; + } value; +}; + +static struct commandtype_to_string { + _RtaCommandType type; + const char *string; +} _RtaCommandTypeToString[] = { + { .type = RtaCommandType_Unknown, .string = "Unknown" }, + { .type = RtaCommandType_CreateProtocolStack, .string = "CreateProtocolStack" }, + { .type = RtaCommandType_OpenConnection, .string = "OpenConnection" }, + { .type = RtaCommandType_CloseConnection, .string = "CloseConnection" }, + { .type = RtaCommandType_DestroyProtocolStack, .string = "DestroyProtocolStack" }, + { .type = RtaCommandType_ShutdownFramework, .string = "ShutdownFramework" }, + { .type = RtaCommandType_TransmitStatistics, .string = "TransmitStatistics" }, + { .type = RtaCommandType_Last, .string = NULL }, +}; + +// =============================== +// Internal API + +static void +_rtaCommand_Destroy(RtaCommand **commandPtr) +{ + RtaCommand *command = *commandPtr; + switch (command->type) { + case RtaCommandType_ShutdownFramework: + // no inner-release needed + break; + + case RtaCommandType_CreateProtocolStack: + rtaCommandCreateProtocolStack_Release(&command->value.createStack); + break; + + case RtaCommandType_OpenConnection: + rtaCommandOpenConnection_Release(&command->value.openConnection); + break; + + case RtaCommandType_CloseConnection: + rtaCommandCloseConnection_Release(&command->value.closeConnection); + break; + + case RtaCommandType_DestroyProtocolStack: + rtaCommandDestroyProtocolStack_Release(&command->value.destroyStack); + break; + + case RtaCommandType_TransmitStatistics: + rtaCommandTransmitStatistics_Release(&command->value.transmitStats); + break; + + default: + trapIllegalValue(command->type, "Illegal command type %d", command->type); + break; + } +} + +#ifdef Transport_DISABLE_VALIDATION +# define _rtaCommand_OptionalAssertValid(_instance_) +#else +# define _rtaCommand_OptionalAssertValid(_instance_) _rtaCommand_AssertValid(_instance_) +#endif + +static void +_rtaCommand_AssertValid(const RtaCommand *command) +{ + assertNotNull(command, "RtaCommand must be non-null"); + assertTrue(RtaCommandType_Unknown < command->type && command->type < RtaCommandType_Last, + "Invalid RtaCommand type, must be %d < type %d < %d", + RtaCommandType_Unknown, + command->type, + RtaCommandType_Last); + + switch (command->type) { + case RtaCommandType_ShutdownFramework: + assertNull(command->value.noValue, "RtaCommand value must be null for ShutdownFramework or Statistics"); + break; + + case RtaCommandType_CreateProtocolStack: + assertNotNull(command->value.createStack, "RtaCommand createStack member must be non-null"); + break; + + case RtaCommandType_OpenConnection: + assertNotNull(command->value.openConnection, "RtaCommand openConnection member must be non-null"); + break; + + case RtaCommandType_CloseConnection: + assertNotNull(command->value.closeConnection, "RtaCommand closeConnection member must be non-null"); + break; + + case RtaCommandType_DestroyProtocolStack: + assertNotNull(command->value.destroyStack, "RtaCommand destroyStack member must be non-null"); + break; + + case RtaCommandType_TransmitStatistics: + assertNotNull(command->value.transmitStats, "RtaCommand transmitStats member must be non-null"); + break; + + default: + trapIllegalValue(command->type, "Illegal command type %d", command->type); + break; + } +} + +parcObject_ExtendPARCObject(RtaCommand, _rtaCommand_Destroy, NULL, NULL, NULL, NULL, NULL, NULL); + +parcObject_ImplementAcquire(rtaCommand, RtaCommand); + +parcObject_ImplementRelease(rtaCommand, RtaCommand); + +static RtaCommand * +_rtaCommand_Allocate(_RtaCommandType type) +{ + RtaCommand *command = parcObject_CreateInstance(RtaCommand); + command->type = type; + return command; +} + +static const char * +_rtaCommand_TypeToString(const RtaCommand *command) +{ + for (int i = 0; _RtaCommandTypeToString[i].string != NULL; i++) { + if (_RtaCommandTypeToString[i].type == command->type) { + return _RtaCommandTypeToString[i].string; + } + } + trapUnexpectedState("Command is not a valid type: %d", command->type); +} + +// =============================== +// Public API + +void +rtaCommand_Display(const RtaCommand *command, int indentation) +{ + _rtaCommand_OptionalAssertValid(command); + + parcDisplayIndented_PrintLine(indentation, "RtaCommand type %s (%d) value pointer %p\n", + _rtaCommand_TypeToString(command), command->type, command->value); +} + +/* + * Gets a reference to itself and puts it in the ring buffer + */ +bool +rtaCommand_Write(const RtaCommand *command, PARCRingBuffer1x1 *commandRingBuffer) +{ + _rtaCommand_OptionalAssertValid(command); + + RtaCommand *reference = rtaCommand_Acquire(command); + + bool addedToRingBuffer = parcRingBuffer1x1_Put(commandRingBuffer, reference); + + if (!addedToRingBuffer) { + // it was not stored in the ring, so we need to be responsible and release it + rtaCommand_Release(&reference); + } + + return addedToRingBuffer; +} + +RtaCommand * +rtaCommand_Read(PARCRingBuffer1x1 *commandRingBuffer) +{ + RtaCommand *referenceFromRing = NULL; + + bool fetchedReference = parcRingBuffer1x1_Get(commandRingBuffer, (void **) &referenceFromRing); + if (fetchedReference) { + return referenceFromRing; + } + return NULL; +} + +// ====================== +// CLOSE CONNECTION + +bool +rtaCommand_IsCloseConnection(const RtaCommand *command) +{ + _rtaCommand_OptionalAssertValid(command); + return (command->type == RtaCommandType_CloseConnection); +} + +RtaCommand * +rtaCommand_CreateCloseConnection(const RtaCommandCloseConnection *close) +{ + RtaCommand *command = _rtaCommand_Allocate(RtaCommandType_CloseConnection); + command->value.closeConnection = rtaCommandCloseConnection_Acquire(close); + return command; +} + +const RtaCommandCloseConnection * +rtaCommand_GetCloseConnection(const RtaCommand *command) +{ + assertTrue(rtaCommand_IsCloseConnection(command), "Command is not CloseConnection"); + return command->value.closeConnection; +} + +// ====================== +// OPEN CONNECTION + +bool +rtaCommand_IsOpenConnection(const RtaCommand *command) +{ + _rtaCommand_OptionalAssertValid(command); + return (command->type == RtaCommandType_OpenConnection); +} + +RtaCommand * +rtaCommand_CreateOpenConnection(const RtaCommandOpenConnection *open) +{ + RtaCommand *command = _rtaCommand_Allocate(RtaCommandType_OpenConnection); + command->value.openConnection = rtaCommandOpenConnection_Acquire(open); + return command; +} + +const RtaCommandOpenConnection * +rtaCommand_GetOpenConnection(const RtaCommand *command) +{ + assertTrue(rtaCommand_IsOpenConnection(command), "Command is not OpenConnection"); + return command->value.openConnection; +} + +// ====================== +// CREATE STACK + +bool +rtaCommand_IsCreateProtocolStack(const RtaCommand *command) +{ + _rtaCommand_OptionalAssertValid(command); + return (command->type == RtaCommandType_CreateProtocolStack); +} + +RtaCommand * +rtaCommand_CreateCreateProtocolStack(const RtaCommandCreateProtocolStack *createStack) +{ + RtaCommand *command = _rtaCommand_Allocate(RtaCommandType_CreateProtocolStack); + command->value.createStack = rtaCommandCreateProtocolStack_Acquire(createStack); + return command; +} + +const RtaCommandCreateProtocolStack * +rtaCommand_GetCreateProtocolStack(const RtaCommand *command) +{ + assertTrue(rtaCommand_IsCreateProtocolStack(command), "Command is not CreateProtocolStack"); + return command->value.createStack; +} + +bool +rtaCommand_IsDestroyProtocolStack(const RtaCommand *command) +{ + _rtaCommand_OptionalAssertValid(command); + return (command->type == RtaCommandType_DestroyProtocolStack); +} + +RtaCommand * +rtaCommand_CreateDestroyProtocolStack(const RtaCommandDestroyProtocolStack *destroyStack) +{ + RtaCommand *command = _rtaCommand_Allocate(RtaCommandType_DestroyProtocolStack); + command->value.destroyStack = rtaCommandDestroyProtocolStack_Acquire(destroyStack); + return command; +} + +const RtaCommandDestroyProtocolStack * +rtaCommand_GetDestroyProtocolStack(const RtaCommand *command) +{ + assertTrue(rtaCommand_IsDestroyProtocolStack(command), "Command is not DestroyProtocolStack"); + return command->value.destroyStack; +} + +bool +rtaCommand_IsShutdownFramework(const RtaCommand *command) +{ + _rtaCommand_OptionalAssertValid(command); + return (command->type == RtaCommandType_ShutdownFramework); +} + +RtaCommand * +rtaCommand_CreateShutdownFramework(void) +{ + RtaCommand *command = _rtaCommand_Allocate(RtaCommandType_ShutdownFramework); + command->value.noValue = NULL; + return command; +} + +// no getter + +bool +rtaCommand_IsTransmitStatistics(const RtaCommand *command) +{ + _rtaCommand_OptionalAssertValid(command); + return (command->type == RtaCommandType_TransmitStatistics); +} + +RtaCommand * +rtaCommand_CreateTransmitStatistics(const RtaCommandTransmitStatistics *transmitStats) +{ + RtaCommand *command = _rtaCommand_Allocate(RtaCommandType_TransmitStatistics); + command->value.transmitStats = rtaCommandTransmitStatistics_Acquire(transmitStats); + return command; +} + +const RtaCommandTransmitStatistics * +rtaCommand_GetTransmitStatistics(const RtaCommand *command) +{ + assertTrue(rtaCommand_IsTransmitStatistics(command), "Command is not TransmitStatistics"); + return command->value.transmitStats; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_Command.h b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_Command.h new file mode 100644 index 00000000..02973c54 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_Command.h @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_Command.h + * @brief Wraps individual commands and is written to/from a Ring Buffer + * + * The RtaCommand is the common wrapper for all the specific command types. It also supports functions to + * write it to a PARCRingBuffer and read from a one. + * + * The ShutdownFramework command is a little different than all the other commands. There are no parameters + * to this command, so there is no separate type for it. You can create an RtaCommand of this flavor and + * check for it (rtaCommand_IsShutdownFramework), but there is no Get function. + * + */ +#ifndef Libccnx_rta_Commands_h +#define Libccnx_rta_Commands_h + +struct rta_command; +typedef struct rta_command RtaCommand; + +#include <ccnx/transport/transport_rta/commands/rta_CommandCloseConnection.h> +#include <ccnx/transport/transport_rta/commands/rta_CommandCreateProtocolStack.h> +#include <ccnx/transport/transport_rta/commands/rta_CommandDestroyProtocolStack.h> +#include <ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.h> +#include <ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.h> + +#include <parc/concurrent/parc_RingBuffer_1x1.h> + + +/** + * Writes a command to a Ring Buffer + * + * Creates a reference to the command and puts the reference on the ring buffer. + * The caller still owns their own reference to the command. + * + * This command does not involve a PARCNotifier. If using a notifier in conjunction + * with the ring buffer, the caller is reponsible for posting the notification after + * all ther writes are done. + * + * The function will not block. If the ring buffer is full, it will return false. + * + * @param [in] command The command to put (by reference) on the ring buffer. + * @param [in] commandRingBuffer The ring buffer to use + * + * @return true A reference was put on the ring buffer + * @return false Failed to put reference, likely because the ring buffer was full. + * + * Example: + * @code + * { + * RtaCommand *command = rtaCommand_CreateShutdownFramework(); + * + * bool success = rtaCommand_Write(command, ring); + * if (!success) { + * // return error to user that we're backlogged + * } + * + * rtaCommand_Release(&command); + * } + * @endcode + */ +bool rtaCommand_Write(const RtaCommand *command, PARCRingBuffer1x1 *commandRingBuffer); + +/** + * Reads a command from a ring buffer + * + * If the buffer is empty, will return NULL. + * + * @param [in] commandRingBuffer The buffer to read + * + * @return non-null A valid command object + * @return null Could not read a whole command object + * + * Example: + * @code + * { + * PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(4, NULL); + * RtaCommand *command = rtaCommand_CreateShutdownFramework(); + * + * bool success = rtaCommand_Write(command, ring); + * assertTrue(success, "Failed to put command in to ring buffer"); + * + * // We should now have two references + * assertTrue(parcObject_GetReferenceCount(command) == 2, "Wrong refernce count, got %zu expected %zu", parcObject_GetReferenceCount(command), 2); + * + * RtaCommand *test = rtaCommand_Read(ring); + * assertTrue(test == command, "Wrong pointers, got %p expected %p", (void *) test, (void *) command); + * + * rtaCommand_Release(&command); + * rtaCommand_Release(&test); + * parcRingBuffer1x1_Release(&ring); + * } + * @endcode + */ +RtaCommand *rtaCommand_Read(PARCRingBuffer1x1 *commandRingBuffer); + +/** + * Increase the number of references to a `RtaCommand`. + * + * Note that new `RtaCommand` is not created, + * only that the given `RtaCommand` reference count is incremented. + * Discard the reference by invoking `rtaCommand_Release`. + * + * @param [in] command The RtaCommand to reference. + * + * @return non-null A reference to `command`. + * @return null An error + * + * Example: + * @code + * { + * RtaCommand *command = rtaCommand_CreateShutdownFramework(); + * RtaCommand *second = rtaCommand_Acquire(command); + * + * // release order does not matter + * rtaCommand_Release(&command); + * rtaCommand_Release(&second); + * } + * @endcode + */ +RtaCommand *rtaCommand_Acquire(const RtaCommand *command); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] commandPtr A pointer to the object to release, will return NULL'd. + * + * Example: + * @code + * { + * RtaCommand *command = rtaCommand_CreateShutdownFramework(); + * RtaCommand *second = rtaCommand_Acquire(command); + * + * // release order does not matter + * rtaCommand_Release(&command); + * rtaCommand_Release(&second); + * } + * @endcode + */ +void rtaCommand_Release(RtaCommand **commandPtr); + +/** + * Print a human readable representation of the given `RtaCommand`. + * + * @param [in] command A pointer to the instance to display. + * @param [in] indentation The level of indentation to use to pretty-print the output. + * + * Example: + * @code + * { + * RtaCommand *command = rtaCommand_CreateShutdownFramework(); + * rtaCommand_Display(command, 0); + * rtaCommand_Release(&command); + * } + * @endcode + * + */ +void rtaCommand_Display(const RtaCommand *command, int indentation); + +// ====================== +// CLOSE CONNECTION + + +/** + * Tests if the RtaCommand is of type CloseConnection + * + * Tests if the RtaCommand is of type CloseConnection. This will also assert the + * RtaCommand invariants, so the RtaCommand object must be a properly constructed object. + * + * @param [in] command An allocated RtaCommand ojbect + * + * @return true The object is of type CloseConnection + * @return false The object is of some other type + * + * Example: + * @code + * { + * RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(77); + * RtaCommand *command = rtaCommand_CreateCloseConnection(closeConnection); + * assertTrue(rtaCommand_IsCloseConnection(command), "Command is not CloseConnection"); + * rtaCommand_Release(&command); + * rtaCommandCloseConnection_Release(&closeConnection); + * } + * @endcode + */ +bool rtaCommand_IsCloseConnection(const RtaCommand *command); + +/** + * Allocates and creates an RtaCommand object from a RtaCommandCloseConnection + * + * Allocates and creates an RtaCommand object from a RtaCommandCloseConnection + * by acquiring a reference to it and storing it in the RtaCommand. The caller + * may release their reference to `close` at any time. + * + * @param [in] close The specific command to make acquire a reference from. + * + * @return non-null A properly allocated and configured RtaCommand. + * @return null An error. + * + * Example: + * @code + * { + * RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(77); + * RtaCommand *command = rtaCommand_CreateCloseConnection(closeConnection); + * + * // release order does not matter + * rtaCommand_Release(&command); + * rtaCommandCloseConnection_Release(&closeConnection); + * } + * @endcode + */ +RtaCommand *rtaCommand_CreateCloseConnection(const RtaCommandCloseConnection *close); + +/** + * Returns the internal RtaCommandCloseConnection object + * + * Returns the internal RtaCommandCloseConnection object, the user should not release it. + * The the RtaCommand is not of type CloseConnection, it will assert in its validation. + * + * @param [in] command The RtaCommand to query for the object. + * + * @return The RtaCommandCloseConnection object that constructed the RtaCommand. + * + * Example: + * @code + * RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(77); + * RtaCommand *command = rtaCommand_CreateCloseConnection(closeConnection); + * + * const RtaCommandCloseConnection *testValue = rtaCommand_GetCloseConnection(command); + * assertTrue(testValue == closeConnection, "Wrong pointer returned"); + * + * rtaCommand_Release(&command); + * rtaCommandCloseConnection_Release(&closeConnection); + * @endcode + */ +const RtaCommandCloseConnection *rtaCommand_GetCloseConnection(const RtaCommand *command); + +// ====================== +// OPEN CONNECTION + +/** + * Tests if the RtaCommand is of type OpenConnection + * + * Tests if the RtaCommand is of type OpenConnection. This will also assert the + * RtaCommand invariants, so the RtaCommand object must be a properly constructed object. + * + * @param [in] command An allocated RtaCommand ojbect + * + * @return true The object is of type OpenConnection + * @return false The object is of some other type + * + * Example: + * @code + * { + * RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(111, 2341, 2450987, NULL); + * RtaCommand *command = rtaCommand_CreateOpenConnection(openConnection); + * assertTrue(rtaCommand_IsOpenConnection(command), "Command is not OpenConnection"); + * rtaCommand_Release(&command); + * rtaCommandOpenConnection_Release(&openConnection); + * } + * @endcode + */ +bool rtaCommand_IsOpenConnection(const RtaCommand *command); + +/** + * Allocates and creates an RtaCommand object from a RtaCommandOpenConnection + * + * Allocates and creates an RtaCommand object from a RtaCommandOpenConnection + * by acquiring a reference to it and storing it in the RtaCommand. The caller + * may release their reference to `open` at any time. + * + * @param [in] open The specific command to make acquire a reference from. + * + * @return non-null A properly allocated and configured RtaCommand. + * @return null An error. + * + * Example: + * @code + * { + * RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(111, 2341, 2450987, NULL); + * RtaCommand *command = rtaCommand_CreateOpenConnection(openConnection); + * + * // release order does not matter + * rtaCommand_Release(&command); + * rtaCommandOpenConnection_Release(&openConnection); + * } + * @endcode + */ +RtaCommand *rtaCommand_CreateOpenConnection(const RtaCommandOpenConnection *open); + +/** + * Returns the internal RtaCommandOpenConnection object + * + * Returns the internal RtaCommandOpenConnection object, the user should not release it. + * The the RtaCommand is not of type CloseConnection, it will assert in its validation. + * + * @param [in] command The RtaCommand to query for the object. + * + * @return The RtaCommandOpenConnection object that constructed the RtaCommand. + * + * Example: + * @code + * RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(111, 2341, 2450987, NULL); + * RtaCommand *command = rtaCommand_CreateOpenConnection(openConnection); + * + * const RtaCommandOpenConnection *testValue = rtaCommand_GetOpenConnection(command); + * assertTrue(testValue == openConnection, "Wrong pointer returned"); + * + * rtaCommand_Release(&command); + * rtaCommandOpenConnection_Release(&openConnection); + * @endcode + */ +const RtaCommandOpenConnection *rtaCommand_GetOpenConnection(const RtaCommand *command); + +// ====================== +// CREATE STACK + +/** + * Tests if the RtaCommand is of type CreateProtocolStack + * + * Tests if the RtaCommand is of type CreateProtocolStack. This will also assert the + * RtaCommand invariants, so the RtaCommand object must be a properly constructed object. + * + * @param [in] command An allocated RtaCommand ojbect + * + * @return true The object is of type CreateProtocolStack + * @return false The object is of some other type + * + * Example: + * @code + * { + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(111, config); + * RtaCommand *command = rtaCommand_CreateCreateProtocolStack(createStack); + * assertTrue(rtaCommand_IsCreateProtocolStack(command), "Command is not CreateProtocolStack"); + * rtaCommand_Release(&command); + * rtaCommandCreateProtocolStack_Release(&createStack); + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +bool rtaCommand_IsCreateProtocolStack(const RtaCommand *command); + +/** + * Allocates and creates an RtaCommand object from a RtaCommandCreateProtocolStack + * + * Allocates and creates an RtaCommand object from a RtaCommandCreateProtocolStack + * by acquiring a reference to it and storing it in the RtaCommand. The caller + * may release their reference to `createStack` at any time. + * + * @param [in] createStack The specific command to make acquire a reference from. + * + * @return non-null A properly allocated and configured RtaCommand. + * @return null An error. + * + * Example: + * @code + * { + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(111, config); + * RtaCommand *command = rtaCommand_CreateCreateProtocolStack(createStack); + * + * // release order does not matter + * rtaCommand_Release(&command); + * rtaCommandCreateProtocolStack_Release(&createStack); + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +RtaCommand *rtaCommand_CreateCreateProtocolStack(const RtaCommandCreateProtocolStack *createStack); + +/** + * Returns the internal RtaCommandCreateProtocolStack object + * + * Returns the internal RtaCommandCreateProtocolStack object, the user should not release it. + * The the RtaCommand is not of type CloseConnection, it will assert in its validation. + * + * @param [in] command The RtaCommand to query for the object. + * + * @return The RtaCommandCreateProtocolStack object that constructed the RtaCommand. + * + * Example: + * @code + * { + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(111, config); + * RtaCommand *command = rtaCommand_CreateCreateProtocolStack(createStack); + * + * const RtaCommandOpenConnection *testValue = rtaCommand_GetOpenConnection(command); + * assertTrue(testValue == createStack, "Wrong pointer returned"); + * + * rtaCommand_Release(&command); + * rtaCommandCreateProtocolStack_Release(&createStack); + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +const RtaCommandCreateProtocolStack *rtaCommand_GetCreateProtocolStack(const RtaCommand *command); + +// ====================== +// DESTROY STACK + +/** + * Tests if the RtaCommand is of type DestroyProtocolStack + * + * Tests if the RtaCommand is of type DestroyProtocolStack. This will also assert the + * RtaCommand invariants, so the RtaCommand object must be a properly constructed object. + * + * @param [in] command An allocated RtaCommand ojbect + * + * @return true The object is of type DestroyProtocolStack + * @return false The object is of some other type + * + * Example: + * @code + * { + * RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(77); + * RtaCommand *command = rtaCommand_CreateDestroyProtocolStack(destroyStack); + * assertTrue(rtaCommand_IsDestroyProtocolStack(command), "Command is not DestroyProtocolStack"); + * rtaCommand_Release(&command); + * rtaCommandDestroyProtocolStack_Release(&destroyStack); + * } + * @endcode + */ +bool rtaCommand_IsDestroyProtocolStack(const RtaCommand *command); + +/** + * Allocates and creates an RtaCommand object from a RtaCommandDestroyProtocolStack + * + * Allocates and creates an RtaCommand object from a RtaCommandDestroyProtocolStack + * by acquiring a reference to it and storing it in the RtaCommand. The caller + * may release their reference to `destroyStack` at any time. + * + * @param [in] destroyStack The specific command to make acquire a reference from. + * + * @return non-null A properly allocated and configured RtaCommand. + * @return null An error. + * + * Example: + * @code + * { + * RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(77); + * RtaCommand *command = rtaCommand_CreateDestroyProtocolStack(destroyStack); + * + * // release order does not matter + * rtaCommand_Release(&command); + * rtaCommandDestroyProtocolStack_Release(&destroyStack); + * } + * @endcode + */ +RtaCommand *rtaCommand_CreateDestroyProtocolStack(const RtaCommandDestroyProtocolStack *destroyStack); + +/** + * Returns the internal RtaCommandDestroyProtocolStack object + * + * Returns the internal RtaCommandDestroyProtocolStack object, the user should not release it. + * The the RtaCommand is not of type CloseConnection, it will assert in its validation. + * + * @param [in] command The RtaCommand to query for the object. + * + * @return The RtaCommandDestroyProtocolStack object that constructed the RtaCommand. + * + * Example: + * @code + * RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(77); + * RtaCommand *command = rtaCommand_CreateDestroyProtocolStack(destroyStack); + * + * const RtaCommandDestroyProtocolStack *testValue = rtaCommand_GetOpenConnection(command); + * assertTrue(testValue == destroyStack, "Wrong pointer returned"); + * + * rtaCommand_Release(&command); + * rtaCommandDestroyProtocolStack_Release(&destroyStack); + * @endcode + */ +const RtaCommandDestroyProtocolStack *rtaCommand_GetDestroyProtocolStack(const RtaCommand *command); + +// ====================== +// SHUTDOWN FRAMEWORK + +/** + * Tests if the RtaCommand is of type ShutdownFramework + * + * Tests if the RtaCommand is of type ShutdownFramework. This will also assert the + * RtaCommand invariants, so the RtaCommand object must be a properly constructed object. + * + * The ShutdownFramework command has no parameters, so there is no rtaCommand_GetShutdownFramework() function. + * + * @param [in] command An allocated RtaCommand ojbect + * + * @return true The object is of type ShutdownFramework + * @return false The object is of some other type + * + * Example: + * @code + * { + * RtaCommand *command = rtaCommand_CreateShutdownFramework(); + * assertTrue(rtaCommand_IsShutdownFramework(command), "Command is not shutdown framework"); + * rtaCommand_Release(&command); + * } + * @endcode + */ +bool rtaCommand_IsShutdownFramework(const RtaCommand *command); + +/** + * Allocates and creates an RtaCommand object of type ShutdownFramework. + * + * Allocates and creates an RtaCommand object of type ShutdownFramework. + * There are no parameters to ShutdownFramework, so there is no underlying type. + * + * @return non-null A properly allocated and configured RtaCommand. + * @return null An error. + * + * Example: + * @code + * { + * RtaCommand *command = rtaCommand_CreateShutdownFramework(); + * rtaCommand_Release(&command); + * } + * @endcode + */ +RtaCommand *rtaCommand_CreateShutdownFramework(void); + +// ====================== +// TRANSMIT STATS + +/** + * Tests if the RtaCommand is of type TransmitStatistics + * + * Tests if the RtaCommand is of type TransmitStatistics. This will also assert the + * RtaCommand invariants, so the RtaCommand object must be a properly constructed object. + * + * @param [in] command An allocated RtaCommand ojbect + * + * @return true The object is of type TransmitStatistics + * @return false The object is of some other type + * + * Example: + * @code + * { + * RtaCommandTransmitStatistics *transmitStats = rtaCommandTransmitStatistics_Create((struct timeval) { 1, 2 }, "filename"); + * RtaCommand *command = rtaCommand_CreateTransmitStatistics(transmitStats); + * assertTrue(rtaCommand_IsTransmitStatistics(command), "Command is not TransmitStatistics"); + * rtaCommand_Release(&command); + * rtaCommandTransmitStatistics_Release(&transmitStats); + * } + * @endcode + */ +bool rtaCommand_IsTransmitStatistics(const RtaCommand *command); + +/** + * Allocates and creates an RtaCommand object from a RtaCommandTransmitStatistics + * + * Allocates and creates an RtaCommand object from a RtaCommandTransmitStatistics + * by acquiring a reference to it and storing it in the RtaCommand. The caller + * may release their reference to `transmitStats` at any time. + * + * @param [in] transmitStats The specific command to make acquire a reference from. + * + * @return non-null A properly allocated and configured RtaCommand. + * @return null An error. + * + * Example: + * @code + * { + * RtaCommandTransmitStatistics *transmitStats = rtaCommandTransmitStatistics_Create((struct timeval) { 1, 2 }, "filename"); + * RtaCommand *command = rtaCommand_CreateTransmitStatistics(transmitStats); + * + * // release order does not matter + * rtaCommand_Release(&command); + * rtaCommandTransmitStatistics_Release(&transmitStats); + * } + * @endcode + */ +RtaCommand *rtaCommand_CreateTransmitStatistics(const RtaCommandTransmitStatistics *transmitStats); + +/** + * Returns the internal RtaCommandTransmitStatistics object + * + * Returns the internal RtaCommandTransmitStatistics object, the user should not release it. + * The the RtaCommand is not of type CloseConnection, it will assert in its validation. + * + * @param [in] command The RtaCommand to query for the object. + * + * @return The RtaCommandTransmitStatistics object that constructed the RtaCommand. + * + * Example: + * @code + * RtaCommandTransmitStatistics *transmitStats = rtaCommandTransmitStatistics_Create((struct timeval) { 1, 2 }, "filename"); + * RtaCommand *command = rtaCommand_CreateTransmitStatistics(transmitStats); + * + * const RtaCommandDestroyProtocolStack *testValue = rtaCommand_GetOpenConnection(command); + * assertTrue(testValue == destroyStack, "Wrong pointer returned"); + * + * rtaCommand_Release(&command); + * rtaCommandTransmitStatistics_Release(&transmitStats); + * @endcode + */ +const RtaCommandTransmitStatistics *rtaCommand_GetTransmitStatistics(const RtaCommand *command); +#endif // Libccnx_rta_Commands_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCloseConnection.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCloseConnection.c new file mode 100644 index 00000000..d5c092e8 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCloseConnection.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * Implements the RtaCommandCloseConnection object. Only has to pass one argument, the apiSocket number, + * to identify which connection to close. + * + * Example: + * @code + * <#example#> + * @endcode + */ + +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <ccnx/transport/transport_rta/commands/rta_CommandCloseConnection.h> + +struct rta_command_closeconnection { + int apiNotifierFd; +}; + +parcObject_ExtendPARCObject(RtaCommandCloseConnection, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + +parcObject_ImplementAcquire(rtaCommandCloseConnection, RtaCommandCloseConnection); + +parcObject_ImplementRelease(rtaCommandCloseConnection, RtaCommandCloseConnection); + +RtaCommandCloseConnection * +rtaCommandCloseConnection_Create(int apiNotifierFd) +{ + RtaCommandCloseConnection *closeConnection = parcObject_CreateInstance(RtaCommandCloseConnection); + closeConnection->apiNotifierFd = apiNotifierFd; + return closeConnection; +} + +int +rtaCommandCloseConnection_GetApiNotifierFd(const RtaCommandCloseConnection *closeConnection) +{ + assertNotNull(closeConnection, "Parameter closeConnection must be non-null"); + return closeConnection->apiNotifierFd; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCloseConnection.h b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCloseConnection.h new file mode 100644 index 00000000..4c46d831 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCloseConnection.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_CommandCloseConnection.h + * @brief Represents a command to close a connection + * + * Used to construct an RtaCommand object that is passed to rtaTransport_PassCommand() or _rtaTransport_SendCommandToFramework() + * to send a command from the API's thread of execution to the Transport's thread of execution. + * + */ +#ifndef Libccnx_rta_CommandCloseConnection_h +#define Libccnx_rta_CommandCloseConnection_h + +struct rta_command_closeconnection; +typedef struct rta_command_closeconnection RtaCommandCloseConnection; + +/** + * Creates a CloseConnection command object + * + * Creates a CloseConnection command object used to signal the RTA framework to + * close a specified connection. The user passes its socket number to the close + * command to signify which connection. + * + * The apiNotifierFd number must correspond to the apiSocket number used in rtaCommandOpenConnection(). + * + * @param [in] apiNotifierFd The descriptor number used by the API + * + * @return non-null An allocated object + * @return null An error + * + * Example: + * @code + * void foo(RTATransport *transport) + * { + * #define API_SIDE 0 + * #define TRANSPORT_SIDE 1 + * + * int pair[2]; + * socketpair(AF_LOCAL, SOCK_STREAM, 0, pair); + * PARCJSON *config = createConnectionConfig(); + * + * RtaCommandOpenConnection *openCommand = rtaCommandOpenConnection_Create(6, pair[API_SIDE], pair[TRANSPORT_SIDE], config); + * RtaCommand *command = rtaCommand_CreateOpenConnection(openCommand); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandOpenConnection_Release(&openCommand); + * + * // ... do work ... + * + * RtaCommandCloseConnection *closeCommand = rtaCommandCloseConnection_Create(pair[API_SIDE]); + * command = rtaCommand_CreateCloseConnection(openCommand); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandCloseConnection_Release(&closeCommand); + * } + * @endcode + */ +RtaCommandCloseConnection *rtaCommandCloseConnection_Create(int apiNotifierFd); + +/** + * Increase the number of references to a `RtaCommandCloseConnection`. + * + * Note that new `RtaCommandCloseConnection` is not created, + * only that the given `RtaCommandCloseConnection` reference count is incremented. + * Discard the reference by invoking `rtaCommandCloseConnection_Release`. + * + * @param [in] closeConnection The RtaCommandCloseConnection to reference. + * + * @return non-null A reference to `closeConnection`. + * @return null An error + * + * Example: + * @code + * { + * RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(pair[API_SIDE]); + * RtaCommandCloseConnection *second = rtaCommandCloseConnection_Acquire(closeConnection); + * + * // release order does not matter + * rtaCommandCloseConnection_Release(&closeConnection); + * rtaCommandCloseConnection_Release(&second); + * } + * @endcode + */ +RtaCommandCloseConnection *rtaCommandCloseConnection_Acquire(const RtaCommandCloseConnection *closeConnection); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] closePtr A pointer to the object to release, will return NULL'd. + * + * Example: + * @code + * { + * RtaCommandCloseConnection *closeCommand = rtaCommandCloseConnection_Create(pair[API_SIDE]); + * RtaCommand *command = rtaCommand_CreateCloseConnection(openCommand); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandCloseConnection_Release(&closeCommand); + * } + * @endcode + */ +void rtaCommandCloseConnection_Release(RtaCommandCloseConnection **closePtr); + +/** + * Returns the API notifier descriptor of the close command + * + * Returns the apiNotifierFd parameter. + * + * @param [in] closeConnection An allocated RtaCommandCloseConnection + * + * @return integer The value passed to rtaCommandCloseConnection_Create(). + * + * Example: + * @code + * { + * int apiNotifierFd = 7; + * RtaCommandCloseConnection *closeCommand = rtaCommandCloseConnection_Create(apiNotifierFd); + * int testValue = rtaCommandCloseConnection_GetApiNotifierFd(closeCommand); + * assertTrue(testValue == apiNotifierFd, "Wrong value got %d expected %d", testValue, apiNotifierFd); + * rtaCommandCloseConnection_Release(&closeCommand); + * } + * @endcode + */ +int rtaCommandCloseConnection_GetApiNotifierFd(const RtaCommandCloseConnection *closeConnection); +#endif // Libccnx_rta_CommandCloseConnection_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCreateProtocolStack.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCreateProtocolStack.c new file mode 100644 index 00000000..fb5ecd9a --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCreateProtocolStack.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * Implements the RtaCommandCreateProtocolStack object which signals to RTA Framework to open a new connection + * with the given configuration. + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <ccnx/transport/transport_rta/commands/rta_CommandCreateProtocolStack.h> + +struct rta_command_createprotocolstack { + int stackId; + CCNxStackConfig *config; +}; + +static void +_rtaCommandCreateProtocolStack_Destroy(RtaCommandCreateProtocolStack **openConnectionPtr) +{ + RtaCommandCreateProtocolStack *openConnection = *openConnectionPtr; + + if (openConnection->config) { + ccnxStackConfig_Release(&openConnection->config); + } +} + +parcObject_ExtendPARCObject(RtaCommandCreateProtocolStack, _rtaCommandCreateProtocolStack_Destroy, + NULL, NULL, NULL, NULL, NULL, NULL); + +parcObject_ImplementAcquire(rtaCommandCreateProtocolStack, RtaCommandCreateProtocolStack); + +parcObject_ImplementRelease(rtaCommandCreateProtocolStack, RtaCommandCreateProtocolStack); + +RtaCommandCreateProtocolStack * +rtaCommandCreateProtocolStack_Create(int stackId, CCNxStackConfig *config) +{ + RtaCommandCreateProtocolStack *createStack = parcObject_CreateInstance(RtaCommandCreateProtocolStack); + createStack->stackId = stackId; + createStack->config = ccnxStackConfig_Copy(config); + return createStack; +} + +const char * +rtaCommandCreateProtocolStack_AssessValidity(const RtaCommandCreateProtocolStack *instance) +{ + char *result = NULL; + + if (instance != NULL) { + if (ccnxStackConfig_IsValid(instance->config)) { + result = NULL; + } else { + result = "CCNxStackConfig instance is invalid"; + } + } else { + result = "Instance cannot be NULL"; + } + + return result; +} + +bool +rtaCommandCreateProtocolStack_IsValid(const RtaCommandCreateProtocolStack *instance) +{ + const char *assessment = rtaCommandCreateProtocolStack_AssessValidity(instance); + return assessment == NULL; +} + +void +rtaCommandCreateProtocolStack_AssertValid(const RtaCommandCreateProtocolStack *instance) +{ + const char *assessment = rtaCommandCreateProtocolStack_AssessValidity(instance); + trapIllegalValueIf(assessment != NULL, "%s", assessment); +} + +int +rtaCommandCreateProtocolStack_GetStackId(const RtaCommandCreateProtocolStack *createStack) +{ + assertNotNull(createStack, "Parameter createStack must be non-null"); + return createStack->stackId; +} + +CCNxStackConfig * +rtaCommandCreateProtocolStack_GetStackConfig(const RtaCommandCreateProtocolStack *createStack) +{ + return createStack->config; +} + +PARCJSON * +rtaCommandCreateProtocolStack_GetConfig(const RtaCommandCreateProtocolStack *createStack) +{ + assertNotNull(createStack, "Parameter createStack must be non-null"); + return ccnxStackConfig_GetJson(createStack->config); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCreateProtocolStack.h b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCreateProtocolStack.h new file mode 100644 index 00000000..a14f651c --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCreateProtocolStack.h @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_CommandCreateProtocolStack.h + * @brief Represents a command to create a protocol stack + * + * Used to construct an RtaCommand object that is passed to rtaTransport_PassCommand() or _rtaTransport_SendCommandToFramework() + * to send a command from the API's thread of execution to the Transport's thread of execution. + * + */ +#ifndef Libccnx_rta_CommandCreateProtocolStack_h +#define Libccnx_rta_CommandCreateProtocolStack_h + +struct rta_command_createprotocolstack; +typedef struct rta_command_createprotocolstack RtaCommandCreateProtocolStack; + + +#include <ccnx/transport/common/ccnx_StackConfig.h> + +/** + * Creates a CreateProtocolStack command object + * + * Creates a CreateProtocolStack command object used to signal the RTA framework to + * create a new Protocol Stack with the specified stackId and configuration. The caller is + * responsible for ensuring that the stackId is unique among existing stacks (the framework might + * assert an error for duplicates). Note that the check for a unique stack ID is only done + * once the RtaCommandCreateProtocolStack is passed to the RtaFramework, not on creation + * of this object. + * + * @param [in] stackId The new (unique) ID for the stack to create + * @param [in] config the JSON representation of the stack configuration + * + * @return non-null An allocated object + * @return null An error + * + * Example: + * @code + * void + * foo(RTATransport *transport) + * { + * int stackId = nextStackIdNumber(); + * + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + * RtaCommand *command = rtaCommand_CreateCreateProtocolStack(createStack); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandCreateProtocolStack_Release(&createStack); + * + * // ... do work ... + * + * RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(stackId); + * command = rtaCommand_CreateDestroyProtocolStack(destroyStack); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandDestroyProtocolStack(&destroyStack); + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +RtaCommandCreateProtocolStack *rtaCommandCreateProtocolStack_Create(int stackId, CCNxStackConfig *config); + +/** + * Increase the number of references to a `RtaCommandCreateProtocolStack`. + * + * Note that new `RtaCommandCreateProtocolStack` is not created, + * only that the given `RtaCommandCreateProtocolStack` reference count is incremented. + * Discard the reference by invoking `rtaCommandCreateProtocolStack_Release`. + * + * @param [in] createStack The RtaCommandCreateProtocolStack to reference. + * + * @return non-null A reference to `createStack`. + * @return null An error + * + * Example: + * @code + * { + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + * RtaCommandCreateProtocolStack *second = rtaCommandCreateProtocolStack_Acquire(createStack); + * + * // release order does not matter + * rtaCommandCreateProtocolStack_Release(&createStack); + * rtaCommandCreateProtocolStack_Release(&second); + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +RtaCommandCreateProtocolStack *rtaCommandCreateProtocolStack_Acquire(const RtaCommandCreateProtocolStack *createStack); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] closePtr A pointer to the object to release, will return NULL'd. + * + * Example: + * @code + * { + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + * RtaCommand *command = rtaCommand_CreateCreateProtocolStack(createStack); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandCreateProtocolStack_Release(&createStack); + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +void rtaCommandCreateProtocolStack_Release(RtaCommandCreateProtocolStack **createStackPtr); + +/** + * Returns the Stack ID of the create stack command + * + * Returns the Stack ID parameter. + * + * @param [in] createStack An allocated RtaCommandCreateProtocolStack + * + * @return integer The value passed to rtaCommandCreateProtocolStack_Create(). + * + * Example: + * @code + * { + * int stackId = 7; + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + * int testValue = rtaCommandCreateProtocolStack_GetStackId(createStack); + * assertTrue(testValue == stackId, "Wrong value got %d expected %d", testValue, stackId); + * rtaCommandCreateProtocolStack_Release(&createStack); + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +int rtaCommandCreateProtocolStack_GetStackId(const RtaCommandCreateProtocolStack *createStack); + +/** + * Get the CCNxStackConfig used by the given `RtaCommandCreateProtocolStack` instance. + * + * @param [in] createStack A pointer to a valid `RtaCommandCreateProtocolStack` instance. + * + * @return A pointer to the CCNxStackConfig used by the given `RtaCommandCreateProtocolStack` instance. + * + * Example: + * @code + * { + * int stackId = 7; + * + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + * + * CCNxStackConfig *config = rtaCommandCreateProtocolStack_GetStackConfig(createStack); + * + * rtaCommandCreateProtocolStack_Release(&createStack); + * + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +CCNxStackConfig *rtaCommandCreateProtocolStack_GetStackConfig(const RtaCommandCreateProtocolStack *createStack); + +/** + * Returns the PARCJSON stack configuration of the create stack command + * + * Returns the JSON representation of the stack configuration. + * + * @param [in] createStack An allocated RtaCommandCreateProtocolStack + * + * @return The value passed to rtaCommandCreateProtocolStack_Create(). + * + * Example: + * @code + * { + * int stackId = 7; + * + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + * + * PARCJSON *testValue = rtaCommandCreateProtocolStack_GetConfig(createStack); + * assertTrue(ccnxJson_Equals(config, testValue), "Wrong value"); + * rtaCommandCreateProtocolStack_Release(&createStack); + * + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +PARCJSON *rtaCommandCreateProtocolStack_GetConfig(const RtaCommandCreateProtocolStack *createStack); + +/** + * Derive an explanation for why a RtaCommandCreateProtocolStack instance is invalid. + * + * Returns either a nul-terminated C string containing a human-readable explanation, + * or NULL which indicates the instance is valid. + * + * @param [in] instance A pointer to a `RtaCommandCreateProtocolStack` instance. + * + * @return NULL The instance is valid. + * @return non-NULL A nul-terminated C string containing an explanation. + * + * Example: + * @code + * { + * RtaCommandCreateProtocolStack *instance = rtaCommandCreateProtocolStack_Create(...); + * + * if (rtaCommandCreateProtocolStack_IsValid(instance)) { + * printf("Instance is valid.\n"); + * } + * } + * @endcode + */ +const char *rtaCommandCreateProtocolStack_AssessValidity(const RtaCommandCreateProtocolStack *instance); + +/** + * Determine if an instance of `RtaCommandCreateProtocolStack` is valid. + * + * Valid means the internal state of the type is consistent with its required current or future behaviour. + * This may include the validation of internal instances of types. + * + * @param [in] instance A pointer to a `RtaCommandCreateProtocolStack` instance. + * + * @return true The instance is valid. + * @return false The instance is not valid. + * + * Example: + * @code + * { + * RtaCommandCreateProtocolStack *instance = rtaCommandCreateProtocolStack_Create(...); + * + * if (rtaCommandCreateProtocolStack_IsValid(instance)) { + * printf("Instance is valid.\n"); + * } + * } + * @endcode + */ +bool rtaCommandCreateProtocolStack_IsValid(const RtaCommandCreateProtocolStack *instance); + +/** + * Assert that the given `RtaCommandCreateProtocolStack` instance is valid. + * + * @param [in] instance A pointer to a valid RtaCommandCreateProtocolStack instance. + * + * Example: + * @code + * { + * RtaCommandCreateProtocolStack *a = rtaCommandCreateProtocolStack_Create(); + * + * rtaCommandCreateProtocolStack_AssertValid(a); + * + * printf("Instance is valid.\n"); + * + * rtaCommandCreateProtocolStack_Release(&b); + * } + * @endcode + */ +void rtaCommandCreateProtocolStack_AssertValid(const RtaCommandCreateProtocolStack *instance); +#endif // Libccnx_rta_CommandCreateProtocolStack_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandDestroyProtocolStack.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandDestroyProtocolStack.c new file mode 100644 index 00000000..4ce4321f --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandDestroyProtocolStack.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * Implements the RtaCommandDestroyProtocolStack object which signals to RTA Framework to open a new connection + * with the given configuration. + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <ccnx/transport/transport_rta/commands/rta_CommandDestroyProtocolStack.h> + +struct rta_command_destroyprotocolstack { + int stackId; +}; + +parcObject_ExtendPARCObject(RtaCommandDestroyProtocolStack, + NULL, NULL, NULL, NULL, NULL, NULL, NULL); + +parcObject_ImplementAcquire(rtaCommandDestroyProtocolStack, RtaCommandDestroyProtocolStack); + +parcObject_ImplementRelease(rtaCommandDestroyProtocolStack, RtaCommandDestroyProtocolStack); + +// ======= Public API + +RtaCommandDestroyProtocolStack * +rtaCommandDestroyProtocolStack_Create(int stackId) +{ + RtaCommandDestroyProtocolStack *createStack = parcObject_CreateInstance(RtaCommandDestroyProtocolStack); + createStack->stackId = stackId; + return createStack; +} + + +int +rtaCommandDestroyProtocolStack_GetStackId(const RtaCommandDestroyProtocolStack *destroyStack) +{ + assertNotNull(destroyStack, "Parameter destroyStack must be non-null"); + return destroyStack->stackId; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandDestroyProtocolStack.h b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandDestroyProtocolStack.h new file mode 100644 index 00000000..feecfac9 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandDestroyProtocolStack.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_CommandDestroyProtocolStack.h + * @brief Represents a command to destroy a protocol stack + * + * Used to construct an RtaCommand object that is passed to rtaTransport_PassCommand() or _rtaTransport_SendCommandToFramework() + * to send a command from the API's thread of execution to the Transport's thread of execution. + * + */ +#ifndef Libccnx_rta_CommandDestroyProtocolStack_h +#define Libccnx_rta_CommandDestroyProtocolStack_h + +struct rta_command_destroyprotocolstack; +typedef struct rta_command_destroyprotocolstack RtaCommandDestroyProtocolStack; + +/** + * Creates a DestroyProtocolStack command object + * + * Creates a DestroyProtocolStack command object used to signal the RTA framework to + * destroy a protocol stack and all connections in it. + * + * @param [in] stackId The ID used to create the protocol stack. + * + * @return non-null An allocated object + * @return null An error + * + * Example: + * @code + * void foo(RTATransport *transport) + * { + * int stackId = nextStackIdNumber(); + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * + * RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + * RtaCommand *command = rtaCommand_CreateCreateProtocolStack(createStack); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandCreateProtocolStack_Release(&createStack); + * + * // ... do work ... + * + * RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(stackId); + * command = rtaCommand_CreateDestroyProtocolStack(destroyStack); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandDestroyProtocolStack_Release(&destroyStack); + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +RtaCommandDestroyProtocolStack *rtaCommandDestroyProtocolStack_Create(int stackId); + +/** + * Increase the number of references to a `RtaCommandDestroyProtocolStack`. + * + * Note that new `RtaCommandDestroyProtocolStack` is not created, + * only that the given `RtaCommandDestroyProtocolStack` reference count is incremented. + * Discard the reference by invoking `rtaCommandDestroyProtocolStack_Release`. + * + * @param [in] destroyStack The RtaCommandDestroyProtocolStack to reference. + * + * @return non-null A reference to `destroyStack`. + * @return null An error + * + * Example: + * @code + * { + * CCNxStackConfig *config = ccnxStackConfig_Create(); + * RtaCommandDestroyProtocolStack *destroyStack = rtaCommandCreateProtocolStack_Create(stackId, config); + * RtaCommandDestroyProtocolStack *second = rtaCommandDestroyProtocolStack_Acquire(destroyStack); + * + * // release order does not matter + * rtaCommandDestroyProtocolStack_Release(&destroyStack); + * rtaCommandDestroyProtocolStack_Release(&second); + * ccnxStackConfig_Release(&config); + * } + * @endcode + */ +RtaCommandDestroyProtocolStack *rtaCommandDestroyProtocolStack_Acquire(const RtaCommandDestroyProtocolStack *destroyStack); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] closePtr A pointer to the object to release, will return NULL'd. + * + * Example: + * @code + * { + * RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(stackId); + * RtaCommand *command = rtaCommand_CreateDestroyProtocolStack(destroyStack); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandDestroyProtocolStack(&destroyStack); + * } + * @endcode + */ +void rtaCommandDestroyProtocolStack_Release(RtaCommandDestroyProtocolStack **destroyStackPtr); + +/** + * Returns the Stack ID of the destroy stack command + * + * Returns the Stack ID parameter. + * + * @param [in] destroyStack An allocated RtaCommandDestroyProtocolStack + * + * @return integer The value passed to rtaCommandDestroyProtocolStack_Create(). + * + * Example: + * @code + * { + * int stackId = 7; + * RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(stackId); + * int testValue = rtaCommandDestroyProtocolStack_GetStackId(destroyStack); + * assertTrue(testValue == stackId, "Wrong value got %d expected %d", testValue, stackId); + * rtaCommandDestroyProtocolStack(&destroyStack); + * } + * @endcode + */ +int rtaCommandDestroyProtocolStack_GetStackId(const RtaCommandDestroyProtocolStack *destroyStack); +#endif // Libccnx_rta_CommandDestroyProtocolStack_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.c new file mode 100644 index 00000000..1ec1a945 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * Implements the RtaCommandOpenConnection object which signals to RTA Framework to open a new connection + * with the given configuration. + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.h> + +struct rta_command_openconnection { + int stackId; + int apiNotifierFd; + int transportNotifierFd; + PARCJSON *config; +}; + +// ======= Private API + +static void +_rtaCommandOpenConnection_Destroy(RtaCommandOpenConnection **openConnectionPtr) +{ + RtaCommandOpenConnection *openConnection = *openConnectionPtr; + if (openConnection->config != NULL) { + parcJSON_Release(&openConnection->config); + } +} + +parcObject_ExtendPARCObject(RtaCommandOpenConnection, _rtaCommandOpenConnection_Destroy, + NULL, NULL, NULL, NULL, NULL, NULL); + +parcObject_ImplementAcquire(rtaCommandOpenConnection, RtaCommandOpenConnection); + +parcObject_ImplementRelease(rtaCommandOpenConnection, RtaCommandOpenConnection); + +// ======= Public API + +RtaCommandOpenConnection * +rtaCommandOpenConnection_Create(int stackId, int apiNotifierFd, int transportNotifierFd, const PARCJSON *config) +{ + RtaCommandOpenConnection *openConnection = parcObject_CreateInstance(RtaCommandOpenConnection); + openConnection->stackId = stackId; + openConnection->apiNotifierFd = apiNotifierFd; + openConnection->transportNotifierFd = transportNotifierFd; + openConnection->config = parcJSON_Copy(config); + return openConnection; +} + +int +rtaCommandOpenConnection_GetApiNotifierFd(const RtaCommandOpenConnection *openConnection) +{ + assertNotNull(openConnection, "Parameter openConnection must be non-null"); + return openConnection->apiNotifierFd; +} + +int +rtaCommandOpenConnection_GetStackId(const RtaCommandOpenConnection *openConnection) +{ + assertNotNull(openConnection, "Parameter openConnection must be non-null"); + return openConnection->stackId; +} + +int +rtaCommandOpenConnection_GetTransportNotifierFd(const RtaCommandOpenConnection *openConnection) +{ + assertNotNull(openConnection, "Parameter openConnection must be non-null"); + return openConnection->transportNotifierFd; +} + +PARCJSON * +rtaCommandOpenConnection_GetConfig(const RtaCommandOpenConnection *openConnection) +{ + assertNotNull(openConnection, "Parameter openConnection must be non-null"); + return openConnection->config; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.h b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.h new file mode 100644 index 00000000..031ae04b --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.h @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_CommandOpenConnection.h + * @brief Represents a command to open a connection + * + * Used to construct an RtaCommand object that is passed to rtaTransport_PassCommand() or _rtaTransport_SendCommandToFramework() + * to send a command from the API's thread of execution to the Transport's thread of execution. + * + */ +#ifndef Libccnx_rta_CommandOpenConnection_h +#define Libccnx_rta_CommandOpenConnection_h + + +struct rta_command_openconnection; +typedef struct rta_command_openconnection RtaCommandOpenConnection; + +/** + * Creates a OpenConnection command object + * + * Creates a OpenConnection command object used to signal the RTA framework to + * open a specified connection. The user passes in the two sides of a socket pair + * plus the JSON representation of the connection. + * + * The RTA transport API connector will use the transportNotifierFd side of the socket pair. + * + * The caller must know the protocol stack handle stackId to specify which stack to + * associate the connection with. + * + * @param [in] stackId The protocol stack handle to use for the connection. + * @param [in] apiNotifierFd A descriptor used to notify the API that there's data to read from the transport. + * @param [in] transportNotifierFd A descriptor used to notify the Transport there's data to read from the API. + * @param [in] config The stack/connection config. + * + * @return non-null An allocated object + * @return null An error + * + * Example: + * @code + * void foo(RTATransport *transport) + * { + * #define API_SIDE 0 + * #define TRANSPORT_SIDE 1 + * + * int pair[2]; + * socketpair(AF_LOCAL, SOCK_STREAM, 0, pair); + * PARCJSON *config = createConnectionConfig(); + * + * RtaCommandOpenConnection *openCommand = rtaCommandOpenConnection_Create(6, pair[API_SIDE], pair[TRANSPORT_SIDE], config); + * RtaCommand *command = rtaCommand_CreateOpenConnection(openCommand); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandOpenConnection_Release(&openCommand); + * + * // ... do work ... + * RtaCommandCloseConnection *closeCommand = rtaCommandCloseConnection_Create(pair[API_SIDE]); + * RtaCommand *command = rtaCommand_CreateCloseConnection(openCommand); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandCloseConnection_Release(&closeCommand); + * } + * @endcode + */ +RtaCommandOpenConnection *rtaCommandOpenConnection_Create(int stackId, int apiNotifierFd, int transportNotifierFd, const PARCJSON *config); + +/** + * Increase the number of references to a `RtaCommandOpenConnection`. + * + * Note that new `RtaCommandOpenConnection` is not created, + * only that the given `RtaCommandOpenConnection` reference count is incremented. + * Discard the reference by invoking `rtaCommandOpenConnection_Release`. + * + * @param [in] openConnection The RtaCommandDestroyProtocolStack to reference. + * + * @return non-null A reference to `openConnection`. + * @return null An error + * + * Example: + * @code + * { + * RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(6, pair[API_SIDE], pair[TRANSPORT_SIDE], config); + * RtaCommandOpenConnection *second = rtaCommandOpenConnection_Acquire(openConnection); + * + * // release order does not matter + * rtaCommandOpenConnection_Release(&openConnection); + * rtaCommandOpenConnection_Release(&second); + * } + * @endcode + */ +RtaCommandOpenConnection *rtaCommandOpenConnection_Acquire(const RtaCommandOpenConnection *openConnection); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] closePtr A pointer to the object to release, will return NULL'd. + * + * Example: + * @code + * { + * RtaCommandOpenConnection *openCommand = rtaCommandOpenConnection_Create(6, pair[API_SIDE], pair[TRANSPORT_SIDE], config); + * RtaCommand *command = rtaCommand_CreateOpenConnection(openCommand); + * _rtaTransport_SendCommandToFramework(transport, command); + * rtaCommand_Release(&command); + * rtaCommandOpenConnection_Release(&openCommand); + * } + * @endcode + */ +void rtaCommandOpenConnection_Release(RtaCommandOpenConnection **openPtr); + +/** + * Returns the Stack ID of the open command + * + * Returns the Stack ID parameter. + * + * @param [in] openConnection An allocated RtaCommandOpenConnection + * + * @return integer The value passed to rtaCommandOpenConnection_Create(). + * + * Example: + * @code + * { + * int stackId = 7; + * RtaCommandOpenConnection *openCommand = rtaCommandOpenConnection_Create(apiSocket, pair[API_SIDE], pair[TRANSPORT_SIDE], config); + * int testValue = rtaCommandOpenConnection_GetStackId(openCommand); + * assertTrue(testValue == stackId, "Wrong value got %d expected %d", testValue, stackId); + * rtaCommandOpenConnection_Release(&openCommand); + * } + * @endcode + */ +int rtaCommandOpenConnection_GetStackId(const RtaCommandOpenConnection *openConnection); + +/** + * Returns the API descriptor of the open command + * + * Returns the apiNotifierFd parameter. The API descriptor is the side read/written by the API. + * + * @param [in] openConnection An allocated RtaCommandOpenConnection + * + * @return integer The value passed to rtaCommandOpenConnection_Create(). + * + * Example: + * @code + * { + * int stackId = 7; + * RtaCommandOpenConnection *openCommand = rtaCommandOpenConnection_Create(apiSocket, pair[API_SIDE], pair[TRANSPORT_SIDE], config); + * int testValue = rtaCommandOpenConnection_GetApiNotifierFd(openCommand); + * assertTrue(testValue == pair[API_SIDE], "Wrong value got %d expected %d", testValue, pair[API_SIDE]); + * rtaCommandOpenConnection_Release(&openCommand); + * } + * @endcode + */ +int rtaCommandOpenConnection_GetApiNotifierFd(const RtaCommandOpenConnection *openConnection); + +/** + * Returns the Transport descriptor of the open command + * + * Returns the transportNotifierFd parameter. The transport descriptor is the side read/written by the Transport. + * + * @param [in] openConnection An allocated RtaCommandOpenConnection + * + * @return integer The value passed to rtaCommandOpenConnection_Create(). + * + * Example: + * @code + * { + * int stackId = 7; + * RtaCommandOpenConnection *openCommand = rtaCommandOpenConnection_Create(apiSocket, pair[API_SIDE], pair[TRANSPORT_SIDE], config); + * int testValue = rtaCommandOpenConnection_GetTransportNotifierFd(openCommand); + * assertTrue(testValue == pair[TRANSPORT_SIDE], "Wrong value got %d expected %d", testValue, pair[TRANSPORT_SIDE]); + * rtaCommandOpenConnection_Release(&openCommand); + * } + * @endcode + */ +int rtaCommandOpenConnection_GetTransportNotifierFd(const RtaCommandOpenConnection *openConnection); + +/** + * Returns the PARCJSON stack configuration of the create stack command + * + * Returns the JSON representation of the stack configuration. + * + * @param [in] createStack An allocated RtaCommandCreateProtocolStack + * + * @return The value passed to rtaCommandCreateProtocolStack_Create(). + * + * Example: + * @code + * { + * int stackId = 7; + * PARCJSON *config = createConnectionConfiguration(); + * RtaCommandOpenConnection *openCommand = rtaCommandOpenConnection_Create(apiSocket, pair[API_SIDE], pair[TRANSPORT_SIDE], config); + * PARCJSON *testValue = rtaCommandOpenConnection_GetConfig(openCommand); + * assertTrue(ccnxJson_Equals(config, testValue), "Wrong value"); + * rtaCommandOpenConnection_Release(&openCommand); + * parcJSON_Release(&config); + * } + * @endcode + */ +PARCJSON *rtaCommandOpenConnection_GetConfig(const RtaCommandOpenConnection *openConnection); +#endif // Libccnx_rta_CommandOpenConnection_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.c new file mode 100644 index 00000000..129f68c7 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * Implements the RtaCommandTransmitStatistics object which signals to RTA Framework to open a new connection + * with the given configuration. + */ + +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> +#include <sys/param.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.h> + +struct rta_command_transmitstatistics { + struct timeval period; + char *filename; +}; + +// ======= Private API + +static void +_rtaCommandTransmitStatistics_Destroy(RtaCommandTransmitStatistics **transmitStatsPtr) +{ + RtaCommandTransmitStatistics *transmitStats = *transmitStatsPtr; + parcMemory_Deallocate((void **) &(transmitStats->filename)); +} + +parcObject_ExtendPARCObject(RtaCommandTransmitStatistics, _rtaCommandTransmitStatistics_Destroy, + NULL, NULL, NULL, NULL, NULL, NULL); + +parcObject_ImplementAcquire(rtaCommandTransmitStatistics, RtaCommandTransmitStatistics); + +parcObject_ImplementRelease(rtaCommandTransmitStatistics, RtaCommandTransmitStatistics); + +// ======= Public API + +RtaCommandTransmitStatistics * +rtaCommandTransmitStatistics_Create(struct timeval period, const char *filename) +{ + assertNotNull(filename, "Filename must be non-null"); + + RtaCommandTransmitStatistics *transmitStats = parcObject_CreateInstance(RtaCommandTransmitStatistics); + memcpy(&transmitStats->period, &period, sizeof(struct timeval)); + transmitStats->filename = parcMemory_StringDuplicate(filename, PATH_MAX); + + return transmitStats; +} + +struct timeval +rtaCommandTransmitStatistics_GetPeriod(const RtaCommandTransmitStatistics *transmitStats) +{ + assertNotNull(transmitStats, "Parameter transmitStats must be non-null"); + return transmitStats->period; +} + +const char * +rtaCommandTransmitStatistics_GetFilename(const RtaCommandTransmitStatistics *transmitStats) +{ + assertNotNull(transmitStats, "Parameter transmitStats must be non-null"); + return transmitStats->filename; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.h b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.h new file mode 100644 index 00000000..cc5c9d02 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_CommandTransmitStatistics.h + * @brief Represents a command to setup a statistics file + * + * Used to construct an RtaCommand object that is passed to rtaTransport_PassCommand() or _rtaTransport_SendCommandToFramework() + * to send a command from the API's thread of execution to the Transport's thread of execution. + * + */ +#ifndef Libccnx_rta_CommandTransmitStatistics_h +#define Libccnx_rta_CommandTransmitStatistics_h + +struct rta_command_transmitstatistics; +typedef struct rta_command_transmitstatistics RtaCommandTransmitStatistics; + +RtaCommandTransmitStatistics *rtaCommandTransmitStatistics_Create(struct timeval period, const char *filename); + +/** + * Increase the number of references to a `RtaCommandTransmitStatistics`. + * + * Note that new `RtaCommandTransmitStatistics` is not created, + * only that the given `RtaCommandTransmitStatistics` reference count is incremented. + * Discard the reference by invoking `rtaCommandTransmitStatistics_Release`. + * + * @param [in] transmitStats The RtaCommandTransmitStatistics to reference. + * + * @return non-null A reference to `transmitStats`. + * @return null An error + * + * Example: + * @code + * { + * RtaCommandOpenConnection *transmitStats = rtaCommandTransmitStatistics_Create((struct timeval) { 1, 2 }, "filename"); + * RtaCommandOpenConnection *second = rtaCommandTransmitStatistics_Acquire(transmitStats); + * + * // release order does not matter + * rtaCommandTransmitStatistics_Release(&transmitStats); + * rtaCommandTransmitStatistics_Release(&second); + * } + * @endcode + */ +RtaCommandTransmitStatistics *rtaCommandTransmitStatistics_Acquire(const RtaCommandTransmitStatistics *transmitStats); + +/** + * Release a previously acquired reference to the specified instance, + * decrementing the reference count for the instance. + * + * The pointer to the instance is set to NULL as a side-effect of this function. + * + * If the invocation causes the last reference to the instance to be released, + * the instance is deallocated and the instance's implementation will perform + * additional cleanup and release other privately held references. + * + * @param [in,out] openPtr A pointer to the object to release, will return NULL'd. + * + * Example: + * @code + * { + * } + * @endcode + */ +void rtaCommandTransmitStatistics_Release(RtaCommandTransmitStatistics **openPtr); + +/** + * Returns the time period to use when writing statistics + * + * The time period is how often the transport will write the statistics to the specified file. + * + * @param [in] transmitStats An allocated RtaCommandTransmitStatistics + * + * @return timeval The value passed to rtaCommandTransmitStatistics_Create(). + * + * Example: + * @code + * { + * int stackId = 7; + * struct timeval period = { 1, 2 }; + * const char *filename = "filename"; + * RtaCommandOpenConnection *transmitStats = rtaCommandTransmitStatistics_Create(period, filename); + * struct timeval testValue = rtaCommandTransmitStatistics_GetPeriod(transmitStats); + * assertTrue(timercmp(&testValue, &period, ==), "Wrong period"); + * rtaCommandTransmitStatistics_Release(&transmitStats); + * } + * @endcode + */ +struct timeval rtaCommandTransmitStatistics_GetPeriod(const RtaCommandTransmitStatistics *transmitStats); + +/** + * Returns the filename to use when writing statistics + * + * The filename to append statistics to. + * + * @param [in] transmitStats An allocated RtaCommandTransmitStatistics + * + * @return timeval The value passed to rtaCommandTransmitStatistics_Create(). + * + * Example: + * @code + * { + * int stackId = 7; + * struct timeval period = { 1, 2 }; + * const char *filename = "filename"; + * RtaCommandOpenConnection *transmitStats = rtaCommandTransmitStatistics_Create(period, filename); + * struct timeval testValue = rtaCommandTransmitStatistics_GetPeriod(transmitStats); + * assertTrue(strcmp(filename, testValue) == 0, "Wrong filename"); + * rtaCommandTransmitStatistics_Release(&transmitStats); + * } + * @endcode + */ +const char *rtaCommandTransmitStatistics_GetFilename(const RtaCommandTransmitStatistics *transmitStats); +#endif // Libccnx_rta_CommandTransmitStatistics_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/.gitignore b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/.gitignore new file mode 100644 index 00000000..73fd1137 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/.gitignore @@ -0,0 +1,7 @@ +test_rta_Command +test_rta_CommandCloseConnection +test_rta_CommandCreateProtocolStack +test_rta_CommandDestroyProtocolStack +test_rta_CommandOpenConnection +test_rta_CommandTransmitStatistics + diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/CMakeLists.txt b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/CMakeLists.txt new file mode 100644 index 00000000..d3a5f009 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/CMakeLists.txt @@ -0,0 +1,18 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_rta_Command + test_rta_CommandCreateProtocolStack + test_rta_CommandOpenConnection + test_rta_CommandCloseConnection + test_rta_CommandDestroyProtocolStack + test_rta_CommandTransmitStatistics +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_Command.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_Command.c new file mode 100644 index 00000000..c27cb41d --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_Command.c @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_Command.c" + +#include <inttypes.h> +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(rta_Command) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Command) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Command) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Acquire); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Release); + + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_CreateShutdownFramework); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_CreateCloseConnection); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_CreateCreateProtocolStack); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_CreateDestroyProtocolStack); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_CreateOpenConnection); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_CreateTransmitStatistics); + + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_GetCloseConnection); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_GetCreateProtocolStack); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_GetDestroyProtocolStack); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_GetOpenConnection); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_GetTransmitStatistics); + + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsCloseConnection_True); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsCreateProtocolStack_True); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsDestroyProtocolStack_True); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsOpenConnection_True); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsShutdownFramework_True); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsTransmitStatistics_True); + + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsCloseConnection_False); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsCreateProtocolStack_False); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsDestroyProtocolStack_False); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsOpenConnection_False); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsShutdownFramework_False); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_IsTransmitStatistics_False); + + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Read_Single); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Write_Single); + + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Read_Underflow); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Write_Overflow); + + // miscellaneous functions + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Display); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaCommand_Acquire) +{ + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + size_t firstRefCount = parcObject_GetReferenceCount(command); + + RtaCommand *second = rtaCommand_Acquire(command); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + assertTrue(secondRefCount == firstRefCount + 1, "Wrong refcount after acquire, got %zu expected %zu", secondRefCount, firstRefCount + 1); + + rtaCommand_Release(&command); + rtaCommand_Release(&second); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_Release) +{ + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + + RtaCommand *second = rtaCommand_Acquire(command); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + rtaCommand_Release(&command); + size_t thirdRefCount = parcObject_GetReferenceCount(second); + + assertTrue(thirdRefCount == secondRefCount - 1, "Wrong refcount after release, got %zu expected %zu", thirdRefCount, secondRefCount - 1); + + rtaCommand_Release(&second); +} + +// ======================= +// Create/From operations + +LONGBOW_TEST_CASE(Global, rtaCommand_CreateShutdownFramework) +{ + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + assertNotNull(command, "Got null command from create"); + assertTrue(command->type == RtaCommandType_ShutdownFramework, "Command is not shutdown framework"); + rtaCommand_Release(&command); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_CreateCloseConnection) +{ + RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(77); + RtaCommand *command = rtaCommand_CreateCloseConnection(closeConnection); + assertNotNull(command, "Got null command from create"); + assertTrue(command->type == RtaCommandType_CloseConnection, "Command is not CloseConnection"); + rtaCommand_Release(&command); + rtaCommandCloseConnection_Release(&closeConnection); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_CreateCreateProtocolStack) +{ + CCNxStackConfig *config = ccnxStackConfig_Create(); + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(111, config); + + RtaCommand *command = rtaCommand_CreateCreateProtocolStack(createStack); + assertNotNull(command, "Got null command from create"); + assertTrue(command->type == RtaCommandType_CreateProtocolStack, "Command is not CreateProtocolStack"); + + rtaCommand_Release(&command); + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_CreateDestroyProtocolStack) +{ + RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(77); + RtaCommand *command = rtaCommand_CreateDestroyProtocolStack(destroyStack); + assertNotNull(command, "Got null command from create"); + assertTrue(command->type == RtaCommandType_DestroyProtocolStack, "Command is not DestroyProtocolStack"); + rtaCommand_Release(&command); + rtaCommandDestroyProtocolStack_Release(&destroyStack); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_CreateOpenConnection) +{ + RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(111, 2341, 2450987, NULL); + RtaCommand *command = rtaCommand_CreateOpenConnection(openConnection); + assertNotNull(command, "Got null command from create"); + assertTrue(command->type == RtaCommandType_OpenConnection, "Command is not OpenConnection"); + rtaCommand_Release(&command); + rtaCommandOpenConnection_Release(&openConnection); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_CreateTransmitStatistics) +{ + RtaCommandTransmitStatistics *transmitStats = rtaCommandTransmitStatistics_Create((struct timeval) { 1, 2 }, "filename"); + RtaCommand *command = rtaCommand_CreateTransmitStatistics(transmitStats); + assertNotNull(command, "Got null command from create"); + assertTrue(command->type == RtaCommandType_TransmitStatistics, "Command is not TransmitStatistics"); + rtaCommand_Release(&command); + rtaCommandTransmitStatistics_Release(&transmitStats); +} + +// ======================= +// GET operations + +LONGBOW_TEST_CASE(Global, rtaCommand_GetCloseConnection) +{ + RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(77); + RtaCommand *command = rtaCommand_CreateCloseConnection(closeConnection); + + const RtaCommandCloseConnection *test = rtaCommand_GetCloseConnection(command); + assertTrue(test == closeConnection, "Wrong pointers, got %p expected %p", (void *) test, (void *) closeConnection); + + rtaCommand_Release(&command); + rtaCommandCloseConnection_Release(&closeConnection); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_GetCreateProtocolStack) +{ + CCNxStackConfig *config = ccnxStackConfig_Create(); + + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(111, config); + RtaCommand *command = rtaCommand_CreateCreateProtocolStack(createStack); + + const RtaCommandCreateProtocolStack *test = rtaCommand_GetCreateProtocolStack(command); + assertTrue(test == createStack, "Wrong pointers, got %p expected %p", (void *) test, (void *) createStack); + + rtaCommand_Release(&command); + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_GetDestroyProtocolStack) +{ + RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(77); + RtaCommand *command = rtaCommand_CreateDestroyProtocolStack(destroyStack); + + const RtaCommandDestroyProtocolStack *test = rtaCommand_GetDestroyProtocolStack(command); + assertTrue(test == destroyStack, "Wrong pointers, got %p expected %p", (void *) test, (void *) destroyStack); + + rtaCommand_Release(&command); + rtaCommandDestroyProtocolStack_Release(&destroyStack); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_GetOpenConnection) +{ + RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(111, 2341, 2450987, NULL); + RtaCommand *command = rtaCommand_CreateOpenConnection(openConnection); + + const RtaCommandOpenConnection *test = rtaCommand_GetOpenConnection(command); + assertTrue(test == openConnection, "Wrong pointers, got %p expected %p", (void *) test, (void *) openConnection); + + rtaCommand_Release(&command); + rtaCommandOpenConnection_Release(&openConnection); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_GetTransmitStatistics) +{ + RtaCommandTransmitStatistics *transmitStats = rtaCommandTransmitStatistics_Create((struct timeval) { 1, 2 }, "filename"); + RtaCommand *command = rtaCommand_CreateTransmitStatistics(transmitStats); + + const RtaCommandTransmitStatistics *test = rtaCommand_GetTransmitStatistics(command); + assertTrue(test == transmitStats, "Wrong pointers, got %p expected %p", (void *) test, (void *) transmitStats); + + rtaCommand_Release(&command); + rtaCommandTransmitStatistics_Release(&transmitStats); +} + +// ======================= +// IsX operations + +LONGBOW_TEST_CASE(Global, rtaCommand_IsCloseConnection_True) +{ + RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(77); + RtaCommand *command = rtaCommand_CreateCloseConnection(closeConnection); + assertTrue(rtaCommand_IsCloseConnection(command), "Command is not CloseConnection"); + rtaCommand_Release(&command); + rtaCommandCloseConnection_Release(&closeConnection); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_IsCreateProtocolStack_True) +{ + CCNxStackConfig *config = ccnxStackConfig_Create(); + + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(111, config); + RtaCommand *command = rtaCommand_CreateCreateProtocolStack(createStack); + assertTrue(rtaCommand_IsCreateProtocolStack(command), "Command is not CreateProtocolStack"); + rtaCommand_Release(&command); + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_IsDestroyProtocolStack_True) +{ + RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(77); + RtaCommand *command = rtaCommand_CreateDestroyProtocolStack(destroyStack); + assertTrue(rtaCommand_IsDestroyProtocolStack(command), "Command is not DestroyProtocolStack"); + rtaCommand_Release(&command); + rtaCommandDestroyProtocolStack_Release(&destroyStack); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_IsOpenConnection_True) +{ + RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(111, 2341, 2450987, NULL); + RtaCommand *command = rtaCommand_CreateOpenConnection(openConnection); + assertTrue(rtaCommand_IsOpenConnection(command), "Command is not OpenConnection"); + rtaCommand_Release(&command); + rtaCommandOpenConnection_Release(&openConnection); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_IsShutdownFramework_True) +{ + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + assertTrue(rtaCommand_IsShutdownFramework(command), "Command is not shutdown framework"); + rtaCommand_Release(&command); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_IsTransmitStatistics_True) +{ + RtaCommandTransmitStatistics *transmitStats = rtaCommandTransmitStatistics_Create((struct timeval) { 1, 2 }, "filename"); + RtaCommand *command = rtaCommand_CreateTransmitStatistics(transmitStats); + assertTrue(rtaCommand_IsTransmitStatistics(command), "Command is not TransmitStatistics"); + rtaCommand_Release(&command); + rtaCommandTransmitStatistics_Release(&transmitStats); +} + + +LONGBOW_TEST_CASE(Global, rtaCommand_IsCloseConnection_False) +{ + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + assertFalse(rtaCommand_IsCloseConnection(command), "Command is not CloseConnection, should be false"); + rtaCommand_Release(&command); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_IsCreateProtocolStack_False) +{ + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + assertFalse(rtaCommand_IsCreateProtocolStack(command), "Command is not CreateProtocolStack, should be false"); + rtaCommand_Release(&command); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_IsDestroyProtocolStack_False) +{ + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + assertFalse(rtaCommand_IsDestroyProtocolStack(command), "Command is not DestroyProtocolStack, should be false"); + rtaCommand_Release(&command); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_IsOpenConnection_False) +{ + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + assertFalse(rtaCommand_IsOpenConnection(command), "Command is not OpenConnection, should be false"); + rtaCommand_Release(&command); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_IsShutdownFramework_False) +{ + RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(77); + RtaCommand *command = rtaCommand_CreateCloseConnection(closeConnection); + assertFalse(rtaCommand_IsShutdownFramework(command), "Command is not ShutdownFramework, should be false"); + rtaCommand_Release(&command); + rtaCommandCloseConnection_Release(&closeConnection); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_IsTransmitStatistics_False) +{ + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + assertFalse(rtaCommand_IsTransmitStatistics(command), "Command is not TransmitStatistics, should be false"); + rtaCommand_Release(&command); +} + +// =========================== +// IO operations + +/* + * Read a single command from a ring buffer + */ +LONGBOW_TEST_CASE(Global, rtaCommand_Read_Single) +{ + PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(4, NULL); + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + + bool success = parcRingBuffer1x1_Put(ring, command); + assertTrue(success, "Failed to put command in to ring buffer"); + + RtaCommand *test = rtaCommand_Read(ring); + assertTrue(test == command, "Wrong pointers, got %p expected %p", (void *) test, (void *) command); + + rtaCommand_Release(&command); + parcRingBuffer1x1_Release(&ring); +} + +/* + * Write a single command to a ring buffer and make sure it works + */ +LONGBOW_TEST_CASE(Global, rtaCommand_Write_Single) +{ + PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(4, NULL); + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + + bool success = rtaCommand_Write(command, ring); + assertTrue(success, "Failed to put command in to ring buffer"); + + // We should now have two references + assertTrue(parcObject_GetReferenceCount(command) == 2, "Wrong refernce count, got %" PRIu64 " expected %u", parcObject_GetReferenceCount(command), 2); + + RtaCommand *test = rtaCommand_Read(ring); + assertTrue(test == command, "Wrong pointers, got %p expected %p", (void *) test, (void *) command); + + rtaCommand_Release(&command); + rtaCommand_Release(&test); + parcRingBuffer1x1_Release(&ring); +} + +/* + * Read from an empty ring buffer + */ +LONGBOW_TEST_CASE(Global, rtaCommand_Read_Underflow) +{ + PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(4, NULL); + + RtaCommand *test = rtaCommand_Read(ring); + assertNull(test, "Should have gotten NULL read from an empty ring buffer"); + + parcRingBuffer1x1_Release(&ring); +} + +/* + * Write beyond the capacity of the ring buffer + */ +LONGBOW_TEST_CASE(Global, rtaCommand_Write_Overflow) +{ + // The ring will store up to (ringSize-1) elements, so we can only + // have 3 items in a ring of size 4 + unsigned ringSize = 4; + RtaCommand *commandArray[ringSize]; + + PARCRingBuffer1x1 *ring = parcRingBuffer1x1_Create(ringSize, NULL); + + for (int i = 0; i < ringSize; i++) { + commandArray[i] = rtaCommand_CreateShutdownFramework(); + } + + for (int i = 0; i < ringSize - 1; i++) { + bool success = rtaCommand_Write(commandArray[i], ring); + assertTrue(success, "Failed to put command in to ring buffer"); + } + + // now put the one that will not fit + bool shouldFail = rtaCommand_Write(commandArray[ringSize - 1], ring); + assertFalse(shouldFail, "Writing overflow item should have failed"); + + // now make sure we read off all the right items + for (int i = 0; i < ringSize - 1; i++) { + RtaCommand *test = rtaCommand_Read(ring); + assertTrue(test == commandArray[i], "Wrong pointers, got %p expected %p", (void *) test, (void *) commandArray[i]); + rtaCommand_Release(&test); + } + + for (int i = 0; i < ringSize; i++) { + rtaCommand_Release(&commandArray[i]); + } + parcRingBuffer1x1_Release(&ring); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_Display) +{ + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + rtaCommand_Display(command, 3); + rtaCommand_Release(&command); +} + +// =================================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Command); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandCloseConnection.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandCloseConnection.c new file mode 100644 index 00000000..f6c0a3a3 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandCloseConnection.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_CommandCloseConnection.c" + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(rta_CommandCloseConnection) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_CommandCloseConnection) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_CommandCloseConnection) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCloseConnection_Acquire); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCloseConnection_Create); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCloseConnection_GetApiNotifierFd); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCloseConnection_Release); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaCommandCloseConnection_Acquire) +{ + int apiNotifierFd = 7; + RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(apiNotifierFd); + size_t firstRefCount = parcObject_GetReferenceCount(closeConnection); + + RtaCommandCloseConnection *second = rtaCommandCloseConnection_Acquire(closeConnection); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + assertTrue(secondRefCount == firstRefCount + 1, "Wrong refcount after acquire, got %zu expected %zu", secondRefCount, firstRefCount + 1); + + rtaCommandCloseConnection_Release(&closeConnection); + rtaCommandCloseConnection_Release(&second); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCloseConnection_Create) +{ + int apiNotifierFd = 7; + RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(apiNotifierFd); + assertNotNull(closeConnection, "Got null from create"); + assertTrue(closeConnection->apiNotifierFd == apiNotifierFd, "Internal apiSocket wrong, got %d expected %d", closeConnection->apiNotifierFd, apiNotifierFd); + rtaCommandCloseConnection_Release(&closeConnection); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCloseConnection_GetApiNotifierFd) +{ + int apiNotifierFd = 7; + RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(apiNotifierFd); + + int testFd = rtaCommandCloseConnection_GetApiNotifierFd(closeConnection); + assertTrue(testFd == apiNotifierFd, "Wrong value, got %d expected %d", testFd, apiNotifierFd); + + rtaCommandCloseConnection_Release(&closeConnection); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCloseConnection_Release) +{ + int apiNotifierFd = 7; + RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(apiNotifierFd); + + RtaCommandCloseConnection *second = rtaCommandCloseConnection_Acquire(closeConnection); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + rtaCommandCloseConnection_Release(&closeConnection); + size_t thirdRefCount = parcObject_GetReferenceCount(second); + + assertTrue(thirdRefCount == secondRefCount - 1, "Wrong refcount after release, got %zu expected %zu", thirdRefCount, secondRefCount - 1); + + rtaCommandCloseConnection_Release(&second); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_CommandCloseConnection); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandCreateProtocolStack.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandCreateProtocolStack.c new file mode 100644 index 00000000..948477f8 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandCreateProtocolStack.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_CommandCreateProtocolStack.c" + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <parc/testing/parc_ObjectTesting.h> + +LONGBOW_TEST_RUNNER(rta_CommandCreateProtocolStack) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_CommandCreateProtocolStack) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_CommandCreateProtocolStack) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCreateProtocolStack_Acquire); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCreateProtocolStack_Create); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCreateProtocolStack_IsValid); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCreateProtocolStack_IsValid_NULL); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCreateProtocolStack_IsValid_BadCCNxStackConfig); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCreateProtocolStack_AssertValid); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCreateProtocolStack_GetConfig); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCreateProtocolStack_GetStackId); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCreateProtocolStack_GetStackConfig); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandCreateProtocolStack_Release); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaCommandCreateProtocolStack_Acquire) +{ + int stackId = 7; + CCNxStackConfig *config = ccnxStackConfig_Create(); + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + + parcObjectTesting_AssertAcquire(createStack); + + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCreateProtocolStack_Create) +{ + int stackId = 7; + CCNxStackConfig *config = ccnxStackConfig_Create(); + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + assertNotNull(createStack, "Expected rtaCommandCreateProtocolStack_Create to return non-NULL."); + + assertTrue(createStack->stackId == stackId, "Expected stackId %d, actual %d", stackId, createStack->stackId); + + assertTrue(ccnxStackConfig_Equals(config, createStack->config), + "ProtocolStackConfig instances are not equal"); + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCreateProtocolStack_IsValid) +{ + int stackId = 7; + CCNxStackConfig *config = ccnxStackConfig_Create(); + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + + assertTrue(rtaCommandCreateProtocolStack_IsValid(createStack), + "Expected rtaCommandCreateProtocolStack_Create to return a valid instance."); + + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCreateProtocolStack_IsValid_NULL) +{ + RtaCommandCreateProtocolStack *createStack = NULL; + + assertFalse(rtaCommandCreateProtocolStack_IsValid(createStack), + "Expected rtaCommandCreateProtocolStack_Create to return a valid instance."); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCreateProtocolStack_IsValid_BadCCNxStackConfig) +{ + int stackId = 7; + CCNxStackConfig *config = ccnxStackConfig_Create(); + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + CCNxStackConfig *original = createStack->config; + createStack->config = NULL; // Make it bad. + assertFalse(rtaCommandCreateProtocolStack_IsValid(createStack), + "Expected rtaCommandCreateProtocolStack_Create to return a valid instance."); + createStack->config = original; + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCreateProtocolStack_AssertValid) +{ + int stackId = 7; + CCNxStackConfig *config = ccnxStackConfig_Create(); + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + + rtaCommandCreateProtocolStack_AssertValid(createStack); + + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCreateProtocolStack_GetStackConfig) +{ + int stackId = 7; + CCNxStackConfig *config = ccnxStackConfig_Create(); + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + + CCNxStackConfig *actual = rtaCommandCreateProtocolStack_GetStackConfig(createStack); + + assertTrue(ccnxStackConfig_Equals(config, actual), + "CCNxStackConfig instances are not equal"); + + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCreateProtocolStack_GetConfig) +{ + int stackId = 7; + CCNxStackConfig *config = ccnxStackConfig_Create(); + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + + assertTrue(ccnxStackConfig_Equals(config, createStack->config), + "ProtocolStackConfig instances are not equal"); + + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCreateProtocolStack_GetStackId) +{ + int stackId = 7; + CCNxStackConfig *config = ccnxStackConfig_Create(); + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + + int testStackId = rtaCommandCreateProtocolStack_GetStackId(createStack); + assertTrue(testStackId == stackId, "Wrong value, got %d expected %d", testStackId, stackId); + + rtaCommandCreateProtocolStack_Release(&createStack); + ccnxStackConfig_Release(&config); +} + +LONGBOW_TEST_CASE(Global, rtaCommandCreateProtocolStack_Release) +{ + int stackId = 7; + CCNxStackConfig *config = ccnxStackConfig_Create(); + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, config); + + RtaCommandCreateProtocolStack *second = rtaCommandCreateProtocolStack_Acquire(createStack); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + rtaCommandCreateProtocolStack_Release(&createStack); + size_t thirdRefCount = parcObject_GetReferenceCount(second); + + assertTrue(thirdRefCount == secondRefCount - 1, + "Wrong refcount after release, got %zu expected %zu", thirdRefCount, secondRefCount - 1); + + rtaCommandCreateProtocolStack_Release(&second); + ccnxStackConfig_Release(&config); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_CommandCreateProtocolStack); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandDestroyProtocolStack.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandDestroyProtocolStack.c new file mode 100644 index 00000000..37e1128d --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandDestroyProtocolStack.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_CommandDestroyProtocolStack.c" + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(rta_CommandDestroyProtocolStack) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_CommandDestroyProtocolStack) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_CommandDestroyProtocolStack) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaCommandDestroyProtocolStack_Acquire); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandDestroyProtocolStack_Create); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandDestroyProtocolStack_GetStackId); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandDestroyProtocolStack_Release); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaCommandDestroyProtocolStack_Acquire) +{ + int stackId = 7; + RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(stackId); + size_t firstRefCount = parcObject_GetReferenceCount(destroyStack); + + RtaCommandDestroyProtocolStack *second = rtaCommandDestroyProtocolStack_Acquire(destroyStack); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + assertTrue(secondRefCount == firstRefCount + 1, "Wrong refcount after acquire, got %zu expected %zu", secondRefCount, firstRefCount + 1); + + rtaCommandDestroyProtocolStack_Release(&destroyStack); + rtaCommandDestroyProtocolStack_Release(&second); +} + +LONGBOW_TEST_CASE(Global, rtaCommandDestroyProtocolStack_Create) +{ + int stackId = 7; + RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(stackId); + assertNotNull(destroyStack, "Got null from create"); + assertTrue(destroyStack->stackId == stackId, "Internal stackId wrong, got %d expected %d", destroyStack->stackId, stackId); + rtaCommandDestroyProtocolStack_Release(&destroyStack); +} + +LONGBOW_TEST_CASE(Global, rtaCommandDestroyProtocolStack_GetStackId) +{ + int stackId = 7; + RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(stackId); + + int testStackId = rtaCommandDestroyProtocolStack_GetStackId(destroyStack); + assertTrue(testStackId == stackId, "Wrong value, got %d expected %d", testStackId, stackId); + + rtaCommandDestroyProtocolStack_Release(&destroyStack); +} + +LONGBOW_TEST_CASE(Global, rtaCommandDestroyProtocolStack_Release) +{ + int stackId = 7; + RtaCommandDestroyProtocolStack *destroyStack = rtaCommandDestroyProtocolStack_Create(stackId); + + RtaCommandDestroyProtocolStack *second = rtaCommandDestroyProtocolStack_Acquire(destroyStack); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + rtaCommandDestroyProtocolStack_Release(&destroyStack); + size_t thirdRefCount = parcObject_GetReferenceCount(second); + + assertTrue(thirdRefCount == secondRefCount - 1, "Wrong refcount after release, got %zu expected %zu", thirdRefCount, secondRefCount - 1); + + rtaCommandDestroyProtocolStack_Release(&second); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_CommandDestroyProtocolStack); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandOpenConnection.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandOpenConnection.c new file mode 100644 index 00000000..30d77931 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandOpenConnection.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_CommandOpenConnection.c" + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +typedef struct test_data { + int stackId; + int apiNotifierFd; + int transportNotifierFd; + PARCJSON *config; + + RtaCommandOpenConnection *openConnection; +} TestData; + +static TestData * +_createTestData(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + + data->stackId = 7; + data->apiNotifierFd = 11; + data->transportNotifierFd = 10029; + data->config = parcJSON_Create(); + + data->openConnection = rtaCommandOpenConnection_Create(data->stackId, data->apiNotifierFd, data->transportNotifierFd, data->config); + + return data; +} + +static void +_destroyTestData(TestData **dataPtr) +{ + TestData *data = *dataPtr; + + rtaCommandOpenConnection_Release(&data->openConnection); + parcJSON_Release(&data->config); + parcMemory_Deallocate((void **) &data); + + *dataPtr = NULL; +} + +// ============================================================= + +LONGBOW_TEST_RUNNER(rta_CommandOpenConnection) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_CommandOpenConnection) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_CommandOpenConnection) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaCommandOpenConnection_Acquire); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandOpenConnection_Create); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandOpenConnection_GetApiNotifierFd); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandOpenConnection_GetConfig); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandOpenConnection_GetStackId); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandOpenConnection_GetTransportNotifierFd); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandOpenConnection_Release); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaCommandOpenConnection_Acquire) +{ + TestData *data = _createTestData(); + + size_t firstRefCount = parcObject_GetReferenceCount(data->openConnection); + + RtaCommandOpenConnection *second = rtaCommandOpenConnection_Acquire(data->openConnection); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + assertTrue(secondRefCount == firstRefCount + 1, "Wrong refcount after acquire, got %zu expected %zu", secondRefCount, firstRefCount + 1); + + rtaCommandOpenConnection_Release(&second); + _destroyTestData(&data); +} + +LONGBOW_TEST_CASE(Global, rtaCommandOpenConnection_Create) +{ + TestData *data = _createTestData(); + assertNotNull(data->openConnection, "Got null from create"); + assertTrue(data->openConnection->stackId == data->stackId, "Internal stackId wrong, got %d expected %d", + data->openConnection->stackId, data->stackId); + assertTrue(data->openConnection->apiNotifierFd == data->apiNotifierFd, "Internal apiNotifierFd wrong, got %d expected %d", + data->openConnection->apiNotifierFd, data->apiNotifierFd); + assertTrue(data->openConnection->transportNotifierFd == data->transportNotifierFd, "Internal transportNotifierFd wrong, got %d expected %d", + data->openConnection->transportNotifierFd, data->transportNotifierFd); + assertTrue(parcJSON_Equals(data->openConnection->config, data->config), "JSON configs are not equal"); + _destroyTestData(&data); +} + +LONGBOW_TEST_CASE(Global, rtaCommandOpenConnection_GetApiNotifierFd) +{ + TestData *data = _createTestData(); + + int testApiFd = rtaCommandOpenConnection_GetApiNotifierFd(data->openConnection); + assertTrue(testApiFd == data->apiNotifierFd, "Wrong value, got %d expected %d", testApiFd, data->apiNotifierFd); + + _destroyTestData(&data); +} + +LONGBOW_TEST_CASE(Global, rtaCommandOpenConnection_GetConfig) +{ + TestData *data = _createTestData(); + + PARCJSON *testConfig = rtaCommandOpenConnection_GetConfig(data->openConnection); + assertTrue(parcJSON_Equals(data->config, testConfig), "Configurations do not match"); + + _destroyTestData(&data); +} + +LONGBOW_TEST_CASE(Global, rtaCommandOpenConnection_GetStackId) +{ + TestData *data = _createTestData(); + + int testStackId = rtaCommandOpenConnection_GetStackId(data->openConnection); + assertTrue(testStackId == data->stackId, "Wrong value, got %d expected %d", testStackId, data->stackId); + + _destroyTestData(&data); +} + +LONGBOW_TEST_CASE(Global, rtaCommandOpenConnection_GetTransportNotifierFd) +{ + TestData *data = _createTestData(); + + int testTransportFd = rtaCommandOpenConnection_GetTransportNotifierFd(data->openConnection); + assertTrue(testTransportFd == data->transportNotifierFd, "Wrong value, got %d expected %d", testTransportFd, data->transportNotifierFd); + + _destroyTestData(&data); +} + +LONGBOW_TEST_CASE(Global, rtaCommandOpenConnection_Release) +{ + TestData *data = _createTestData(); + + RtaCommandOpenConnection *second = rtaCommandOpenConnection_Acquire(data->openConnection); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + rtaCommandOpenConnection_Release(&second); + size_t thirdRefCount = parcObject_GetReferenceCount(data->openConnection); + + assertTrue(thirdRefCount == secondRefCount - 1, "Wrong refcount after release, got %zu expected %zu", thirdRefCount, secondRefCount - 1); + + _destroyTestData(&data); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_CommandOpenConnection); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandTransmitStatistics.c b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandTransmitStatistics.c new file mode 100644 index 00000000..cc312e5a --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandTransmitStatistics.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_CommandTransmitStatistics.c" + +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +#include <sys/time.h> + +#define MAX_FILENAME 1024 + +typedef struct test_data { + struct timeval period; + char filename[MAX_FILENAME]; + + RtaCommandTransmitStatistics *transmitStats; +} TestData; + +static TestData * +_createTestData(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->period = (struct timeval) { .tv_sec = 1234, .tv_usec = 2389484 }; + snprintf(data->filename, MAX_FILENAME, "Miss Piggy"); + + data->transmitStats = rtaCommandTransmitStatistics_Create(data->period, data->filename); + + return data; +} + +static void +_destroyTestData(TestData **dataPtr) +{ + TestData *data = *dataPtr; + rtaCommandTransmitStatistics_Release(&data->transmitStats); + parcMemory_Deallocate((void **) &data); + *dataPtr = NULL; +} + +// ============================================================= +LONGBOW_TEST_RUNNER(rta_CommandTransmitStatistics) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_CommandTransmitStatistics) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_CommandTransmitStatistics) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaCommandTransmitStatistics_Acquire); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandTransmitStatistics_Create); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandTransmitStatistics_GetFilename); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandTransmitStatistics_GetPeriod); + LONGBOW_RUN_TEST_CASE(Global, rtaCommandTransmitStatistics_Release); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaCommandTransmitStatistics_Acquire) +{ + TestData *data = _createTestData(); + + size_t firstRefCount = parcObject_GetReferenceCount(data->transmitStats); + + RtaCommandTransmitStatistics *second = rtaCommandTransmitStatistics_Acquire(data->transmitStats); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + assertTrue(secondRefCount == firstRefCount + 1, "Wrong refcount after acquire, got %zu expected %zu", secondRefCount, firstRefCount + 1); + + rtaCommandTransmitStatistics_Release(&second); + _destroyTestData(&data); +} + +LONGBOW_TEST_CASE(Global, rtaCommandTransmitStatistics_Create) +{ + TestData *data = _createTestData(); + assertNotNull(data->transmitStats, "Got null from create"); + + assertTrue(timercmp(&data->period, &data->transmitStats->period, ==), "Period values not equal"); + assertTrue(strcmp(data->filename, data->transmitStats->filename) == 0, "Filenames not equal"); + _destroyTestData(&data); +} + +LONGBOW_TEST_CASE(Global, rtaCommandTransmitStatistics_GetFilename) +{ + TestData *data = _createTestData(); + + const char *testFilename = rtaCommandTransmitStatistics_GetFilename(data->transmitStats); + assertTrue(strcmp(data->filename, testFilename) == 0, "Filenames not equal"); + + _destroyTestData(&data); +} + +LONGBOW_TEST_CASE(Global, rtaCommandTransmitStatistics_GetPeriod) +{ + TestData *data = _createTestData(); + + struct timeval testPeriod = rtaCommandTransmitStatistics_GetPeriod(data->transmitStats); + assertTrue(timercmp(&data->period, &testPeriod, ==), "Period values not equal"); + + _destroyTestData(&data); +} + +LONGBOW_TEST_CASE(Global, rtaCommandTransmitStatistics_Release) +{ + TestData *data = _createTestData(); + + RtaCommandTransmitStatistics *second = rtaCommandTransmitStatistics_Acquire(data->transmitStats); + size_t secondRefCount = parcObject_GetReferenceCount(second); + + rtaCommandTransmitStatistics_Release(&second); + size_t thirdRefCount = parcObject_GetReferenceCount(data->transmitStats); + + assertTrue(thirdRefCount == secondRefCount - 1, "Wrong refcount after release, got %zu expected %zu", thirdRefCount, secondRefCount - 1); + + _destroyTestData(&data); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_CommandTransmitStatistics); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/component_Vegas.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/component_Vegas.c new file mode 100644 index 00000000..70bcec63 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/component_Vegas.c @@ -0,0 +1,673 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// Source code layout: +// - component_Vegas.c: the component wrapper and session multiplexing +// - vegas_Session.c: code for a specific basename session +// - vegas_Segment.c: code for specific segment operations + +/** + * Component behavior + * =================== + * This component provides flow-controlled in-order delivery of segmented + * content objects using a sequential segment number in the last component + * of the object name. + * + * The state machine described here is within a single RtaConnection. Separate + * connections are independent. + * + * Down Stack Behavior + * ------------------------ + * When an interest comes down the stack, it will initiate a flow-controlled + * session. If the last component of the interest name is a segment number, + * that is the starting segment number. Otherwise, we assume the interest + * name is the base name for a segmented object, including the version number. + * + * Other types of messages coming down the stack (e.g. control or content objects) + * are passed down the stack unaltered. + * + * If an interest comes down that represents a subset of an existing flow (i.e. + * it has a segment number beyond the current starting segment of the flow contol + * window), the window is advanced to that segment number and any un-delivered + * content objects are dropped. + * + * If an interest comes down that represents a superset of an existing flow + * (i.e. it has a starting segment number less than the current window), the + * current flow control sessions is re-wound to the lower sequence number + * and continues from there. + * + * Up Stack Behavior + * ------------------------ + * Non-content objects (e.g. control and interests) are passed up the stack unmodified. + * + * A content object that matches a flow control session is managed by the session. + * They are only passed up the stack in-order, and will be dropped if they are outside + * the window. + * + * A content object that does not match a flow control session is dropped. That's because + * the only interests we send down the stack are our own for flow controlled sessions, so + * no content object should go up the stack unless its part of a flow controlled session. + * + * Control Messages + * ------------------------ + * The API may cancel flow control sessions in several ways: + * + * 1) Close the Connection. This will cancel all in progress sessions and drop + * any un-delivered objects. + * + * 2) Send a Control message down the stack with the base name to cancel. The + * name is considered the base name of the flow and does not depend on the + * starting segment number. + * + * { "CPI_CANCEL_FLOW" : { "FLOW_NAME" : <base name w/o segment number> } } + * + * Implementation Notes + * ========================= + * For each RtaConnection, there's a {@code struct fc_connection_state}. This + * contains a list of in-progress sessions indexed by the hash of the base name + * (name up to but not including final segment). Right now, it's a linked list + * but should be implemented as a hash table. + * + * Each session is represented by a {@code struct fc_session}. + * + * Each entry in the flow control window is a {@code fc_window_entry}. + * + * session->window_head and session->window_tail define the limits of the + * congestion window. Everything in the interval [head, tail) is expressed + * as an interest. The size of that interval may be larger than the + * congestion window cwnd if we're decreaed the window. We never decrease + * tail, only the cwnd. + * + * + * Flow Control Algorithm + * ========================= + * Based on TCP Vegas. Please read the Vegas paper. We use similar + * variable names to the paper. Code looks quite a bit like the linux + * tcp_vegas.c too. + * + * Here's the differences. In CCN, an Interest is like an ACK token, it + * gives the network permission to send. The node issuing Interests needs + * to pace them to not exceed the network capacity. This is done by + * observing the delay of Content Objects. If the delay grows too quickly, + * then we back off linearly. If the delay is not much above what we expected + * based on the minimum observed delay, we increase linearly. + * + * During slow start, the interest window (still called "cwnd") doubles + * every other RTT until we exceed the slow_start_threshold or the delay + * increases too much. + * + * The RTT is calculated every RTT based on the observed minimum RTT during + * the previous period. + * + * We use RFC6298 Retransmission Timeout (RTO) calculation methods per + * flow control session (object basename). + * + * Just to be clear, there are two timers working. The RTO timer is for + * retransmitting interests if the flow as stalled out. The Vegas RTT + * calculation is for congestion window calculations. + * + * We we receive an out-of-order content object, we'll check the earlier + * segments to see if they have passed the Vegas RTT. If so, we'll + * re-express the interests. + * + * Each time we re-express an Interest, we might decrese the congestion + * window. If the last time the interest was sent was more recent than + * the last time we decreased the congestion window, we'll decrease the + * congestion window. If the last expression of the interest was before + * the most recent window decrease, the window is left alone. This means + * we'll only decreae the window once per re-expression. + */ +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <limits.h> +#include <sys/queue.h> +#include <stdbool.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> + +#include <parc/algol/parc_EventQueue.h> + +#include <ccnx/transport/common/transport_Message.h> +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> +#include <ccnx/transport/transport_rta/components/component_Flowcontrol.h> +#include <ccnx/transport/test_tools/traffic_tools.h> + +#include <ccnx/api/control/controlPlaneInterface.h> +#include <ccnx/api/control/cpi_ControlFacade.h> + +#include "vegas_private.h" + +#include <parc/logging/parc_LogLevel.h> + +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +// =========================================================== + +typedef struct fc_session_holder { + uint64_t basename_hash; + CCNxName *basename; + VegasSession *session; + + // used by fc_connection_state to hold these + // Should change to hashtable on the hash + TAILQ_ENTRY(fc_session_holder) list; +} FcSessionHolder; + +/** + * This is the per-connection state. It allows us to have multiple + * flow control session on one connection for different names + */ +struct vegas_connection_state { + RtaConnection *parent_connection; + RtaFramework *parent_framework; + + TAILQ_HEAD(, fc_session_holder) sessions_head; +}; + + +// =========================================================== + +static int component_Fc_Vegas_Init(RtaProtocolStack *stack); +static int component_Fc_Vegas_Opener(RtaConnection *conn); +static void component_Fc_Vegas_Upcall_Read(PARCEventQueue *, PARCEventType event, void *conn); +static void component_Fc_Vegas_Downcall_Read(PARCEventQueue *, PARCEventType event, void *conn); +static int component_Fc_Vegas_Closer(RtaConnection *conn); +static int component_Fc_Vegas_Release(RtaProtocolStack *stack); +static void component_Fc_Vegas_StateChange(RtaConnection *conn); + +// Function structs for component variations +RtaComponentOperations flow_vegas_ops = { + .init = component_Fc_Vegas_Init, + .open = component_Fc_Vegas_Opener, + .upcallRead = component_Fc_Vegas_Upcall_Read, + .upcallEvent = NULL, + .downcallRead = component_Fc_Vegas_Downcall_Read, + .downcallEvent = NULL, + .close = component_Fc_Vegas_Closer, + .release = component_Fc_Vegas_Release, + .stateChange = component_Fc_Vegas_StateChange +}; + + +// ====== +// Session related functions +static int vegas_HandleInterest(RtaConnection *conn, TransportMessage *tm); +static FcSessionHolder *vegas_LookupSession(VegasConnectionState *fc, TransportMessage *tm); +static FcSessionHolder *vegas_LookupSessionByName(VegasConnectionState *fc, CCNxName *name); + +static FcSessionHolder *vegas_CreateSessionHolder(VegasConnectionState *fc, RtaConnection *conn, + CCNxName *basename, uint64_t name_hash); + +static bool vegas_HandleControl(RtaConnection *conn, CCNxTlvDictionary *controlDictionary, PARCEventQueue *outputQueue); + +// ================================================ + +static int +component_Fc_Vegas_Init(RtaProtocolStack *stack) +{ + // we don't do any stack-wide initialization + return 0; +} + +static int +component_Fc_Vegas_Opener(RtaConnection *conn) +{ + struct vegas_connection_state *fcConnState = parcMemory_AllocateAndClear(sizeof(struct vegas_connection_state)); + assertNotNull(fcConnState, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(struct vegas_connection_state)); + + fcConnState->parent_connection = rtaConnection_Copy(conn); + fcConnState->parent_framework = rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn)); + + TAILQ_INIT(&fcConnState->sessions_head); + + rtaConnection_SetPrivateData(conn, FC_VEGAS, fcConnState); + rtaComponentStats_Increment(rtaConnection_GetStats(conn, FC_VEGAS), STATS_OPENS); + + return 0; +} + +/* + * Read from below. + * These should only be content objects associated with our stream. + * + * Non-content objects are passed up the stack. + */ +static void +component_Fc_Vegas_Upcall_Read(PARCEventQueue *in, PARCEventType event, void *stack_ptr) +{ + TransportMessage *tm; + + while ((tm = rtaComponent_GetMessage(in)) != NULL) { + struct timeval delay = transportMessage_GetDelay(tm); + + RtaConnection *conn = rtaConnection_GetFromTransport(tm); + RtaComponentStats *stats = rtaConnection_GetStats(conn, FC_VEGAS); + + rtaComponentStats_Increment(stats, STATS_UPCALL_IN); + + if (transportMessage_IsControl(tm)) { + PARCEventQueue *out = rtaComponent_GetOutputQueue(conn, FC_VEGAS, RTA_UP); + + if (rtaComponent_PutMessage(out, tm)) { + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + } else { + //TODO + } + } else if (transportMessage_IsContentObject(tm)) { + // this takes ownership of the transport message + VegasConnectionState *fc = rtaConnection_GetPrivateData(conn, FC_VEGAS); + FcSessionHolder *holder = vegas_LookupSession(fc, tm); + + // it's quite possible that we get content objects for sessions that + // no longer exist. They are dropped. + if (holder != NULL) { + vegasSession_ReceiveContentObject(holder->session, tm); + } else { + transportMessage_Destroy(&tm); + } + } else { + PARCEventQueue *out = rtaComponent_GetOutputQueue(conn, FC_VEGAS, RTA_UP); + if (rtaComponent_PutMessage(out, tm)) { + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + } else { + //TODO + } + } + + if (DEBUG_OUTPUT) { + printf("%s total upcall reads in %" PRIu64 " out %" PRIu64 " last delay %.6f\n", + __func__, + rtaComponentStats_Get(stats, STATS_UPCALL_IN), + rtaComponentStats_Get(stats, STATS_UPCALL_OUT), + delay.tv_sec + delay.tv_usec * 1E-6); + } + } +} + +static void +component_Fc_Vegas_Downcall_Read(PARCEventQueue *in, PARCEventType event, void *conn) +{ + RtaProtocolStack *stack = (RtaProtocolStack *) conn; + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(stack, FC_VEGAS, RTA_DOWN); + TransportMessage *tm; + +// printf("%s reading from queue %p\n", __func__, in); + + while ((tm = rtaComponent_GetMessage(in)) != NULL) { + RtaConnection *conn = rtaConnection_GetFromTransport(tm); + RtaComponentStats *stats = rtaConnection_GetStats(conn, FC_VEGAS); + rtaComponentStats_Increment(stats, STATS_DOWNCALL_IN); + + if (transportMessage_IsControl(tm)) { + CCNxTlvDictionary *controlDictionary = transportMessage_GetDictionary(tm); + if (ccnxControlFacade_IsCPI(controlDictionary) && vegas_HandleControl(conn, controlDictionary, in)) { + transportMessage_Destroy(&tm); + } else { + // we did not consume the message, so forward it down + if (rtaComponent_PutMessage(out, tm)) { + rtaComponentStats_Increment(stats, STATS_DOWNCALL_OUT); + } + } + } else if (transportMessage_IsInterest(tm)) { + vegas_HandleInterest(conn, tm); + + // The flow controller consumes Interests going down the stack and will + // start issuing its own interests instead. + transportMessage_Destroy(&tm); + } else { + if (rtaComponent_PutMessage(out, tm)) { + rtaComponentStats_Increment(stats, STATS_DOWNCALL_OUT); + } + } + + if (DEBUG_OUTPUT) { + struct timeval delay = tm ? transportMessage_GetDelay(tm) : (struct timeval) { 0, 0 }; + printf("%s total downcall reads in %" PRIu64 " out %" PRIu64 " last delay %.6f\n", + __func__, + rtaComponentStats_Get(stats, STATS_DOWNCALL_IN), + rtaComponentStats_Get(stats, STATS_DOWNCALL_OUT), + delay.tv_sec + delay.tv_usec * 1E-6); + } + } +} + +static int +component_Fc_Vegas_Closer(RtaConnection *conn) +{ + VegasConnectionState *fcConnState; + + assertNotNull(conn, "Got null connection\n"); + if (conn == NULL) { + return -1; + } + + fcConnState = rtaConnection_GetPrivateData(conn, FC_VEGAS); + + assertNotNull(fcConnState, "could not retrieve private data for FC_VEGAS on connid %u\n", + rtaConnection_GetConnectionId(conn)); + if (fcConnState == NULL) { + return -1; + } + + rtaConnection_Destroy(&fcConnState->parent_connection); + + rtaComponentStats_Increment(rtaConnection_GetStats(conn, FC_VEGAS), STATS_CLOSES); + + // close down all the sessions + while (!TAILQ_EMPTY(&fcConnState->sessions_head)) { + FcSessionHolder *holder = TAILQ_FIRST(&fcConnState->sessions_head); + + vegasSession_Destroy(&holder->session); + + TAILQ_REMOVE(&fcConnState->sessions_head, holder, list); + parcMemory_Deallocate((void **) &holder); + } + + parcMemory_Deallocate((void **) &fcConnState); + + return 0; +} + +static int +component_Fc_Vegas_Release(RtaProtocolStack *stack) +{ + // no stack-wide memory + return 0; +} + +static void +component_Fc_Vegas_StateChange(RtaConnection *conn) +{ + assertNotNull(conn, "Got null connection\n"); + + VegasConnectionState *fcConnState = rtaConnection_GetPrivateData(conn, FC_VEGAS); + assertNotNull(fcConnState, "could not retrieve private data for FC_VEGAS on connid %u\n", + rtaConnection_GetConnectionId(conn)); + + // should replace this with a hash table + FcSessionHolder *holder; + TAILQ_FOREACH(holder, &fcConnState->sessions_head, list) + { + if (vegasSession_GetConnectionId(holder->session) == rtaConnection_GetConnectionId(conn)) { + vegasSession_StateChanged(holder->session); + } + } +} + +// ======================================================================= + +/** + * If the last component is a segment number, it is ignored + */ +static FcSessionHolder * +vegas_LookupSessionByName(VegasConnectionState *fc, CCNxName *name) +{ + uint64_t hash; + FcSessionHolder *holder; + int trim_segnum = 0; + + assertNotNull(name, "Name is null\n"); + if (name == NULL) { + return NULL; + } + + size_t segmentCount = ccnxName_GetSegmentCount(name); + assertTrue(segmentCount > 1, + "expected name with at least 2 components, but only got %zu, name = '%s'\n", + segmentCount, + ccnxName_ToString(name)); + + + if (segmentCount > 0) { + CCNxNameSegment *segment = ccnxName_GetSegment(name, segmentCount - 1); + if (ccnxNameSegment_GetType(segment) == CCNxNameLabelType_CHUNK) { + trim_segnum = 1; + } + } + + hash = ccnxName_LeftMostHashCode(name, segmentCount - trim_segnum); + + if (DEBUG_OUTPUT) { + printf("%s name %p hash %16" PRIX64 "\n", __func__, (void *) name, hash); + ccnxName_Display(name, 0); + } + + // should replace this with a hash table + TAILQ_FOREACH(holder, &fc->sessions_head, list) + { + if (holder->basename_hash == hash) { + return holder; + } + } + + return NULL; +} + +/* + * Precondition: only called for Content Objects. + * If the last component is a segment number, it is ignored + * + * Match the name of the content object to an active flow control session, + * or return NULL if not found. + */ +static FcSessionHolder * +vegas_LookupSession(VegasConnectionState *fc, TransportMessage *tm) +{ + assertTrue(transportMessage_IsContentObject(tm), + "Transport message is not a ContentObject\n"); + + CCNxTlvDictionary *contentObjectDictionary = transportMessage_GetDictionary(tm); + CCNxName *name = ccnxContentObject_GetName(contentObjectDictionary); + + return vegas_LookupSessionByName(fc, name); +} + +// ============================================= + +/* + * Precondition: it's an interest + */ +static int +vegas_HandleInterest(RtaConnection *conn, TransportMessage *tm) +{ + assertTrue(transportMessage_IsInterest(tm), "Transport message is not an interest"); + + VegasConnectionState *fc = rtaConnection_GetPrivateData(conn, FC_VEGAS); + CCNxTlvDictionary *interestDictionary = transportMessage_GetDictionary(tm); + + // we do not modify or destroy this name + CCNxName *original_name = ccnxInterest_GetName(interestDictionary); + + CCNxName *basename = ccnxName_Copy(original_name); + + if (DEBUG_OUTPUT) { + printf("%s orig name %p basename %p\n", __func__, (void *) original_name, (void *) basename); + } + + // can we decode the last component as a segment number? + uint64_t segnum = 0; + bool segnum_found = trafficTools_GetObjectSegmentFromName(basename, &segnum); + if (segnum_found) { + // it is a segment number + ccnxName_Trim(basename, 1); + } + + FcSessionHolder *holder = vegas_LookupSessionByName(fc, basename); + + if (holder == NULL) { + // create a new session + // This takes ownership of the basename + uint64_t name_hash = ccnxName_HashCode(basename); + holder = vegas_CreateSessionHolder(fc, conn, basename, name_hash); + + CCNxInterestInterface *interestImpl = ccnxInterestInterface_GetInterface(interestDictionary); + + uint32_t lifetime = ccnxInterest_GetLifetime(interestDictionary); + + PARCBuffer *keyIdRestriction = ccnxInterest_GetKeyIdRestriction(interestDictionary); // might be NULL + + holder->session = vegasSession_Create(fc, conn, basename, segnum, + interestImpl, lifetime, keyIdRestriction); + + vegasSession_Start(holder->session); + + rtaConnection_SendStatus(conn, + FC_VEGAS, + RTA_UP, + notifyStatusCode_FLOW_CONTROL_STARTED, + original_name, + NULL); + } else { + assertTrue(segnum_found, "Duplicate interest w/o segnum for existing session"); + + if (segnum_found) { + vegasSession_Seek(holder->session, segnum); + } + + ccnxName_Release(&basename); + } + + return 0; +} + +static FcSessionHolder * +vegas_CreateSessionHolder(VegasConnectionState *fc, RtaConnection *conn, CCNxName *basename, uint64_t name_hash) +{ + FcSessionHolder *holder = parcMemory_AllocateAndClear(sizeof(FcSessionHolder)); + assertNotNull(holder, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(FcSessionHolder)); + holder->basename_hash = name_hash; + holder->basename = basename; + holder->session = NULL; + + TAILQ_INSERT_TAIL(&fc->sessions_head, holder, list); + + if (DEBUG_OUTPUT) { + printf("%s created holder %p hash %016" PRIX64 "\n", __func__, (void *) holder, holder->basename_hash); + } + return holder; +} + +/** + * This is called by a session when it is done + */ +void +vegas_EndSession(VegasConnectionState *fc, VegasSession *session) +{ + FcSessionHolder *holder; + + // should replace this with a hash table + TAILQ_FOREACH(holder, &fc->sessions_head, list) + { + if (holder->session == session) { + TAILQ_REMOVE(&fc->sessions_head, holder, list); + break; + } + } + + assertNotNull(holder, "invalid state, got null holder"); + + rtaConnection_SendStatus(fc->parent_connection, + FC_VEGAS, + RTA_UP, + notifyStatusCode_FLOW_CONTROL_FINISHED, + holder->basename, + NULL); + + vegasSession_Destroy(&holder->session); + parcMemory_Deallocate((void **) &holder); +} + +static void +vegas_SendControlPlaneResponse(RtaConnection *conn, CCNxTlvDictionary *controlDictionary, PARCEventQueue *outputQueue) +{ + TransportMessage *tm = transportMessage_CreateFromDictionary(controlDictionary); + + transportMessage_SetInfo(tm, rtaConnection_Copy(conn), rtaConnection_FreeFunc); + + if (rtaComponent_PutMessage(outputQueue, tm)) { + RtaComponentStats *stats = rtaConnection_GetStats(conn, FC_VEGAS); + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + } +} + +/** + * @function vegas_HandleControl + * @abstract Process CPI reqeusts + * @discussion + * <#Discussion#> + * + * @param <#param1#> + * @return true if we consumed the message, false if it should go down the stack + */ +static bool +vegas_HandleControl(RtaConnection *conn, CCNxTlvDictionary *controlDictionary, PARCEventQueue *outputQueue) +{ + bool success = false; + + if (ccnxControlFacade_IsCPI(controlDictionary)) { + PARCJSON *json = ccnxControlFacade_GetJson(controlDictionary); + if (cpi_getCPIOperation2(json) == CPI_CANCEL_FLOW) { + VegasConnectionState *fc = rtaConnection_GetPrivateData(conn, FC_VEGAS); + CCNxName *name = cpiCancelFlow_GetFlowName(json); + + PARCJSON *reply = NULL; + FcSessionHolder *holder = vegas_LookupSessionByName(fc, name); + if (holder != NULL) { + if (DEBUG_OUTPUT) { + char *string = ccnxName_ToString(name); + printf("%s Cancelling flow %s\n", __func__, string); + parcMemory_Deallocate((void **) &string); + } + + TAILQ_REMOVE(&fc->sessions_head, holder, list); + vegasSession_Destroy(&holder->session); + parcMemory_Deallocate((void **) &holder); + + reply = cpiAcks_CreateAck(json); + } else { + if (DEBUG_OUTPUT) { + char *string = ccnxName_ToString(name); + printf("%s got request to cancel unknown flow %s\n", __func__, string); + parcMemory_Deallocate((void **) &string); + } + + reply = cpiAcks_CreateNack(json); + } + CCNxTlvDictionary *response = ccnxControlFacade_CreateCPI(reply); + vegas_SendControlPlaneResponse(conn, response, outputQueue); + ccnxTlvDictionary_Release(&response); + + parcJSON_Release(&reply); + ccnxName_Release(&name); + + // we consume it + success = true; + } + } + return success; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/test/test_component_Vegas.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/test/test_component_Vegas.c new file mode 100644 index 00000000..2a1cfe73 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/test/test_component_Vegas.c @@ -0,0 +1,696 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG_OUTPUT 0 + +#include "../component_Vegas.c" +#include "../vegas_Session.c" + +#include <sys/un.h> +#include <strings.h> +#include <sys/queue.h> + +#include <LongBow/unit-test.h> +#include <LongBow/runtime.h> + +#include <parc/security/parc_Security.h> +#include <parc/security/parc_PublicKeySignerPkcs12Store.h> +#include <parc/algol/parc_SafeMemory.h> + +#include <ccnx/api/notify/notify_Status.h> + +#include <ccnx/transport/test_tools/traffic_tools.h> +#include <ccnx/common/ccnx_ContentObject.h> +#include <ccnx/common/ccnx_NameSegmentNumber.h> + +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.c> +#include <ccnx/transport/transport_rta/core/rta_Connection.c> +#include <ccnx/transport/transport_rta/config/config_All.h> +#include <ccnx/transport/test_tools/traffic_tools.h> + +#include "../../test/testrig_MockFramework.c" + +#ifndef MAXPATH +#define MAXPATH 1024 +#endif + +// file descriptor for random numbers, part of Fixture +static int randomFd; + +typedef struct test_data { + MockFramework *mock; + char keystore_filename[MAXPATH]; + char keystore_password[MAXPATH]; +} TestData; + +static CCNxTransportConfig * +createParams(const char *keystore_name, const char *keystore_passwd) +{ + assertNotNull(keystore_name, "Got null keystore name\n"); + assertNotNull(keystore_passwd, "Got null keystore passwd\n"); + + CCNxStackConfig *stackConfig = apiConnector_ProtocolStackConfig( + testingUpper_ProtocolStackConfig( + vegasFlowController_ProtocolStackConfig( + testingLower_ProtocolStackConfig( + protocolStack_ComponentsConfigArgs(ccnxStackConfig_Create(), + apiConnector_GetName(), + testingUpper_GetName(), + vegasFlowController_GetName(), + testingLower_GetName(), + NULL))))); + + CCNxConnectionConfig *connConfig = apiConnector_ConnectionConfig( + testingUpper_ConnectionConfig( + vegasFlowController_ConnectionConfig( + tlvCodec_ConnectionConfig( + testingLower_ConnectionConfig(ccnxConnectionConfig_Create()))))); + + publicKeySignerPkcs12Store_ConnectionConfig(connConfig, keystore_name, keystore_passwd); + + CCNxTransportConfig *result = ccnxTransportConfig_Create(stackConfig, connConfig); + ccnxStackConfig_Release(&stackConfig); + return result; +} + +static TestData * +_commonSetup(const char *name) +{ + parcSecurity_Init(); + + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + sprintf(data->keystore_filename, "/tmp/keystore_%s_%d.p12", name, getpid()); + sprintf(data->keystore_password, "12345"); + + unlink(data->keystore_filename); + + CCNxTransportConfig *config = createParams(data->keystore_filename, data->keystore_password); + data->mock = mockFramework_Create(config); + ccnxTransportConfig_Destroy(&config); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + mockFramework_Destroy(&data->mock); + unlink(data->keystore_filename); + parcMemory_Deallocate((void **) &data); + + parcSecurity_Fini(); +} + +// ====================================================== + +LONGBOW_TEST_RUNNER(Fc_Vegas) +{ + LONGBOW_RUN_TEST_FIXTURE(Component); +} + +LONGBOW_TEST_RUNNER_SETUP(Fc_Vegas) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + randomFd = open("/dev/urandom", O_RDONLY); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(Fc_Vegas) +{ + close(randomFd); + return LONGBOW_STATUS_SUCCEEDED; +} + +// ============================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, vegasSession_GetFinalBlockIdFromContentObject_None); + LONGBOW_RUN_TEST_CASE(Local, vegasSession_GetFinalBlockIdFromContentObject_TestCases); + + LONGBOW_RUN_TEST_CASE(Local, vegasSession_GetSegnumFromObject); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup(longBowTestCase_GetName(testCase))); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +static CCNxTlvDictionary * +createSignedContentObject(void) +{ + CCNxName *name = ccnxName_CreateFromCString("lci:/some/name"); + PARCBuffer *payload = parcBuffer_Flip(parcBuffer_PutArray(parcBuffer_Allocate(20), 11, (uint8_t *) "the payload")); + CCNxTlvDictionary *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + parcBuffer_Release(&payload); + ccnxName_Release(&name); + + PARCBuffer *keyid = parcBuffer_Flip(parcBuffer_PutArray(parcBuffer_Allocate(20), 5, (uint8_t *) "keyid")); + ccnxValidationRsaSha256_Set(contentObject, keyid, NULL); + parcBuffer_Release(&keyid); + + PARCBuffer *sigbits = parcBuffer_WrapCString("the signature"); + PARCSignature *signature = parcSignature_Create(PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256, parcBuffer_Flip(sigbits)); + ccnxContentObject_SetSignature(contentObject, keyid, signature, NULL); + + parcSignature_Release(&signature); + parcBuffer_Release(&sigbits); + + return contentObject; +} + +static CCNxTlvDictionary * +createSignedContentObjectWithFinalBlockId(uint64_t fbid) +{ + CCNxTlvDictionary *obj = createSignedContentObject(); + ccnxContentObject_SetFinalChunkNumber(obj, fbid); + + return obj; +} + +LONGBOW_TEST_CASE(Local, vegasSession_GetFinalBlockIdFromContentObject_None) +{ + CCNxTlvDictionary *contentObjectDictionary = createSignedContentObject(); + bool success = vegasSession_GetFinalBlockIdFromContentObject(contentObjectDictionary, NULL); + assertFalse(success, "Should have failed getting FBID from content object"); + ccnxTlvDictionary_Release(&contentObjectDictionary); +} + +LONGBOW_TEST_CASE(Local, vegasSession_GetFinalBlockIdFromContentObject_TestCases) +{ + struct test_struct { + uint64_t value; + size_t encodedBytes; + uint8_t *encoded; + } test_vector[] = { + { .value = 0x0000000000000000ULL, .encodedBytes = 1, .encoded = (uint8_t[1]) { 0x00 } }, + { .value = 0x0000000000000001ULL, .encodedBytes = 1, .encoded = (uint8_t[1]) { 0x01 } }, + { .value = 0x00000000000000FFULL, .encodedBytes = 1, .encoded = (uint8_t[1]) { 0xFF } }, + { .value = 0x0000000000000100ULL, .encodedBytes = 2, .encoded = (uint8_t[2]) { 0x01, 0x00} }, + { .value = 0x0100000000000100ULL, .encodedBytes = 8, .encoded = (uint8_t[8]) { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00} }, + { .value = 0x8000000000000100ULL, .encodedBytes = 8, .encoded = (uint8_t[8]) { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00} }, + { .value = 0xFFFFFFFFFFFFFFFFULL, .encodedBytes = 8, .encoded = (uint8_t[8]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} }, + { .value = 0, .encodedBytes = 0, .encoded = NULL } + }; + + for (int i = 0; test_vector[i].encoded != NULL; i++) { + CCNxTlvDictionary *signed_with_fbid = createSignedContentObjectWithFinalBlockId(test_vector[i].value); + + uint64_t testvalue = -1; + bool success = vegasSession_GetFinalBlockIdFromContentObject(signed_with_fbid, &testvalue); + assertTrue(success, "Failed to get FBID from content object index %d value %016" PRIx64 "\n", + i, + test_vector[i].value) + { + ccnxTlvDictionary_Display(signed_with_fbid, 0); + } + + + assertTrue(testvalue == test_vector[i].value, + "Segment number does not match index %d value %016" PRIx64 ": got %" PRIx64 "\n", + i, + test_vector[i].value, + testvalue); + + ccnxTlvDictionary_Release(&signed_with_fbid); + } +} + +LONGBOW_TEST_CASE(Local, vegasSession_GetSegnumFromObject) +{ + struct test_struct { + bool valid; + uint64_t segnum; + char *uri; + } test_vectors[] = { + { .valid = false, .segnum = 0, .uri = "lci:/foo/bar" }, + { .valid = true, .segnum = 0, .uri = "lci:/foo/" CCNxNameLabel_Chunk "=%00" }, + { .valid = true, .segnum = 0x1020, .uri = "lci:/foo/" CCNxNameLabel_Chunk "=%10%20" }, + { .valid = true, .segnum = 0x6162, .uri = "lci:/foo/" CCNxNameLabel_Chunk "=ab" }, + { .valid = true, .segnum = 0x616263, .uri = "lci:/foo/" CCNxNameLabel_Chunk "=abc" }, + { .valid = true, .segnum = 0x61626364, .uri = "lci:/foo/" CCNxNameLabel_Chunk "=abcd" }, + { .valid = true, .segnum = 0x6162636465, .uri = "lci:/foo/" CCNxNameLabel_Chunk "=abcde" }, + { .valid = true, .segnum = 0x616263646566, .uri = "lci:/foo/" CCNxNameLabel_Chunk "=abcdef" }, + { .valid = true, .segnum = 0x61626364656667, .uri = "lci:/foo/" CCNxNameLabel_Chunk "=abcdefg" }, + { .valid = true, .segnum = 0x6162636465666768, .uri = "lci:/foo/" CCNxNameLabel_Chunk "=abcdefgh" }, + { .valid = false, .segnum = 0, .uri = NULL } + }; + + for (int i = 0; test_vectors[i].uri != NULL; i++) { + CCNxName *name = ccnxName_CreateFromCString(test_vectors[i].uri); + CCNxTlvDictionary *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, NULL); + + uint64_t testSeqnum = -1; + int failure = vegasSession_GetSegnumFromObject(contentObject, &testSeqnum); + + + + if (test_vectors[i].valid) { + assertFalse(failure, "Incorrect success index %d: got %d expected %d", + i, failure, test_vectors[i].valid); + + assertTrue(testSeqnum == test_vectors[i].segnum, "Incorrect segnum index %d, got %" PRIu64 " expected %" PRIu64, + i, testSeqnum, test_vectors[i].segnum); + } else { + assertTrue(failure, "Incorrect success index %d: got %d expected %d", + i, failure, test_vectors[i].valid); + } + + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&contentObject); + } +} + +// ============================================================== + +LONGBOW_TEST_FIXTURE(Component) +{ + LONGBOW_RUN_TEST_CASE(Component, open_close); + + // these should all be pass through + LONGBOW_RUN_TEST_CASE(Component, content_object_down); + LONGBOW_RUN_TEST_CASE(Component, control_msg_down); + LONGBOW_RUN_TEST_CASE(Component, interest_up); + LONGBOW_RUN_TEST_CASE(Component, control_msg_up); + LONGBOW_RUN_TEST_CASE(Component, cancel_flow); +} + +LONGBOW_TEST_FIXTURE_SETUP(Component) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup(longBowTestCase_GetName(testCase))); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Component) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ============================================ + +LONGBOW_TEST_CASE(Component, open_close) +{ + // dont actually do anything. make sure no memory leaks in setup and teardown. +} + + +// ============================================ +// Passthrough messages + +LONGBOW_TEST_CASE(Component, content_object_down) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + TransportMessage *truth_tm = trafficTools_CreateTransportMessageWithSignedContentObject(data->mock->connection); + + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + PARCEventQueue *read = rtaProtocolStack_GetPutQueue(data->mock->stack, FC_VEGAS, RTA_UP); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + rtaComponent_PutMessage(in, truth_tm); + rtaFramework_NonThreadedStep(data->mock->framework); + flow_vegas_ops.downcallRead(read, PARCEventType_Read, (void *) data->mock->stack); + rtaFramework_NonThreadedStep(data->mock->framework); + + TransportMessage *test_tm = rtaComponent_GetMessage(out); + + assertTrue(test_tm == truth_tm, + "Got wrong transport message pointer, got %p expected %p", + (void *) test_tm, + (void *) truth_tm); + + transportMessage_Destroy(&truth_tm); +} + +LONGBOW_TEST_CASE(Component, control_msg_down) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + TransportMessage *truth_tm = trafficTools_CreateTransportMessageWithControlMessage(data->mock->connection); + + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + PARCEventQueue *read = rtaProtocolStack_GetPutQueue(data->mock->stack, FC_VEGAS, RTA_UP); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + rtaComponent_PutMessage(in, truth_tm); + rtaFramework_NonThreadedStep(data->mock->framework); + flow_vegas_ops.downcallRead(read, PARCEventType_Read, (void *) data->mock->stack); + rtaFramework_NonThreadedStep(data->mock->framework); + TransportMessage *test_tm = rtaComponent_GetMessage(out); + + assertTrue(test_tm == truth_tm, + "Got wrong transport message pointer, got %p expected %p", + (void *) test_tm, + (void *) truth_tm); + + transportMessage_Destroy(&truth_tm); +} + +LONGBOW_TEST_CASE(Component, interest_up) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + TransportMessage *truth_tm = trafficTools_CreateTransportMessageWithInterest(data->mock->connection); + + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + PARCEventQueue *read = rtaProtocolStack_GetPutQueue(data->mock->stack, FC_VEGAS, RTA_DOWN); + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + rtaComponent_PutMessage(in, truth_tm); + rtaFramework_NonThreadedStep(data->mock->framework); + flow_vegas_ops.upcallRead(read, PARCEventType_Read, (void *) data->mock->stack); + rtaFramework_NonThreadedStep(data->mock->framework); + TransportMessage *test_tm = rtaComponent_GetMessage(out); + + assertTrue(test_tm == truth_tm, + "Got wrong transport message pointer, got %p expected %p", + (void *) test_tm, + (void *) truth_tm); + + transportMessage_Destroy(&truth_tm); +} + +LONGBOW_TEST_CASE(Component, control_msg_up) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + TransportMessage *truth_tm = trafficTools_CreateTransportMessageWithControlMessage(data->mock->connection); + + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + PARCEventQueue *read = rtaProtocolStack_GetPutQueue(data->mock->stack, FC_VEGAS, RTA_DOWN); + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + rtaComponent_PutMessage(in, truth_tm); + rtaFramework_NonThreadedStep(data->mock->framework); + flow_vegas_ops.upcallRead(read, PARCEventType_Read, (void *) data->mock->stack); + rtaFramework_NonThreadedStep(data->mock->framework); + TransportMessage *test_tm = rtaComponent_GetMessage(out); + + assertTrue(test_tm == truth_tm, + "Got wrong transport message pointer, got %p expected %p", + (void *) test_tm, + (void *) truth_tm); + + transportMessage_Destroy(&test_tm); +} + +// ============================================ +// These should start a flow control session + +/** + * Creates an interest w/o a segment number + * Sends it down the stack to the flow controller + * Flow controller should append segment number 0 to the interest and send that down the stack + */ +LONGBOW_TEST_CASE(Component, interest_down) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + TransportMessage *truth_tm = trafficTools_CreateTransportMessageWithInterest(data->mock->connection); + + // If we can, add a payload to the Interest. Why not. + PARCBuffer *payload = NULL; + CCNxInterest *interest = transportMessage_GetDictionary(truth_tm); + CCNxInterestInterface *impl = ccnxInterestInterface_GetInterface(interest); + if (impl != NULL && impl != &CCNxInterestFacadeV1_Implementation) { + // V1 or greater should support Interest payloads. + payload = parcBuffer_WrapCString("This is a payload."); + impl->setPayload(interest, payload); + } + + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + PARCEventQueue *read = rtaProtocolStack_GetPutQueue(data->mock->stack, FC_VEGAS, RTA_UP); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + rtaComponent_PutMessage(in, truth_tm); + rtaFramework_NonThreadedStep(data->mock->framework); + flow_vegas_ops.downcallRead(read, PARCEventType_Read, (void *) data->mock->stack); + rtaFramework_NonThreadedStep(data->mock->framework); + + // we should see a status message up the stack and interests + // going down the stack. + + + TransportMessage *test_tm = rtaComponent_GetMessage(in); + assertNotNull(test_tm, "got null transport message back up the queue, expecting status\n"); + + assertTrue(transportMessage_IsControl(test_tm), + "Transport message is not a control object") + { + ccnxTlvDictionary_Display(transportMessage_GetDictionary(test_tm), 0); + } + + CCNxTlvDictionary *test_dict = transportMessage_GetDictionary(test_tm); + + PARCJSON *json = ccnxControlFacade_GetJson(test_dict); + + NotifyStatus *status = notifyStatus_ParseJSON(json); + + assertNotNull(status, "Could not parse NotifyStatus JSON message"); + assertTrue(notifyStatus_GetFiledes(status) == data->mock->connection->api_fd, + "Expected file descriptor %d, actual %d\n", data->mock->connection->api_fd, notifyStatus_GetFiledes(status)); + + assertTrue(notifyStatus_IsFlowControlStarted(status), + "Expected notifyStatus_IsFlowControlStarted to be true, actual code %d", notifyStatus_GetStatusCode(status)); + + notifyStatus_Release(&status); + + transportMessage_Destroy(&test_tm); + + // Read segment 0 interest + trafficTools_ReadAndVerifySegment(out, ccnxInterest_GetName(transportMessage_GetDictionary(truth_tm)), 0, payload); + + // Now read segment 1 + trafficTools_ReadAndVerifySegment(out, ccnxInterest_GetName(transportMessage_GetDictionary(truth_tm)), 1, payload); + + if (payload != NULL) { + parcBuffer_Release(&payload); + } + + transportMessage_Destroy(&truth_tm); +} + + +LONGBOW_TEST_CASE(Component, interest_down_slow_retransmit) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + TransportMessage *truth_tm = trafficTools_CreateTransportMessageWithInterest(data->mock->connection); + + VegasConnectionState *fc; + FcSessionHolder *holder; + + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + PARCEventQueue *read = rtaProtocolStack_GetPutQueue(data->mock->stack, FC_VEGAS, RTA_UP); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + rtaComponent_PutMessage(in, truth_tm); + rtaFramework_NonThreadedStep(data->mock->framework); + flow_vegas_ops.downcallRead(read, PARCEventType_Read, (void *) data->mock->stack); + rtaFramework_NonThreadedStep(data->mock->framework); + + // -------------------------------------- + // Read segment 0 interest + CCNxTlvDictionary *interest = transportMessage_GetDictionary(truth_tm); + + trafficTools_ReadAndVerifySegment(out, ccnxInterest_GetName(interest), 0, NULL); + + // Now read segment 1 + trafficTools_ReadAndVerifySegment(out, ccnxInterest_GetName(interest), 1, NULL); + + // -------------------------------------- + // now bump the time and see what happens. + // these are normally set in the timer sallback + fc = rtaConnection_GetPrivateData(data->mock->connection, FC_VEGAS); + holder = TAILQ_FIRST(&fc->sessions_head); + assertNotNull(holder, "got null session holder"); + + printf("*** bump time\n"); + + data->mock->framework->clock_ticks += 1001; + + // RTO timeout will be 1 second + vegasSession_TimerCallback(-1, PARCEventType_Timeout, holder->session); + trafficTools_ReadAndVerifySegment(out, ccnxInterest_GetName(interest), 0, NULL); + + transportMessage_Destroy(&truth_tm); +} + + +LONGBOW_TEST_CASE(Component, interest_down_fast_retransmit) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + TransportMessage *truth_tm = trafficTools_CreateTransportMessageWithInterest(data->mock->connection); + + CCNxName *basename, *segmentname; + VegasConnectionState *fc; + FcSessionHolder *holder; + + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + PARCEventQueue *read = rtaProtocolStack_GetPutQueue(data->mock->stack, FC_VEGAS, RTA_UP); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + rtaComponent_PutMessage(in, truth_tm); + rtaFramework_NonThreadedStep(data->mock->framework); + flow_vegas_ops.downcallRead(read, PARCEventType_Read, (void *) data->mock->stack); + rtaFramework_NonThreadedStep(data->mock->framework); + + // -------------------------------------- + // Read segment 0 interest + CCNxTlvDictionary *interest = transportMessage_GetDictionary(truth_tm); + + trafficTools_ReadAndVerifySegment(out, ccnxInterest_GetName(interest), 0, NULL); + + // Now read segment 1 + trafficTools_ReadAndVerifySegment(out, ccnxInterest_GetName(interest), 1, NULL); + + // -------------------------------------- + // now bump the time and see what happens. + // these are normally set in the timer sallback + fc = rtaConnection_GetPrivateData(data->mock->connection, FC_VEGAS); + holder = TAILQ_FIRST(&fc->sessions_head); + assertNotNull(holder, "got null session holder"); + + + data->mock->framework->clock_ticks += 20; + printf("*** bump time %" PRIu64 "\n", data->mock->framework->clock_ticks); + vegasSession_TimerCallback(-1, PARCEventType_Timeout, holder->session); + + // -------------------------------------- + // send an out-of-order content object, should see a fast retransmit + + basename = ccnxName_Copy(ccnxInterest_GetName(interest)); + segmentname = ccnxName_Copy(basename); + + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, 1); + ccnxName_Append(segmentname, segment); + ccnxNameSegment_Release(&segment); + + transportMessage_Destroy(&truth_tm); + + // this takes ownership of segment name + TransportMessage *reply = + trafficTools_CreateTransportMessageWithSignedContentObjectWithName(data->mock->connection, + segmentname, data->keystore_filename, data->keystore_password); + + rtaComponent_PutMessage(out, reply); + + data->mock->framework->clock_ticks += 40; + printf("*** bump time %" PRIu64 "\n", data->mock->framework->clock_ticks); + rtaFramework_NonThreadedStepCount(data->mock->framework, 5); + vegasSession_TimerCallback(-1, PARCEventType_Timeout, holder->session); + + trafficTools_ReadAndVerifySegment(out, basename, 0, NULL); + + ccnxName_Release(&segmentname); + ccnxName_Release(&basename); +} + +/** + * Send an interest down the stack to start a flow controller, then send + * a control message to cancel it. + */ +LONGBOW_TEST_CASE(Component, cancel_flow) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + TransportMessage *truth_tm = trafficTools_CreateTransportMessageWithInterest(data->mock->connection); + + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + + CCNxName *flowName = ccnxName_Acquire(ccnxInterest_GetName(transportMessage_GetDictionary(truth_tm))); + + // ================================ + // This will signal to the flow controller that it should start a flow + // We give up ownership of "truth_tm" at this point + rtaComponent_PutMessage(in, truth_tm); + rtaFramework_NonThreadedStepCount(data->mock->framework, 5); + + // ================================ + // we should see a status message up the stack + + TransportMessage *test_tm = rtaComponent_GetMessage(in); + assertNotNull(test_tm, "got null transport message back up the queue, expecting status\n"); + + assertTrue(transportMessage_IsControl(test_tm), "Transport message is not a Control") + { + ccnxTlvDictionary_Display(transportMessage_GetDictionary(test_tm), 0); + } + + CCNxTlvDictionary *controlDictionary = transportMessage_GetDictionary(test_tm); + + PARCJSON *json = ccnxControlFacade_GetJson(controlDictionary); + + NotifyStatus *status = notifyStatus_ParseJSON(json); + assertTrue(notifyStatus_IsFlowControlStarted(status), + "Expected notifyStatus_IsFlowControlStarted to be true. Actual code %d\n", notifyStatus_GetStatusCode(status)); + notifyStatus_Release(&status); + + // ================================ + // After the notification, the flow is "started" and we can cancel it + + // Now that its started, send a cancel + PARCJSON *cancelFlow = cpiCancelFlow_Create(flowName); + CCNxTlvDictionary *cancelDictionary = ccnxControlFacade_CreateCPI(cancelFlow); + parcJSON_Release(&cancelFlow); + + TransportMessage *cancelTm = transportMessage_CreateFromDictionary(cancelDictionary); + transportMessage_SetInfo(cancelTm, rtaConnection_Copy(data->mock->connection), rtaConnection_FreeFunc); + rtaComponent_PutMessage(in, cancelTm); + rtaFramework_NonThreadedStepCount(data->mock->framework, 5); + + // now verify that its gone + VegasConnectionState *fc = rtaConnection_GetPrivateData(data->mock->connection, FC_VEGAS); + FcSessionHolder *holder = TAILQ_FIRST(&fc->sessions_head); + assertNull(holder, "The session list is not empty!"); + + ccnxTlvDictionary_Release(&cancelDictionary); + transportMessage_Destroy(&test_tm); + ccnxName_Release(&flowName); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(Fc_Vegas); + exit(longBowMain(argc, argv, testRunner, NULL)); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/test/test_vegas_Session.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/test/test_vegas_Session.c new file mode 100644 index 00000000..b48e9a8a --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/test/test_vegas_Session.c @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define DEBUG_OUTPUT 0 + +#include "../component_Vegas.c" +#include "../vegas_Session.c" + +#include <sys/un.h> +#include <strings.h> +#include <sys/queue.h> + +#include <LongBow/unit-test.h> +#include <LongBow/runtime.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.h> + +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.c> +#include <ccnx/transport/transport_rta/core/rta_Connection.c> + +#include <parc/security/parc_Security.h> +#include <parc/security/parc_PublicKeySignerPkcs12Store.h> +#include <ccnx/transport/transport_rta/config/config_All.h> + +#include <ccnx/api/notify/notify_Status.h> + +#include <ccnx/transport/test_tools/traffic_tools.h> + +#include <ccnx/common/ccnx_ContentObject.h> +#include <ccnx/common/internal/ccnx_ValidationFacadeV1.h> + +#include "../../test/testrig_MockFramework.c" + +#ifndef MAXPATH +#define MAXPATH 1024 +#endif + +// file descriptor for random numbers, part of Fixture +static int randomFd; + +typedef struct test_data { + MockFramework *mock; + char keystore_filename[MAXPATH]; + char keystore_password[MAXPATH]; +} TestData; + +static CCNxTransportConfig * +createParams(const char *keystore_name, const char *keystore_passwd) +{ + assertNotNull(keystore_name, "Got null keystore name\n"); + assertNotNull(keystore_passwd, "Got null keystore passwd\n"); + + CCNxStackConfig *stackConfig = apiConnector_ProtocolStackConfig( + testingUpper_ProtocolStackConfig( + vegasFlowController_ProtocolStackConfig( + testingLower_ProtocolStackConfig( + protocolStack_ComponentsConfigArgs(ccnxStackConfig_Create(), + apiConnector_GetName(), + testingUpper_GetName(), + vegasFlowController_GetName(), + testingLower_GetName(), + NULL))))); + + CCNxConnectionConfig *connConfig = apiConnector_ConnectionConfig( + testingUpper_ConnectionConfig( + vegasFlowController_ConnectionConfig( + testingLower_ConnectionConfig(ccnxConnectionConfig_Create())))); + + + publicKeySignerPkcs12Store_ConnectionConfig(connConfig, keystore_name, keystore_passwd); + + CCNxTransportConfig *result = ccnxTransportConfig_Create(stackConfig, connConfig); + ccnxStackConfig_Release(&stackConfig); + return result; +} + +static TestData * +_commonSetup(const char *name) +{ + parcSecurity_Init(); + + TestData *data = parcMemory_Allocate(sizeof(TestData)); + + assertNotNull(data, "Got null memory from parcMemory_Allocate"); + + sprintf(data->keystore_filename, "/tmp/keystore_%s_%d.p12", name, getpid()); + sprintf(data->keystore_password, "12345"); + + unlink(data->keystore_filename); + + CCNxTransportConfig *config = createParams(data->keystore_filename, data->keystore_password); + data->mock = mockFramework_Create(config); + ccnxTransportConfig_Destroy(&config); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + mockFramework_Destroy(&data->mock); + unlink(data->keystore_filename); + + parcMemory_Deallocate((void **) &data); + + parcSecurity_Fini(); +} + + + +// ====================================================== + +LONGBOW_TEST_RUNNER(VegasSession) +{ + LONGBOW_RUN_TEST_FIXTURE(Local); + LONGBOW_RUN_TEST_FIXTURE(IterateFinalChunkNumber); +} + +LONGBOW_TEST_RUNNER_SETUP(VegasSession) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + randomFd = open("/dev/urandom", O_RDONLY); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(VegasSession) +{ + close(randomFd); + return LONGBOW_STATUS_SUCCEEDED; +} + +// ============================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, vegasSession_GetFinalBlockIdFromContentObject_None); + LONGBOW_RUN_TEST_CASE(Local, vegasSession_GetFinalBlockIdFromContentObject_TestCases); + LONGBOW_RUN_TEST_CASE(Local, vegasSession_GetSegnumFromObject); + + LONGBOW_RUN_TEST_CASE(Local, vegasSession_ReceiveContentObject_InOrder_LastBlockSetsFinalId); + LONGBOW_RUN_TEST_CASE(Local, vegasSession_ReceiveContentObject_InOrder_FirstAndLastBlocksSetsFinalId); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup(longBowTestCase_GetName(testCase))); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +static CCNxTlvDictionary * +createSignedContentObject(void) +{ + CCNxName *name = ccnxName_CreateFromCString("ccnx:/some/name"); + PARCBuffer *payload = parcBuffer_Flip(parcBuffer_PutArray(parcBuffer_Allocate(20), 11, (uint8_t *) "the payload")); + CCNxTlvDictionary *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload); + parcBuffer_Release(&payload); + ccnxName_Release(&name); + + PARCBuffer *keyid = parcBuffer_Flip(parcBuffer_PutArray(parcBuffer_Allocate(20), 5, (uint8_t *) "keyid")); + ccnxValidationRsaSha256_Set(contentObject, keyid, NULL); + parcBuffer_Release(&keyid); + + PARCBuffer *sigbits = parcBuffer_Flip(parcBuffer_PutArray(parcBuffer_Allocate(20), 13, (uint8_t *) "the signature")); + + switch (ccnxTlvDictionary_GetSchemaVersion(contentObject)) { + case CCNxTlvDictionary_SchemaVersion_V1: + ccnxValidationFacadeV1_SetPayload(contentObject, sigbits); + break; + default: + trapNotImplemented("Unsupprted schema version in createSignedContentObject()"); + break; + } + + parcBuffer_Release(&sigbits); + + return contentObject; +} + +static CCNxTlvDictionary * +createSignedContentObjectWithFinalBlockId(uint64_t fbid) +{ + CCNxTlvDictionary *obj = createSignedContentObject(); + ccnxContentObject_SetFinalChunkNumber(obj, fbid); + return obj; +} + +LONGBOW_TEST_CASE(Local, vegasSession_GetFinalBlockIdFromContentObject_None) +{ + CCNxTlvDictionary *contentObjectDictionary = createSignedContentObject(); + bool success = vegasSession_GetFinalBlockIdFromContentObject(contentObjectDictionary, NULL); + assertFalse(success, "Should have failed getting FBID from content object"); + ccnxTlvDictionary_Release(&contentObjectDictionary); +} + +LONGBOW_TEST_CASE(Local, vegasSession_GetFinalBlockIdFromContentObject_TestCases) +{ + struct test_struct { + uint64_t value; + size_t encodedBytes; + uint8_t *encoded; + } test_vector[] = { + { .value = 0x0000000000000000ULL, .encodedBytes = 1, .encoded = (uint8_t[1]) { 0x00 } }, + { .value = 0x0000000000000001ULL, .encodedBytes = 1, .encoded = (uint8_t[1]) { 0x01 } }, + { .value = 0x00000000000000FFULL, .encodedBytes = 1, .encoded = (uint8_t[1]) { 0xFF } }, + { .value = 0x0000000000000100ULL, .encodedBytes = 2, .encoded = (uint8_t[2]) { 0x01, 0x00} }, + { .value = 0x0100000000000100ULL, .encodedBytes = 8, .encoded = (uint8_t[8]) { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00} }, + { .value = 0x8000000000000100ULL, .encodedBytes = 8, .encoded = (uint8_t[8]) { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00} }, + { .value = 0xFFFFFFFFFFFFFFFFULL, .encodedBytes = 8, .encoded = (uint8_t[8]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} }, + { .value = 0, .encodedBytes = 0, .encoded = NULL } + }; + + for (int i = 0; test_vector[i].encoded != NULL; i++) { + CCNxTlvDictionary *signed_with_fbid = createSignedContentObjectWithFinalBlockId(test_vector[i].value); + + uint64_t testvalue = -1; + bool success = vegasSession_GetFinalBlockIdFromContentObject(signed_with_fbid, &testvalue); + assertTrue(success, "Failed to get FBID from content object index %d value %016" PRIx64 "\n", + i, + test_vector[i].value) + { + ccnxTlvDictionary_Display(signed_with_fbid, 0); + } + + + assertTrue(testvalue == test_vector[i].value, + "Segment number does not match index %d value %016" PRIx64 ": got %" PRIx64 "\n", + i, + test_vector[i].value, + testvalue); + + ccnxTlvDictionary_Release(&signed_with_fbid); + } +} + +LONGBOW_TEST_CASE(Local, vegasSession_GetSegnumFromObject) +{ + struct test_struct { + bool valid; + uint64_t segnum; + char *uri; + } test_vectors[] = { + { .valid = false, .segnum = 0, .uri = "ccnx:/foo/bar" }, + { .valid = true, .segnum = 0, .uri = "ccnx:/foo/" CCNxNameLabel_Chunk "=%00" }, + { .valid = true, .segnum = 0x1020, .uri = "ccnx:/foo/" CCNxNameLabel_Chunk "=%10%20" }, + { .valid = true, .segnum = 0x6162, .uri = "ccnx:/foo/" CCNxNameLabel_Chunk "=ab" }, + { .valid = true, .segnum = 0x616263, .uri = "ccnx:/foo/" CCNxNameLabel_Chunk "=abc" }, + { .valid = true, .segnum = 0x61626364, .uri = "ccnx:/foo/" CCNxNameLabel_Chunk "=abcd" }, + { .valid = true, .segnum = 0x6162636465, .uri = "ccnx:/foo/" CCNxNameLabel_Chunk "=abcde" }, + { .valid = true, .segnum = 0x616263646566, .uri = "ccnx:/foo/" CCNxNameLabel_Chunk "=abcdef" }, + { .valid = true, .segnum = 0x61626364656667, .uri = "ccnx:/foo/" CCNxNameLabel_Chunk "=abcdefg" }, + { .valid = true, .segnum = 0x6162636465666768, .uri = "ccnx:/foo/" CCNxNameLabel_Chunk "=abcdefgh" }, + { .valid = false, .segnum = 0, .uri = NULL } + }; + + for (int i = 0; test_vectors[i].uri != NULL; i++) { + CCNxName *name = ccnxName_CreateFromCString(test_vectors[i].uri); + CCNxTlvDictionary *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, NULL); + + uint64_t testSeqnum = -1; + int failure = vegasSession_GetSegnumFromObject(contentObject, &testSeqnum); + + + + if (test_vectors[i].valid) { + assertFalse(failure, "Incorrect success index %d: got %d expected %d", + i, failure, test_vectors[i].valid); + + assertTrue(testSeqnum == test_vectors[i].segnum, "Incorrect segnum index %d, got %" PRIu64 " expected %" PRIu64, + i, testSeqnum, test_vectors[i].segnum); + } else { + assertTrue(failure, "Incorrect success index %d: got %d expected %d", + i, failure, test_vectors[i].valid); + } + + ccnxName_Release(&name); + ccnxTlvDictionary_Release(&contentObject); + } +} + + +// ================================================================= +// Tests related to the FinalBlockId and how the publisher sets it in +// a stream of content objects + +#define DO_NOT_SET ((uint64_t) -1) +#define SENTINEL ((uint64_t) -1) + +typedef struct test_vector { + uint64_t chunk; + uint64_t setFinalBlockId; + bool isLast; + bool interestReceived; + bool dataReceived; +} TestVector; + + +static void +_verifyFlowStartNotification(TestData *data, TransportMessage *notify) +{ + assertNotNull(notify, "got null transport message back up the queue, expecting status\n"); + + assertTrue(transportMessage_IsControl(notify), + "Transport message is not a control object") + { + ccnxTlvDictionary_Display(transportMessage_GetDictionary(notify), 0); + } + + CCNxTlvDictionary *test_dict = transportMessage_GetDictionary(notify); + + PARCJSON *json = ccnxControlFacade_GetJson(test_dict); + + NotifyStatus *status = notifyStatus_ParseJSON(json); + + assertNotNull(status, "Could not parse NotifyStatus JSON message"); + assertTrue(notifyStatus_GetFiledes(status) == data->mock->connection->api_fd, + "Expected file descriptor %d, actual %d\n", data->mock->connection->api_fd, notifyStatus_GetFiledes(status)); + + assertTrue(notifyStatus_IsFlowControlStarted(status), + "Expected notifyStatus_IsFlowControlStarted to be true, actual code %d", notifyStatus_GetStatusCode(status)); + + notifyStatus_Release(&status); +} + + +static CCNxName * +_startFlow(TestData *data) +{ + TransportMessage *downInterest = trafficTools_CreateTransportMessageWithInterest(data->mock->connection); + CCNxName *sessionName = ccnxName_Acquire(ccnxInterest_GetName(transportMessage_GetDictionary(downInterest))); + PARCEventQueue *upperQueue = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + + rtaComponent_PutMessage(upperQueue, downInterest); + rtaFramework_NonThreadedStepCount(data->mock->framework, 10); + + // we should see a status message up the stack and interests + // going down the stack. + + TransportMessage *notify = rtaComponent_GetMessage(upperQueue); + _verifyFlowStartNotification(data, notify); + transportMessage_Destroy(¬ify); + + return sessionName; +} + +/* + * Caveat: this only works because we create a single session + */ +static VegasSession * +_grabSession(TestData *data, CCNxName *name) +{ + VegasConnectionState *fc = rtaConnection_GetPrivateData(data->mock->connection, FC_VEGAS); + + FcSessionHolder *holder = vegas_LookupSessionByName(fc, name); + + assertNotNull(holder, "Could not find the session holder in the flow controller"); + return holder->session; +} + +/* + * a tick is 1 milli-second, but it could be different depending on how + * the framework is started + */ +static void +_bumpTime(TestData *data, unsigned ticks, CCNxName *name) +{ + data->mock->framework->clock_ticks += ticks; + vegasSession_TimerCallback(-1, PARCEventType_Timeout, _grabSession(data, name)); +} + +static uint64_t +_getChunkNumberFromName(const CCNxName *name) +{ + size_t segmentCount = ccnxName_GetSegmentCount(name); + CCNxNameSegment *lastSegment = ccnxName_GetSegment(name, segmentCount - 1); + CCNxNameLabelType nameType = ccnxNameSegment_GetType(lastSegment); + assertTrue(nameType == CCNxNameLabelType_CHUNK, "Wrong segment type got %d expected %d", nameType, CCNxNameLabelType_CHUNK); + uint64_t chunkNumber = ccnxNameSegmentNumber_Value(lastSegment); + return chunkNumber; +} + +static TestVector * +_getVector(TestVector *vectors, uint64_t chunkNumber) +{ + // find the test vector for this chunk + for (int i = 0; vectors[i].chunk != SENTINEL; i++) { + if (vectors[i].chunk == chunkNumber) { + return &vectors[i]; + } + } + trapIllegalValue(chunkNumber, "Could not find chunk number in test vector"); +} + +static TransportMessage * +_createReponseContentObject(CCNxName *name, uint64_t finalBlockid) +{ + CCNxContentObject *obj = ccnxContentObject_CreateWithNameAndPayload(name, NULL); + assertNotNull(obj, "Got null content object."); + + if (finalBlockid != DO_NOT_SET) { + bool success = ccnxContentObject_SetFinalChunkNumber(obj, finalBlockid); + assertTrue(success, "Failed to set final chunk number"); + } + + CCNxMetaMessage *message = ccnxMetaMessage_CreateFromContentObject(obj); + TransportMessage *response = transportMessage_CreateFromDictionary(message); + + ccnxMetaMessage_Release(&message); + ccnxContentObject_Release(&obj); + + return response; +} + +/* + * Returns true if the unit test is finished + */ +static bool +_respondToDownInterest(TestData *data, TestVector *vectors) +{ + PARCEventQueue *lowerQueue = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + bool finished = false; + TransportMessage *msg = rtaComponent_GetMessage(lowerQueue); + if (msg) { + // it should be an Interest with a chunk number + assertTrue(transportMessage_IsInterest(msg), "Got unexpected message") + { + ccnxTlvDictionary_Display(transportMessage_GetDictionary(msg), 3); + } + + CCNxTlvDictionary *interestDictionary = transportMessage_GetDictionary(msg); + CCNxName *name = ccnxInterest_GetName(interestDictionary); + uint64_t chunkNumber = _getChunkNumberFromName(name); + + TestVector *vector = _getVector(vectors, chunkNumber); + + vector->interestReceived = true; + + // create a content object and set the FinalBlockId if vector says to + TransportMessage *response = _createReponseContentObject(name, vector->setFinalBlockId); + RtaConnection *connection = transportMessage_GetInfo(msg); + RtaConnection *connectionRef = rtaConnection_Copy(connection); + transportMessage_SetInfo(response, connectionRef, rtaConnection_FreeFunc); + + rtaComponent_PutMessage(lowerQueue, response); + + finished = vector->isLast; + + transportMessage_Destroy(&msg); + } + return finished; +} + +/* + * Returns true if received the last message + */ +static bool +_consumeUpperContentObject(TestData *data, TestVector *vectors) +{ + PARCEventQueue *upperQueue = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + + bool finished = false; + TransportMessage *msg = rtaComponent_GetMessage(upperQueue); + if (msg) { + // it should be a content object + assertTrue(transportMessage_IsContentObject(msg), "Got unexpected message") + { + ccnxTlvDictionary_Display(transportMessage_GetDictionary(msg), 3); + } + + CCNxTlvDictionary *objectDictionary = transportMessage_GetDictionary(msg); + CCNxName *name = ccnxContentObject_GetName(objectDictionary); + uint64_t chunkNumber = _getChunkNumberFromName(name); + + TestVector *vector = _getVector(vectors, chunkNumber); + + // we should not have seen it before + assertFalse(vector->dataReceived, "Duplicate Content Object chunk %" PRIu64, chunkNumber) + { + ccnxName_Display(name, 3); + } + + vector->dataReceived = true; + + finished = vector->isLast; + + transportMessage_Destroy(&msg); + } + + return finished; +} + +static void +_runTestVector(TestData *data, TestVector vectors[]) +{ + CCNxName *sessionName = _startFlow(data); + + bool finished = false; + + while (!finished) { + rtaFramework_NonThreadedStep(data->mock->framework); + finished = _respondToDownInterest(data, vectors); + + rtaFramework_NonThreadedStep(data->mock->framework); + finished &= _consumeUpperContentObject(data, vectors); + + if (!finished) { + _bumpTime(data, 5, sessionName); + } + } + + ccnxName_Release(&sessionName); +} + +/* + * First chunk sets final block ID, last chunk does not. Should keep reading until + * the real last chunk set to itself. + */ +LONGBOW_TEST_CASE(Local, vegasSession_ReceiveContentObject_InOrder_FirstBlockSetsLastDoesNotFinalId) +{ + TestVector vectors[] = { + { .chunk = 0, .setFinalBlockId = 5, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 1, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 2, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 3, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 4, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 5, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 6, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 7, .setFinalBlockId = 7, .isLast = true, .interestReceived = false, .dataReceived = false }, + { .chunk = SENTINEL, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + }; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _runTestVector(data, vectors); +} + +/* + * FinalBlockId unset until last chunk, which sets to itself + */ +LONGBOW_TEST_CASE(Local, vegasSession_ReceiveContentObject_InOrder_LastBlockSetsFinalId) +{ + TestVector vectors[] = { + { .chunk = 0, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 1, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 2, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 3, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 4, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 5, .setFinalBlockId = 5, .isLast = true, .interestReceived = false, .dataReceived = false }, + { .chunk = SENTINEL, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + }; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _runTestVector(data, vectors); +} + +/* + * First chunk sets FinalBlockId and last chunks, and last chunk sets it to itself + */ +LONGBOW_TEST_CASE(Local, vegasSession_ReceiveContentObject_InOrder_FirstAndLastBlocksSetsFinalId) +{ + TestVector vectors[] = { + { .chunk = 0, .setFinalBlockId = 7, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 1, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 2, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 3, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 4, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 5, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 6, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + { .chunk = 7, .setFinalBlockId = 7, .isLast = true, .interestReceived = false, .dataReceived = false }, + { .chunk = SENTINEL, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }, + }; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _runTestVector(data, vectors); +} + +// ============================================ + +LONGBOW_TEST_FIXTURE(IterateFinalChunkNumber) +{ + LONGBOW_RUN_TEST_CASE(IterateFinalChunkNumber, vegasSession_ReceiveContentObject_InOrder_FirstSetsSecondIncreasesLastSetsFinalId); +} + +LONGBOW_TEST_FIXTURE_SETUP(IterateFinalChunkNumber) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(IterateFinalChunkNumber) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/* + * First chunk sets FinalBlockId, later chunk increases it by N, then final chunk set to self. + * + * In this test, we programmatically create the TestVector array so we can run different iterations of N. + */ +LONGBOW_TEST_CASE(IterateFinalChunkNumber, vegasSession_ReceiveContentObject_InOrder_FirstSetsSecondIncreasesLastSetsFinalId) +{ + const unsigned minsize = 5; + const unsigned maxsize = 20; + + for (unsigned size = minsize; size < maxsize; size++) { + longBowTestCase_SetClipBoardData(testCase, _commonSetup(longBowTestCase_GetName(testCase))); + + TestVector vectors[size]; + + // set initial state + for (int i = 0; i < size; i++) { + vectors[i] = (TestVector) { .chunk = i, .setFinalBlockId = DO_NOT_SET, .isLast = false, .interestReceived = false, .dataReceived = false }; + } + + // first vectors sets it to minsize + vectors[0].setFinalBlockId = minsize; + + // minsize sets it to the end + vectors[minsize - 1].setFinalBlockId = size; + + // last one sets it to itself + vectors[size - 1].setFinalBlockId = size; + vectors[size - 1].isLast = true; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _runTestVector(data, vectors); + + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + assertTrue(outstandingAllocations == 0, "Memory leak for size %u", size); + } +} + + + +// ============================================ + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(VegasSession); + exit(longBowMain(argc, argv, testRunner, NULL)); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/vegas_Session.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/vegas_Session.c new file mode 100644 index 00000000..a77adc34 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/vegas_Session.c @@ -0,0 +1,1379 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * See additional comments in component_Vegas.c + * + * Flow Control Algorithm + * ========================= + * Based on TCP Vegas. Please read the Vegas paper. We use similar + * variable names to the paper. Code looks quite a bit like the linux + * tcp_vegas.c too. + * + * Here's the differences. In CCN, an Interest is like an ACK token, it + * gives the network permission to send. The node issuing Interests needs + * to pace them to not exceed the network capacity. This is done by + * observing the delay of Content Objects. If the delay grows too quickly, + * then we back off linearly. If the delay is not much above what we expected + * based on the minimum observed delay, we increase linearly. + * + * During slow start, the interest window (still called "cwnd") doubles + * every other RTT until we exceed the slow_start_threshold or the delay + * increases too much. + * + * The RTT is calculated every RTT based on the observed minimum RTT during + * the previous period. + * + * We use RFC6298 Retransmission Timeout (RTO) calculation methods per + * flow control session (object basename). + * + * Just to be clear, there are two timers working. The RTO timer is for + * retransmitting interests if the flow as stalled out. The Vegas RTT + * calculation is for congestion window calculations. + * + * We we receive an out-of-order content object, we'll check the earlier + * segments to see if they have passed the Vegas RTT. If so, we'll + * re-express the interests. + * + * Each time we re-express an Interest, we might decrese the congestion + * window. If the last time the interest was sent was more recent than + * the last time we decreased the congestion window, we'll decrease the + * congestion window. If the last expression of the interest was before + * the most recent window decrease, the window is left alone. This means + * we'll only decreae the window once per re-expression. + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <limits.h> +#include <sys/queue.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_SafeMemory.h> + +#include <parc/algol/parc_EventTimer.h> + +#include <ccnx/common/ccnx_NameSegmentNumber.h> +#include <ccnx/common/ccnx_WireFormatMessage.h> + +#include <ccnx/transport/common/transport_Message.h> +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_Services.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> +#include <ccnx/transport/transport_rta/components/component_Flowcontrol.h> +#include "vegas_private.h" + +#include <ccnx/transport/test_tools/traffic_tools.h> + +#include <ccnx/common/internal/ccnx_InterestDefault.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + + +#define USE_MIN_BASE_RTT 0 + + +// initial congestion window of 2 interests +#define FC_INIT_CWND 2 + +// maximum cwnd (at 8KB/object, makes this 128 MB) +#define FC_MAX_CWND 16384 + +#define FC_MAX_SSTHRESH FC_MAX_CWND + +// initial RTT in msec (100 msec) +#define FC_INIT_RTT_MSEC 100 + +// initial RTO in msec +#define FC_INIT_RTO_MSEC 1000 + +#define FC_MSS 8704 +#define min(a, b) ((a < b) ? a : b) +#define max(a, b) ((a > b) ? a : b) + +// =========================================================== +struct vegas_connection_state; + +struct fc_window_entry { + bool valid; + ticks t; + ticks t_first_request; + segnum_t segnum; + + // set to true on the first interest request for + // the segment, false on subsequent requests + // Needed for Karn's algorithm on RTT sampling for RTO + bool first_request; + + // Content Object read + TransportMessage *transport_msg; +}; + +struct vegas_session { + RtaConnection *parent_connection; + RtaFramework *parent_framework; + VegasConnectionState *parent_fc; + + // next sampling time + ticks next_rtt_sample; + + // minimum observed RTT + int64_t base_RTT; // absolute minimum observed + int64_t min_RTT; // minimum RTT in current sample + int cnt_RTT; // number of RTTs seen in current sample + int64_t sum_RTT; // sum of RTTs + int slow_start_threshold; + + // the currently observed RTT + ticks current_rtt; + + // we do one detailed sample per RTT + bool sample_in_progress; + ticks sample_start; + uint64_t sample_segnum; + uint64_t sample_bytes_recevied; + + // Only adjust the cwnd every 2 RTTs. This + // indicates if we should adjust the RTT at the + // end of this sampling period + int do_fc_this_rtt; + + // circular buffer for segments + // tail - head (mod FC_MAX_CWND) is how may outstanding interests + // are in-flight. If the cwnd has been reduced, it could be larger + // than current_cwnd. + uint64_t starting_segnum; // segnum of the head + int window_head; // window index to read from + int window_tail; // window index to insert at + + uint32_t current_cwnd; + ticks last_cwnd_adjust; + + uint64_t final_segnum; // if we know the final block ID + + struct fc_window_entry window[FC_MAX_CWND]; + + PARCEventTimer *tick_event; + + // we will generate Interests with the same version as was received to start the session. + // Will also use the same lifetime settings as the original Interest. + + CCNxInterestInterface *interestInterface; + uint32_t lifetime; + PARCBuffer *keyIdRestriction; + CCNxName *basename; + uint64_t name_hash; + + uint64_t cnt_old_segments; + uint64_t cnt_fast_reexpress; + + // These are for RTO calculation + ticks SRTT; + ticks RTTVAR; + ticks RTO; + ticks next_rto; // when the next timer expires + + PARCLogLevel logLevel; +}; + +// Control parameters, measured in segments (tcp) or objects (ccn) +static int alpha = 2; +static int beta = 32; +static int _gamma = 1; + +// =========================================================== + + + +static void vegasSession_ExpressInterests(VegasSession *session); +static int vegasSession_ExpressInterestForEntry(VegasSession *session, struct fc_window_entry *entry); + +static void vegasSession_FastReexpress(VegasSession *session, struct fc_window_entry *ack_entry); +static void vegasSession_ForwardObjectsInOrder(VegasSession *session); + +static int vegasSession_GetSegnumFromObject(CCNxTlvDictionary *contentObjectDictionary, uint64_t *segnum); +static struct fc_window_entry * +vegasSession_GetWindowEntry(VegasSession *session, TransportMessage *tm, uint64_t segnum); + +static void vegasSession_ReleaseWindowEntry(struct fc_window_entry *entry); +static void vegasSession_RunAlgorithmOnReceive(VegasSession *session, struct fc_window_entry *entry); + +static void vegasSession_SetTimer(VegasSession *session, ticks tick_delay); +static void vegasSession_SlowReexpress(VegasSession *session); + +// ======================================================================= + + +static struct fc_window_entry * +vegasSession_GetWindowEntry(VegasSession *session, TransportMessage *tm, uint64_t segnum) +{ + int offset; + struct fc_window_entry *entry; + + offset = ((segnum - session->starting_segnum) + session->window_head) % FC_MAX_CWND; + entry = &session->window[offset]; + + assertTrue(entry->valid, "Requesting window entry for invalid entry %p", (void *) entry); + assertTrue(segnum == entry->segnum, "Expected seqnum not equal to window entry, expected %" PRIu64 ", got %" PRIu64, segnum, entry->segnum); + + if (entry->transport_msg != NULL) { + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info, __func__, + "session %p duplicate segment %" PRIu64 "", (void *) session, entry->segnum); + } + + transportMessage_Destroy(&entry->transport_msg); + } + + // store the content object + entry->transport_msg = tm; + + return entry; +} + +static int +vegasSession_GetSegnumFromObject(CCNxTlvDictionary *contentObjectDictionary, uint64_t *segnum) +{ + CCNxName *name = ccnxContentObject_GetName(contentObjectDictionary); + assertNotNull(name, "Content Object has null name") + { + ccnxTlvDictionary_Display(contentObjectDictionary, 0); + } + + bool success = trafficTools_GetObjectSegmentFromName(name, segnum); + + if (success) { + return 0; + } + return -1; +} + +static void +vegasSession_ReduceCongestionWindow(VegasSession *session) +{ + if (session->current_cwnd <= session->slow_start_threshold) { + // 3/4 it + session->current_cwnd = session->current_cwnd / 2 + session->current_cwnd / 4; + } else { + // in linear mode + session->current_cwnd--; + } + + if (session->current_cwnd < 2) { + session->current_cwnd = 2; + } + + session->last_cwnd_adjust = rtaFramework_GetTicks(session->parent_framework); +} + +static void +vegasSession_RunAlgorithmOnReceive(VegasSession *session, struct fc_window_entry *entry) +{ + ticks now; + int64_t fc_rtt; + + now = rtaFramework_GetTicks(session->parent_framework); + + // perform statistics updates. + + // If the codec did not include the raw message, we cannot increment the bytes counter + PARCBuffer *wireFormat = ccnxWireFormatMessage_GetWireFormatBuffer(transportMessage_GetDictionary(entry->transport_msg)); + + if (wireFormat) { + session->sample_bytes_recevied += parcBuffer_Remaining(wireFormat); + } + + + /* add +1 so never have 0 RTT */ + fc_rtt = ((int64_t) now - (int64_t) entry->t_first_request) + 1; + if (fc_rtt <= 0) { + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Error)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Error, __func__, + "session %p sock %3d : recv segment %" PRIu64 " with negative RTT, t = %" PRIu64 "", + (void *) session, + rtaConnection_GetConnectionId(session->parent_connection), + entry->segnum, + entry->t); + } + + return; + } + + /* record the absolute minimum RTT ever seen */ + if (fc_rtt < session->base_RTT) { + session->base_RTT = fc_rtt; + } + + /* find the minimum RTT for the sample period */ + session->min_RTT = min(session->min_RTT, fc_rtt); + session->cnt_RTT++; + session->sum_RTT += fc_rtt; + + // calculate RTO as per RFC6298 + if (entry->first_request) { + if (session->SRTT == 0) { + // this is the first one, so do 2.2 + session->SRTT = fc_rtt; + session->RTTVAR = fc_rtt >> 1; + session->RTO = session->SRTT + + max(rtaFramework_UsecToTicks(1000000), 4 * session->RTTVAR); + } else { + // RTTVAR <- (1 - beta) * RTTVAR + beta * |SRTT - R'| + // using beta = 1/4, so we want 3/4 * RTTVAR + int64_t abs = ((int64_t) session->SRTT - (int64_t) fc_rtt); + + if (abs < 0) { + abs = -1 * abs; + } + + session->RTTVAR = ((session->RTTVAR >> 1) + (session->RTTVAR >> 2)) + (abs >> 2); + + // SRTT <- (1 - alpha) * SRTT + alpha * R' + // using alpha = 1/8 and (1-alpha) = 1/2 + 1/4 + 1/8 = 7/8 + session->SRTT = (session->SRTT >> 1) + (session->SRTT >> 2) + (session->SRTT >> 3) + (abs >> 3); + + session->RTO = session->SRTT + + max(rtaFramework_UsecToTicks(1000000), 4 * session->RTTVAR); + } + } + + // we received a packet :) yay. + // we get to extend the RTO expiry + session->next_rto = now + session->RTO; +} + +/* + * called inside workq_mutex lock. + * After we deliver each segment, we increment session->starting_segnum. After we deliver the + * the terminal segement of a stream, session->starting_segnum will be 1 past the final block id. + */ +static void +vegasSession_ForwardObjectsInOrder(VegasSession *session) +{ + while (session->window_head != session->window_tail) { + struct fc_window_entry *entry = &session->window[ session->window_head ]; + + // sanity checks + assertTrue(entry->valid, "Window entry %p for window_head index %u", (void *) entry, session->window_head); + assertTrue(entry->segnum == session->starting_segnum, + "Expected seqnum not equal to window entry, expected %" PRIu64 ", got %" PRIu64, + session->starting_segnum, + entry->segnum); + + if (entry->transport_msg != NULL) { + PARCEventQueue *out = rtaComponent_GetOutputQueue(session->parent_connection, FC_VEGAS, RTA_UP); + RtaComponentStats *stats = rtaConnection_GetStats(session->parent_connection, FC_VEGAS); + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "session %p fd %d forward segment %" PRIu64 " up stack", + (void *) session, + rtaConnection_GetConnectionId(session->parent_connection), + entry->segnum); + } + + if (rtaComponent_PutMessage(out, entry->transport_msg)) { + // if we successfully put the message up the stack, null + // the entry so the transport message will not be destroyed + // when this window entry is released. + entry->transport_msg = NULL; + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + } + + vegasSession_ReleaseWindowEntry(entry); + session->starting_segnum++; + session->window_head = (session->window_head + 1) % FC_MAX_CWND; + } else { + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "session %p fd %d no message segment %" PRIu64 ", no more in order messages", + rtaConnection_GetConnectionId(session->parent_connection), + entry->segnum); + } + + return; + } + } +} + +static int +fc_ssthresh(VegasSession *session) +{ + return min(session->slow_start_threshold, session->current_cwnd - 1); +} + +/** + * Slow-start increase, double the cwnd + */ +static void +fc_slow_start(VegasSession *session) +{ + session->last_cwnd_adjust = rtaFramework_GetTicks(session->parent_framework); + session->current_cwnd = session->current_cwnd << 1; +} + +static +int +fc_in_cwnd_reduction(VegasSession *session) +{ + return 0; +} + +/* + * Similar to the tcp_current_ssthresh. If cwnd > ssthresh, then + * increase ssthres to 1/2 to cwnd, except if we're in a cwnd reduction + * period. + */ +static inline uint32_t +fc_current_ssthresh(VegasSession *session) +{ + if (fc_in_cwnd_reduction(session)) { + return session->slow_start_threshold; + } else { + return max(session->slow_start_threshold, + ((session->current_cwnd >> 1) + + (session->current_cwnd >> 2))); + } +} + +static void +vegasSession_CongestionAvoidanceDebug(VegasSession *session, ticks now) +{ + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + ticks diff = 0; + + if (session->min_RTT != INT_MAX) { + diff = session->current_cwnd * (session->min_RTT - session->base_RTT) / session->base_RTT; + } + + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "session %p do_cong %d currentRTT %5" PRIu64 " cntRTT %3d minRTT %5" PRId64 " baseRTT %5" PRId64 " cwnd %3d next %8" PRIu64 " SRTT %" PRIu64 " RTO %" PRIu64 " oldsegs %" PRIu64 " fast %" PRIu64 " diff %" PRIu64 " allocs %u", + (void *) session, + session->do_fc_this_rtt, + session->current_rtt, + session->cnt_RTT, + session->min_RTT == INT_MAX ? 0 : session->min_RTT, + session->base_RTT == INT_MAX ? 0 : session->base_RTT, + session->current_cwnd, + session->next_rtt_sample, + session->SRTT, + session->RTO, + session->cnt_old_segments, + session->cnt_fast_reexpress, + diff, + parcMemory_Outstanding()); + } +} + +static void +vegasSession_LossBasedAvoidance(VegasSession *session) +{ + session->current_rtt = session->current_rtt * 2; + if (session->current_rtt > 4000) { + session->current_rtt = 4000; + } +} + +/** + * This is the Vegas algorithm + */ +static void +vegasSession_TimeBasedAvoidance(VegasSession *session) +{ + ticks rtt, diff; + uint64_t target_cwnd; + + rtt = session->min_RTT; + + /* + * calculate the target cwnd in segments + */ + target_cwnd = session->current_cwnd * session->base_RTT / rtt; + + diff = session->current_cwnd * (rtt - session->base_RTT) / session->base_RTT; + + if ((diff > _gamma && session->current_cwnd <= session->slow_start_threshold)) { + /* If we're in slow start and going too fast, slow down */ + session->current_cwnd = min(session->current_cwnd, (uint32_t) target_cwnd + 1); + session->slow_start_threshold = fc_ssthresh(session); + session->last_cwnd_adjust = rtaFramework_GetTicks(session->parent_framework); + } else if (session->current_cwnd <= session->slow_start_threshold) { + /* Slow start */ + fc_slow_start(session); + } else { + /* Congestion avoidance. */ + + // if (diff > beta || session->cnt_old_segments ) { + if (diff > beta) { + /* The old window was too fast, so + * we slow down. + */ + + session->current_cwnd--; + session->slow_start_threshold = fc_ssthresh(session); + session->last_cwnd_adjust = rtaFramework_GetTicks(session->parent_framework); + } else if (diff < alpha) { + /* room to grow */ + session->current_cwnd++; + session->last_cwnd_adjust = rtaFramework_GetTicks(session->parent_framework); + } else { + /* middle ground, no changes necessary */ + } + } + + if (session->current_cwnd < 2) { + session->current_cwnd = 2; + } else if (session->current_cwnd > FC_MAX_CWND) { + session->current_cwnd = FC_MAX_CWND; + } + + session->slow_start_threshold = fc_current_ssthresh(session); +} + +static void +vegasSession_CongestionAvoidance(VegasSession *session) +{ + ticks now = rtaFramework_GetTicks(session->parent_framework); + + vegasSession_CongestionAvoidanceDebug(session, now); + + if (session->do_fc_this_rtt) { + if (session->cnt_RTT <= 2) { + vegasSession_LossBasedAvoidance(session); + } else { + vegasSession_TimeBasedAvoidance(session); + } + + session->do_fc_this_rtt = 0; + } else { + session->do_fc_this_rtt = 1; + } + + // Now finish up the statistics and setup for next RTT interval + + session->next_rtt_sample = now + session->current_rtt; + + // low-pass filter the base_RTT from the min_RTT + // base_RTT = 15/16 base_RTT + 1/16 min_RTT = (240 * base_RTT + 16 * min_RTT ) / 256 + + if (!USE_MIN_BASE_RTT && (session->cnt_RTT > 0)) { + session->base_RTT = (240 * session->base_RTT + 16 * session->min_RTT) >> 8; + if (session->base_RTT == 0) { + session->base_RTT = 1; + } + } + + // Smooth the RTT for (3 * current + 1 * minimum) / 4 + + if (session->cnt_RTT > 0) { + session->current_rtt = (12 * session->current_rtt + 4 * session->min_RTT) >> 4; + } + + session->current_rtt = max(session->current_rtt, FC_INIT_RTT_MSEC); + + // reset stats + session->sample_bytes_recevied = 0; + session->min_RTT = INT_MAX; + session->cnt_RTT = 0; + session->cnt_old_segments = 0; + session->cnt_fast_reexpress = 0; + session->sum_RTT = 0; + + vegasSession_CongestionAvoidanceDebug(session, now); +} + +/** + * Slow (course grain) retransmission due to RTO expiry. + * Re-express the first segment of the window. + */ +static +void +vegasSession_SlowReexpress(VegasSession *session) +{ + struct fc_window_entry *entry = &session->window[ session->window_head ]; + + assertTrue(entry->valid, "entry %p segnum %" PRIu64 " invalid state, in window but not valid", + (void *) entry, entry->segnum); + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info, __func__, + "Session %p conn %p RTO re-expression for segnum %" PRIu64 "", + (void *) session, (void *) session->parent_connection, entry->segnum); + } + + entry->first_request = false; + vegasSession_ExpressInterestForEntry(session, entry); +} + +/** + * Do fast retransmissions based on SRTT smoothed estimate. + * ack_entry is the entry for a content object we just received. Look earlier segments + * and if they were asked for more than SRTT ago, ask again. + */ +static void +vegasSession_FastReexpress(VegasSession *session, struct fc_window_entry *ack_entry) +{ + ticks now = rtaFramework_GetTicks(session->parent_framework); + int64_t delta; + uint64_t segnum; + uint64_t top_segnum; + + // This method is called after forward_in_order, so it's possible that + // ack_entry is no longer valid, meaning we've moved the window past it. + // In that case, we're done. + if (ack_entry->valid == false) { + return; + } + + // we don't retransmit beyond the current cwnd. ack_entry might be outside + // the cwnd. + + top_segnum = min(ack_entry->segnum, session->starting_segnum + session->current_cwnd); + + for (segnum = session->starting_segnum; segnum < top_segnum; segnum++) { + int index = (session->window_head + (segnum - session->starting_segnum)) % FC_MAX_CWND; + delta = (int64_t) now - ((int64_t) session->window[index].t + (int64_t) session->SRTT); + + // allow up to -1 slack, because the RunAlgorithm adds +1 to fc_rtt. + if (delta >= -1) { + // we have past the SRTT timeout + + // if we last re-transmitted him since the last cwnd adjustment, adjust again + if ((int64_t) session->window[index].t - (int64_t) session->last_cwnd_adjust >= 0) { + vegasSession_ReduceCongestionWindow(session); + } + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info, __func__, + "session %p conn %p RTO re-expression for segnum %" PRIu64 "", + (void *) session, (void *) session->parent_connection, session->window[index].segnum); + } + + session->window[index].first_request = false; + session->cnt_fast_reexpress++; + vegasSession_ExpressInterestForEntry(session, &session->window[index]); + } + } +} + +/** + * Generates an Interest message for the window entry. + * + * No side effects, apart from putting on Interest on the down queue. + * If the down direction is blocked, this function will not put an interest in the down queue. It will + * look like a lost interest to the flow controller, which should cause the flow controller to slow down. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static int +vegasSession_ExpressInterestForEntry(VegasSession *session, struct fc_window_entry *entry) +{ + if (!rtaConnection_BlockedDown(session->parent_connection)) { + ticks now = rtaFramework_GetTicks(session->parent_framework); + PARCEventQueue *q_out; + TransportMessage *tm_out; + CCNxName *chunk_name; + + entry->t = now; + + chunk_name = ccnxName_Copy(session->basename); + + CCNxNameSegment *segment = ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, entry->segnum); + ccnxName_Append(chunk_name, segment); + ccnxNameSegment_Release(&segment); + + assertNotNull(session->interestInterface, "Got a NULL interestInterface. Should not happen."); + + CCNxTlvDictionary *interestDictionary = + session->interestInterface->create(chunk_name, + session->lifetime, + NULL, // ppkid + NULL, // content object hash + CCNxInterestDefault_HopLimit); + + if (session->keyIdRestriction != NULL) { + session->interestInterface->setKeyIdRestriction(interestDictionary, session->keyIdRestriction); + } + + tm_out = transportMessage_CreateFromDictionary(interestDictionary); + transportMessage_SetInfo(tm_out, rtaConnection_Copy(session->parent_connection), rtaConnection_FreeFunc); + + q_out = rtaComponent_GetOutputQueue(session->parent_connection, FC_VEGAS, RTA_DOWN); + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + char *string = ccnxName_ToString(chunk_name); + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "session %p entry %p segname %p segnum %" PRIu64 " %s sent", + (void *) session, + (void *) entry, + (void *) chunk_name, + entry->segnum, + string); + parcMemory_Deallocate((void **) &string); + } + + ccnxTlvDictionary_Release(&interestDictionary); + ccnxName_Release(&chunk_name); + + if (rtaComponent_PutMessage(q_out, tm_out)) { + rtaComponentStats_Increment(rtaConnection_GetStats(session->parent_connection, FC_VEGAS), + STATS_DOWNCALL_OUT); + } + } else { + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info)) { + CCNxName *segment_name = ccnxName_Copy(session->basename); + ccnxName_Append(segment_name, ccnxNameSegmentNumber_Create(CCNxNameLabelType_CHUNK, entry->segnum)); + char *string = ccnxName_ToString(segment_name); + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info, __func__, + "session %p entry %p segname %p segnum %" PRIu64 " %s SUPPRESSED BLOCKED DOWN QUEUE", + (void *) session, + (void *) entry, + (void *) segment_name, + entry->segnum, + string); + parcMemory_Deallocate((void **) &string); + ccnxName_Release(&segment_name); + } + } + + return 0; +} + +/* + * Express interests out to the max allowed by the cwnd. This function will operate + * even if the down queue is blocked. Those interests will be treated as lost, which will cause + * the flow controller to slow down. + */ +static void +vegasSession_ExpressInterests(VegasSession *session) +{ + ticks now = rtaFramework_GetTicks(session->parent_framework); + + // how many interests are currently outstanding? + int wsize = session->window_tail - session->window_head; + if (wsize < 0) { + wsize += FC_MAX_CWND; + } + + // if we know the FBID, don't ask for anything beyond that + while (wsize < session->current_cwnd && (wsize + session->starting_segnum <= session->final_segnum)) { + // expreess them + struct fc_window_entry *entry = &session->window[session->window_tail]; + + assertFalse(entry->valid, + "Window entry %d marked as valid, but its outside the cwind!", + session->window_tail); + + session->window_tail = (session->window_tail + 1) % FC_MAX_CWND; + + memset(entry, 0, sizeof(struct fc_window_entry)); + + entry->valid = true; + entry->segnum = session->starting_segnum + wsize; + entry->first_request = true; + entry->t_first_request = now; + + if (session->sample_in_progress == 0) { + // make this interest the sample for the RTT + session->sample_in_progress = true; + session->sample_segnum = entry->segnum; + session->sample_start = now; + session->sample_bytes_recevied = 0; + } + + vegasSession_ExpressInterestForEntry(session, entry); + + wsize++; + } +} + +/* + * This is dispatched from the event loop, so its a loosely accurate time + */ +static void +vegasSession_TimerCallback(int fd, PARCEventType what, void *user_data) +{ + VegasSession *session = (VegasSession *) user_data; + int64_t delta; + ticks now; + + assertTrue(what & PARCEventType_Timeout, "%s got unknown signal %d", __func__, what); + + now = rtaFramework_GetTicks(session->parent_framework); + delta = ((int64_t) now - (int64_t) session->next_rtt_sample); + + if (delta >= 0) { + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "Session %p processing timer, delta %" PRId64, + (void *) session, delta); + } + + // This entry is ready for processing + vegasSession_CongestionAvoidance(session); + + // set the next timer + vegasSession_SetTimer(session, session->current_rtt); + } else { + vegasSession_SetTimer(session, -1 * delta); + } + + // check for retransmission + delta = ((int64_t) now - (int64_t) session->next_rto); + if (delta >= 0) { + // Do this once per RTO + vegasSession_SlowReexpress(session); + + // we're now in a doubling regeme. Reset the + // moving average and double the RTO. + session->SRTT = 0; + session->RTTVAR = 0; + session->RTO = session->RTO * 2; + session->next_rto = now + session->RTO; + } +} + +/** + * precondition: the entry is valid + */ +static void +vegasSession_ReleaseWindowEntry(struct fc_window_entry *entry) +{ + assertTrue(entry->valid, "Called on invalid window entry"); + if (!entry->valid) { + return; + } + + if (entry->transport_msg != NULL) { + transportMessage_Destroy(&entry->transport_msg); + } + entry->valid = false; +} + +static void +vegasSession_SetTimer(VegasSession *session, ticks tick_delay) +{ + struct timeval timeout; + uint64_t usec = rtaFramework_TicksToUsec(tick_delay); + const unsigned usec_per_sec = 1000000; + + timeout.tv_sec = usec / usec_per_sec; + timeout.tv_usec = (int) (usec - timeout.tv_sec * usec_per_sec); + + // this replaces any prior events + parcEventTimer_Start(session->tick_event, &timeout); + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "session %p tick_delay %" PRIu64 " timeout %.6f", + (void *) session, + tick_delay, + timeout.tv_sec + 1E-6 * timeout.tv_usec); + } +} + +// ============================================= +// Private API + +/** + * Unsets the final segment number indicating we do not know the value + * + * Sets the final segment number to the maximum possible value, which effectively + * lets us run off to infinity. + * + * @param [in] session An allocated vegas session + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +_vegasSession_UnsetFinalSegnum(VegasSession *session) +{ + session->final_segnum = ULLONG_MAX; +} + +VegasSession * +vegasSession_Create(VegasConnectionState *fc, RtaConnection *conn, CCNxName *basename, segnum_t begin, + CCNxInterestInterface *interestInterface, uint32_t lifetime, PARCBuffer *keyIdRestriction) +{ + assertNotNull(conn, "Called with null connection"); + assertNotNull(basename, + "conn %p connid %u called with null basename", + (void *) conn, + rtaConnection_GetConnectionId(conn)); + + if (conn == NULL || basename == NULL) { + return NULL; + } + + VegasSession *session = parcMemory_AllocateAndClear(sizeof(VegasSession)); + assertNotNull(session, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(VegasSession)); + session->parent_connection = conn; + session->parent_framework = rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn)); + session->interestInterface = interestInterface; + session->lifetime = lifetime; + session->basename = basename; + if (keyIdRestriction != NULL) { + session->keyIdRestriction = parcBuffer_Acquire(keyIdRestriction); + } + session->parent_fc = fc; + + session->tick_event = parcEventTimer_Create(rtaFramework_GetEventScheduler(session->parent_framework), 0, vegasSession_TimerCallback, (void *) session); + + session->starting_segnum = 0; + session->current_cwnd = FC_INIT_CWND; + session->min_RTT = INT_MAX; + session->base_RTT = INT_MAX; + session->do_fc_this_rtt = 0; + session->current_rtt = rtaFramework_UsecToTicks(FC_INIT_RTT_MSEC * 1000); + session->slow_start_threshold = FC_MAX_SSTHRESH; + + session->SRTT = 0; + session->RTTVAR = 0; + session->RTO = rtaFramework_UsecToTicks(FC_INIT_RTO_MSEC * 1000); + session->next_rto = ULLONG_MAX; + session->cnt_old_segments = 0; + session->cnt_fast_reexpress = 0; + + _vegasSession_UnsetFinalSegnum(session); + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Notice)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Notice, __func__, + "session %p initialized connid %u ", + (void *) session, + rtaConnection_GetConnectionId(conn)); + } + return session; +} + +static void +vegasSession_Close(VegasSession *session) +{ + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Notice)) { + char *p = ccnxName_ToString(session->basename); + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Notice, __func__, + "session %p close starting segnum %" PRIu64 " final chunk ID %" PRIu64 " for name %s", + (void *) session, session->starting_segnum, session->final_segnum, p); + parcMemory_Deallocate((void **) &p); + } + + ccnxName_Release(&session->basename); + + while (session->window_head != session->window_tail) { + struct fc_window_entry *entry = &session->window[ session->window_head ]; + + // sanity checks + assertTrue(entry->valid, "connid %u session %p entry %d in window but not valid", + rtaConnection_GetConnectionId(session->parent_connection), + (void *) session, + session->window_head); + + if (entry->valid) { + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + char *p = ccnxName_ToString(session->basename); + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "session %p releasing window entry %d", (void *) session, session->window_head); + parcMemory_Deallocate((void **) &p); + } + + vegasSession_ReleaseWindowEntry(entry); + } + + session->window_head = (session->window_head + 1) % FC_MAX_CWND; + } +} + +void +vegasSession_Destroy(VegasSession **sessionPtr) +{ + VegasSession *session; + + assertNotNull(sessionPtr, "Called with null double pointer"); + session = *sessionPtr; + + if (session->keyIdRestriction != NULL) { + parcBuffer_Release(&session->keyIdRestriction); + } + + vegasSession_Close(session); + + parcEventTimer_Destroy(&(session->tick_event)); + parcMemory_Deallocate((void **) &session); + sessionPtr = NULL; +} + +int +vegasSession_Start(VegasSession *session) +{ + ticks now = rtaFramework_GetTicks(session->parent_framework); + + // express the initial interests + vegasSession_ExpressInterests(session); + + session->next_rtt_sample = now - 1; + session->next_rto = now + session->RTO; + + // put it on the work queue for procesing + + vegasSession_SetTimer(session, session->current_rtt); + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info, __func__, + "Session %p start", (void *) session); + } + + return 0; +} + +int +vegasSession_Pause(VegasSession *session) +{ + trapNotImplemented("vegasSession_Pause"); +} + +int +vegasSession_Resume(VegasSession *session) +{ + trapNotImplemented("vegasSession_Resume"); +} + +int +vegasSession_Seek(VegasSession *session, segnum_t absolutePosition) +{ + trapNotImplemented("vegasSession_See)"); +} + +/** + * Retrieves the final block ID from the content object + * + * Retreives the final block ID from the object, if it exists, and returns it in + * an output parameter. Returns true if found and returned, false otherwise. + * + * @param [in] obj The Content Object to get the FBID form + * @param [out] output Pointer to the seqnum ouptut + * + * @return true If the content object contained a FBID and the output set + * @return false If there is no FBID in the content object + * + * Example: + * @code + * <#example#> + * @endcode + */ +static bool +vegasSession_GetFinalBlockIdFromContentObject(CCNxTlvDictionary *obj, uint64_t *output) +{ + bool result = false; + if (ccnxContentObject_HasFinalChunkNumber(obj)) { + *output = ccnxContentObject_GetFinalChunkNumber(obj); + result = true; + } + return result; +} + +/** + * Sets the final block id in the session based on the signed info + * + * If the final block id exists in the signed info, set the session's FBID. + * + * Rules on FinalChunkNumber: + * + * 1) The “final chunk” of a stream is identified by a content object having a FinalChunkNumber + * set in its metadata that equals the chunk number in its name. + * + * 2) An application may set the FinalChunkNumber early to let a receiver know when the end is coming. These early advisories are not binding. + * + * 3) If the application has ever set the FinalChunkNumber it may not decrease it. If the actual end happens before a previous advisory, + * the application must publish no-payload content objects such that Rule #1 is satisfied + * + * + * @param [in,out] session The Vegas session + * @param [in] obj The signed content object to get the FBID from + * @param [in] nameChunkNumber is the chunk number in the name + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +vegasSession_SetFinalBlockId(VegasSession *session, CCNxTlvDictionary *contentObjectDictionary, uint64_t nameChunkNumber) +{ + // Get the FinalChunkNumber out of the metadata and update our notion of it + uint64_t finalChunkNumber; + if (vegasSession_GetFinalBlockIdFromContentObject(contentObjectDictionary, &finalChunkNumber)) { + session->final_segnum = finalChunkNumber; + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info, __func__, + "Session %p finalChunkNumber %" PRIu64, (void *) session, session->final_segnum); + } + } else { + // There is no final chunk number in the metadata. If the nameChunkNumber == session->final_seqnum, then + // our idea of the final_seqnum is wrong and we should unset it as the producer did not actually close + // the stream when they said they would + + if (session->final_segnum == nameChunkNumber) { + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Warning)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Warning, __func__, + "Session %p finalChunkNumber %" PRIu64 " not set in final chunk, resetting", + (void *) session, session->final_segnum); + } + + _vegasSession_UnsetFinalSegnum(session); + } + } +} + +/** + * We received a duplicate segment from before the start of the current congestion window + * + * + * If we receive a segment from before the start of the current congestion window, then it + * must be a duplicate (we don't have skip forward implemented). Reduce the congestion window size. + * We only reduce the window once per RTT interval no matter how many early duplicates we get. + * + * @param [in,out] session The Vegas session to reduce the window of. + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +vegasSession_ReceivedBeforeWindowStart(VegasSession *session, uint64_t segnum) +{ + // once per cwnd, reduce the window on out-of-order + if (session->cnt_old_segments == 0) { + vegasSession_ReduceCongestionWindow(session); + } + + session->cnt_old_segments++; + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "Session %p connid %3u : recv old segment %" PRIu64 ", starting is %" PRIu64 ", cnt %" PRIu64 "", + (void *) session, + rtaConnection_GetConnectionId(session->parent_connection), + segnum, + session->starting_segnum, + session->cnt_old_segments); + } +} + +static void +vegasSession_SendMoreInterests(VegasSession *session, struct fc_window_entry *entry) +{ + // This will check if there's any earlier segments whose + // RTT has expired and will re-ask for them. This is the + // out-of-order fast retransmit. + vegasSession_FastReexpress(session, entry); + + // have we finished? + if (session->starting_segnum < session->final_segnum) { + // express more interests if we have the window for it + vegasSession_ExpressInterests(session); + } else + if (session->starting_segnum > session->final_segnum) { + // if starting_segment > final_segnum it means that we have delivered the last + // segment up the stack. + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info, __func__, + "Session %p connid %u starting_segnum %" PRIu64 ", final_segnum %" PRIu64 ", FINAL SEGMENT DELIVERED, CLOSING", + (void *) session, + rtaConnection_GetConnectionId(session->parent_connection), + session->starting_segnum, + session->final_segnum); + } + + parcEventTimer_Stop(session->tick_event); + vegas_EndSession(session->parent_fc, session); + } + // else session->starting_segnum == session->final_segnum, we're not done yet. +} + +static CCNxName * +vegasSession_GetNameFromTransportMessage(TransportMessage *tm) +{ + CCNxName *name = NULL; + CCNxTlvDictionary *dictionary = transportMessage_GetDictionary(tm); + switch (ccnxTlvDictionary_GetSchemaVersion(dictionary)) { + case CCNxTlvDictionary_SchemaVersion_V1: + name = ccnxTlvDictionary_GetName(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME); + break; + + default: + break; + } + return name; +} + + + +int +vegasSession_ReceiveContentObject(VegasSession *session, TransportMessage *tm) +{ + assertTrue(transportMessage_IsContentObject(tm), + "Transport message is not a content object"); + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + CCNxName *name = vegasSession_GetNameFromTransportMessage(tm); + char *nameString = NULL; + if (name) { + nameString = ccnxName_ToString(name); + } + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "Session %p connid %3u receive tm %p: %s", + (void *) session, + rtaConnection_GetConnectionId(session->parent_connection), + (void *) tm, + nameString); + if (nameString) { + parcMemory_Deallocate((void **) &nameString); + } + } + + CCNxTlvDictionary *contentObjectDictionary = transportMessage_GetDictionary(tm); + + // get segment number + uint64_t segnum; + int res = vegasSession_GetSegnumFromObject(contentObjectDictionary, &segnum); + if (res != 0) { + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Warning)) { + CCNxName *name = vegasSession_GetNameFromTransportMessage(tm); + char *nameString = NULL; + if (name) { + nameString = ccnxName_ToString(name); + } + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Warning, __func__, + "Session %p connid %3u receive tm %p has no segment number: %s", + (void *) session, + rtaConnection_GetConnectionId(session->parent_connection), + (void *) tm, + nameString); + if (nameString) { + parcMemory_Deallocate((void **) &nameString); + } + } + + // couldn't figure it out + transportMessage_Destroy(&tm); + return -1; + } + + // drop out of order + if (segnum < session->starting_segnum) { + vegasSession_ReceivedBeforeWindowStart(session, segnum); + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "Session %p connid %3u : tm %p received segnum %" PRIu64 " before current head %" PRIu64 "", + (void *) session, + __func__, + rtaConnection_GetConnectionId(session->parent_connection), + (void *) tm, + segnum, + session->starting_segnum); + } + + transportMessage_Destroy(&tm); + return -1; + } + + // Update our idea of the final chunk number. This must be done + // before running the algorithm because session->final_segnum is used + // to decide if we're done. + vegasSession_SetFinalBlockId(session, contentObjectDictionary, segnum); + + + // now run the algorithm on the received object + + struct fc_window_entry *entry = vegasSession_GetWindowEntry(session, tm, segnum); + + if (rtaLogger_IsLoggable(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug)) { + CCNxName *name = vegasSession_GetNameFromTransportMessage(tm); + char *nameString = NULL; + if (name) { + nameString = ccnxName_ToString(name); + } + rtaLogger_Log(rtaFramework_GetLogger(session->parent_framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Debug, __func__, + "Session %p connid %3u receive tm %p segment %" PRIu64 " receive: %s", + (void *) session, + rtaConnection_GetConnectionId(session->parent_connection), + (void *) tm, + segnum, + nameString); + if (nameString) { + parcMemory_Deallocate((void **) &nameString); + } + } + + vegasSession_RunAlgorithmOnReceive(session, entry); + + // forward in-order objects to the user fc + if (!rtaConnection_BlockedUp(session->parent_connection)) { + vegasSession_ForwardObjectsInOrder(session); + } + + vegasSession_SendMoreInterests(session, entry); + + return 0; +} + +unsigned +vegasSession_GetConnectionId(VegasSession *session) +{ + assertNotNull(session, "Parameter session must be non-null"); + return rtaConnection_GetConnectionId(session->parent_connection); +} + +void +vegasSession_StateChanged(VegasSession *session) +{ + if (rtaConnection_BlockedUp(session->parent_connection)) { + // if we're blocked in the up direction, don't do anything. We make this + // check every time we're about ti send stuff up the stack in vegasSession_ReceiveContentObject(). + } else { + // unblocked, forward packets + vegasSession_ForwardObjectsInOrder(session); + } + + if (rtaConnection_BlockedDown(session->parent_connection)) { + // stop generating interests + } else { + // restart interests + } +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/vegas_private.h b/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/vegas_private.h new file mode 100644 index 00000000..b2088567 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/vegas_private.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file vegas_private.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef Libccnx_vegas_private_h +#define Libccnx_vegas_private_h + +#include <ccnx/common/ccnx_Name.h> +#include <ccnx/common/internal/ccnx_ContentObjectInterface.h> + +typedef uint64_t segnum_t; + +struct vegas_session; +typedef struct vegas_session VegasSession; + +struct vegas_connection_state; +typedef struct vegas_connection_state VegasConnectionState; + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] fc An allocated Vegas flow controller + * @param [in] conn The RTA connection owning the flow + * @param [in] basename The name without a chunk number + * @param [in] begin The chunk number to begin requesting at + * @param [in] interestInterface The {@link CCNxInterestInterface} to use to generate new Interests + * @param [in] lifetime The default lifetime, in milli-seconds, to use for generated Interests + * @param [in] keyIdRestriction The KeyIdRestriction, if any, from the originating Interest + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +VegasSession *vegasSession_Create(VegasConnectionState *fc, RtaConnection *conn, CCNxName *basename, + segnum_t begin, CCNxInterestInterface *interestInterface, uint32_t lifetime, + PARCBuffer *keyIdRestriction); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void vegasSession_Destroy(VegasSession **sessionPtr); +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +int vegasSession_Start(VegasSession *session); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +int vegasSession_Pause(VegasSession *session); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +int vegasSession_Resume(VegasSession *session); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +int vegasSession_Seek(VegasSession *session, segnum_t absolutePosition); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +int vegasSession_ReceiveContentObject(VegasSession *session, TransportMessage *tm); + + +/** + * Tell a session that there was a state change in its connection + * + * The caller should ensure that the session's connection is the right one by + * using {@link vegasSession_GetConnectionId}. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void vegasSession_StateChanged(VegasSession *session); + +/** + * Returns the connection id used by the session + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +unsigned vegasSession_GetConnectionId(VegasSession *session); + + +/** + * <#One Line Description#> + * + * Called by a session when it is done + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void vegas_EndSession(VegasConnectionState *fc, VegasSession *session); +#endif // Libccnx_vegas_private_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/codec_Signing.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/codec_Signing.c new file mode 100644 index 00000000..322bea2e --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/codec_Signing.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> + +#include <parc/security/parc_PublicKeySigner.h> +#include <parc/security/parc_Pkcs12KeyStore.h> +#include <parc/security/parc_SymmetricKeySigner.h> +#include <parc/security/parc_SymmetricKeyStore.h> +#include <parc/security/parc_KeyStore.h> +#include <parc/security/parc_Signer.h> +#include <parc/security/parc_CryptoHashType.h> + +#include <ccnx/transport/transport_rta/config/config_Signer.h> +#include "codec_Signing.h" + +PARCSigner * +component_Codec_GetSigner(RtaConnection *conn) +{ + PARCSigner *signer = NULL; + + SignerType signertype = signer_GetImplementationType(rtaConnection_GetParameters(conn)); + + switch (signertype) { + case SignerType_SymmetricKeySigner: { + struct symmetrickeysigner_params params; + bool success = symmetricKeySigner_GetConnectionParams(rtaConnection_GetParameters(conn), ¶ms); + assertTrue(success, "Could not retrieve symmetricKeySigner_GetConnectionParams"); + + PARCSymmetricKeyStore *symmetricKeyStore = parcSymmetricKeyStore_OpenFile(params.filename, params.password, PARCCryptoHashType_SHA256); + PARCSymmetricKeySigner *symmetricKeySigner = parcSymmetricKeySigner_Create(symmetricKeyStore, PARCCryptoHashType_SHA256); + parcSymmetricKeyStore_Release(&symmetricKeyStore); + + signer = parcSigner_Create(symmetricKeySigner, PARCSymmetricKeySignerAsSigner); + parcSymmetricKeySigner_Release(&symmetricKeySigner); + assertNotNull(signer, "got null opening FileKeystore '%s'\n", params.filename); + break; + } + + case SignerType_PublicKeySigner: { + struct publickeysigner_params params; + bool success = publicKeySigner_GetConnectionParams(rtaConnection_GetParameters(conn), ¶ms); + assertTrue(success, "Could not retrieve publicKeySigner_GetConnectionParams"); + + PARCPkcs12KeyStore *pkcs12KeyStore = parcPkcs12KeyStore_Open(params.filename, params.password, PARCCryptoHashType_SHA256); + PARCKeyStore *keyStore = parcKeyStore_Create(pkcs12KeyStore, PARCPkcs12KeyStoreAsKeyStore); + parcPkcs12KeyStore_Release(&pkcs12KeyStore); + PARCPublicKeySigner *publicKeySigner = parcPublicKeySigner_Create(keyStore, PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256); + parcKeyStore_Release(&keyStore); + + signer = parcSigner_Create(publicKeySigner, PARCPublicKeySignerAsSigner); + parcPublicKeySigner_Release(&publicKeySigner); + assertNotNull(signer, "got null opening FileKeystore '%s'\n", params.filename); + break; + } + + default: + assertTrue(0, "Unsupported signer type %d", signertype); + } + + assertNotNull(signer, "Did not match a known signer"); + return signer; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/codec_Signing.h b/libccnx-transport-rta/ccnx/transport/transport_rta/components/codec_Signing.h new file mode 100644 index 00000000..d46f1ed4 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/codec_Signing.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file codec_Signing.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef Libccnx_codec_Signing_h +#define Libccnx_codec_Signing_h + +#include <parc/security/parc_Signer.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] connection <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +PARCSigner *component_Codec_GetSigner(RtaConnection *connection); +#endif // Libccnx_codec_Signing_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Codec.h b/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Codec.h new file mode 100644 index 00000000..8cc15220 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Codec.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file component_Codec.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef Libccnx_component_codec_h +#define Libccnx_component_codec_h + +// Function structs for component variations. There's only the TLV codec now. +extern RtaComponentOperations codec_tlv_ops; +#endif // Libccnx_component_codec_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Codec_Tlv.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Codec_Tlv.c new file mode 100644 index 00000000..8f90cb06 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Codec_Tlv.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> +#include <sys/socket.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> + +#include <ccnx/transport/common/transport_Message.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework_Services.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> + +#include <ccnx/common/codec/ccnxCodec_TlvPacket.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h> + +#include "component_Codec.h" +#include "codec_Signing.h" + +// set to 3 or higher for memory dumps of packets +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +static int component_Codec_Tlv_Init(RtaProtocolStack *stack); +static int component_Codec_Tlv_Opener(RtaConnection *conn); +static void component_Codec_Tlv_Upcall_Read(PARCEventQueue *, PARCEventType event, void *conn); +static void component_Codec_Tlv_Downcall_Read(PARCEventQueue *, PARCEventType event, void *conn); +static int component_Codec_Tlv_Closer(RtaConnection *conn); +static int component_Codec_Tlv_Release(RtaProtocolStack *stack); +static void component_Codec_Tlv_StateChange(RtaConnection *conn); + +RtaComponentOperations codec_tlv_ops = { + .init = component_Codec_Tlv_Init, + .open = component_Codec_Tlv_Opener, + .upcallRead = component_Codec_Tlv_Upcall_Read, + .upcallEvent = NULL, + .downcallRead = component_Codec_Tlv_Downcall_Read, + .downcallEvent = NULL, + .close = component_Codec_Tlv_Closer, + .release = component_Codec_Tlv_Release, + .stateChange = component_Codec_Tlv_StateChange +}; + +typedef struct codec_connection_state { + PARCSigner *signer; +} CodecConnectionState; + +// ================== +// NULL + +static int +component_Codec_Tlv_Init(RtaProtocolStack *stack) +{ + // no ProtocolStack wide state + return 0; +} + + +static int +component_Codec_Tlv_Opener(RtaConnection *conn) +{ + struct codec_connection_state *codec_state = parcMemory_AllocateAndClear(sizeof(struct codec_connection_state)); + assertNotNull(codec_state, "%s parcMemory_AllocateAndClear(%zu) returned NULL", __func__, sizeof(struct codec_connection_state)); + + codec_state->signer = component_Codec_GetSigner(conn); + + rtaConnection_SetPrivateData(conn, CODEC_TLV, codec_state); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %u codec signer %p private %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaConnection_GetConnectionId(conn), + (void *) codec_state->signer, + (void *) codec_state); + } + + return 0; +} + +static void +upcallDictionary(TransportMessage *tm, PARCEventQueue *out, RtaComponentStats *stats) +{ + CCNxTlvDictionary *dictionary = transportMessage_GetDictionary(tm); + + PARCBuffer *wireFormat = ccnxWireFormatMessage_GetWireFormatBuffer(dictionary); + bool success = ccnxCodecTlvPacket_BufferDecode(wireFormat, dictionary); + + if (success) { + if (rtaComponent_PutMessage(out, tm)) { + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + } + } else { + printf("Decoding error!"); + parcBuffer_Display(wireFormat, 3); + } +} + +/* Read from below and send to above */ +static void +component_Codec_Tlv_Upcall_Read(PARCEventQueue *in, PARCEventType event, void *ptr) +{ + RtaProtocolStack *stack = (RtaProtocolStack *) ptr; + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(stack, CODEC_TLV, RTA_UP); + TransportMessage *tm; + + while ((tm = rtaComponent_GetMessage(in)) != NULL) { + RtaConnection *conn = rtaConnection_GetFromTransport(tm); + RtaComponentStats *stats = rtaConnection_GetStats(conn, CODEC_TLV); + rtaComponentStats_Increment(stats, STATS_UPCALL_IN); + + if (transportMessage_IsControl(tm)) { + if (rtaComponent_PutMessage(out, tm)) { + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + } + } else { + upcallDictionary(tm, out, stats); + } + + if (DEBUG_OUTPUT) { + struct timeval delay = transportMessage_GetDelay(tm); + printf("%9" PRIu64 " %s total upcall reads in %" PRIu64 " out %" PRIu64 " last delay %.6f\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaComponentStats_Get(stats, STATS_UPCALL_IN), + rtaComponentStats_Get(stats, STATS_UPCALL_OUT), + delay.tv_sec + delay.tv_usec * 1E-6); + } + } +} + + +static TransportMessage * +component_Codec_Tlv_EncodeDictionary_SchemaV1(TransportMessage *tm, RtaConnection *conn, CCNxTlvDictionary *packetDictionary) +{ + bool hasWireFormat = (ccnxTlvDictionary_IsValueIoVec(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat) || + ccnxTlvDictionary_IsValueBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat)); + + if (!hasWireFormat) { + CodecConnectionState *codec_conn_state = rtaConnection_GetPrivateData(conn, CODEC_TLV); + assertNotNull(codec_conn_state, "%s got null private data\n", __func__); + + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecSchemaV1PacketEncoder_DictionaryEncode(packetDictionary, codec_conn_state->signer); + + if (vec) { + // store a reference back into the dictioary + bool success = ccnxWireFormatMessage_PutIoVec(packetDictionary, vec); + assertTrue(success, "Failed to save wire format in the dictionary") + { + ccnxCodecNetworkBufferIoVec_Display(vec, 0); + } + + if (DEBUG_OUTPUT > 2) { + printf("%s encoded packet:\n", __func__); + ccnxCodecNetworkBufferIoVec_Display(vec, 0); + } + + ccnxCodecNetworkBufferIoVec_Release(&vec); + } else { + trapUnexpectedState("Error encoding packet") + { + ccnxTlvDictionary_Display(packetDictionary, 0); + } + } + } else { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s packetDictionary %p already has wire format\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) packetDictionary); + } + } + + if (tm && DEBUG_OUTPUT > 2) { + CCNxCodecNetworkBufferIoVec *vec = ccnxWireFormatMessage_GetIoVec(packetDictionary); + printf("%9" PRIu64 " %s packetDictionary %p wire format dump\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) packetDictionary); + ccnxCodecNetworkBufferIoVec_Display(vec, 3); + } + + return tm; +} + + +static TransportMessage * +component_Codec_Tlv_EncodeDictionary(TransportMessage *tm, RtaConnection *conn) +{ + // If the dictionary already contains a wireformat, we use that and skip encoding + CCNxTlvDictionary *packetDictionary = transportMessage_GetDictionary(tm); + + assertNotNull(packetDictionary, "Got a NULL packet dictionary for dictionary based encoding"); + if (packetDictionary) { + switch (ccnxTlvDictionary_GetSchemaVersion(packetDictionary)) { + case CCNxTlvDictionary_SchemaVersion_V1: + return component_Codec_Tlv_EncodeDictionary_SchemaV1(tm, conn, packetDictionary); + break; + + default: + trapIllegalValue(packetDictionary, "Unknown schema version: %d", ccnxTlvDictionary_GetSchemaVersion(packetDictionary)); + } + } + return NULL; +} + +/* Read from above and send to below */ +static void +component_Codec_Tlv_Downcall_Read(PARCEventQueue *in, PARCEventType event, void *ptr) +{ + RtaProtocolStack *stack = (RtaProtocolStack *) ptr; + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(stack, CODEC_TLV, RTA_DOWN); + TransportMessage *tm; + + + while ((tm = rtaComponent_GetMessage(in)) != NULL) { + RtaConnection *conn = rtaConnection_GetFromTransport(tm); + RtaComponentStats *stats = rtaConnection_GetStats(conn, CODEC_TLV); + rtaComponentStats_Increment(stats, STATS_DOWNCALL_IN); + + // this will encode everything, including control messages + TransportMessage *encoded = component_Codec_Tlv_EncodeDictionary(tm, conn); + + if (encoded) { + if (rtaComponent_PutMessage(out, encoded)) { + rtaComponentStats_Increment(stats, STATS_DOWNCALL_OUT); + } + } else { + tm = NULL; + } + + if (DEBUG_OUTPUT && tm) { + struct timeval delay = transportMessage_GetDelay(tm); + printf("%9" PRIu64 " %s total downcall reads in %" PRIu64 " out %" PRIu64 " last delay %.6f\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaComponentStats_Get(stats, STATS_DOWNCALL_IN), + rtaComponentStats_Get(stats, STATS_DOWNCALL_OUT), + delay.tv_sec + delay.tv_usec * 1E-6); + } + } +} + +static int +component_Codec_Tlv_Closer(RtaConnection *conn) +{ + struct codec_connection_state *codec_conn_state; + + codec_conn_state = rtaConnection_GetPrivateData(conn, CODEC_TLV); + assertNotNull(codec_conn_state, "%s got null private data\n", __func__); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %u codec signer %p private %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaConnection_GetConnectionId(conn), + (void *) codec_conn_state->signer, + (void *) codec_conn_state); + } + + parcSigner_Release(&codec_conn_state->signer); + + parcMemory_Deallocate((void **) &codec_conn_state); + + return 0; +} + +static int +component_Codec_Tlv_Release(RtaProtocolStack *stack) +{ + // no ProtocolStack wide state + return 0; +} + +static void +component_Codec_Tlv_StateChange(RtaConnection *conn) +{ + struct codec_connection_state *codec_conn_state; + + codec_conn_state = rtaConnection_GetPrivateData(conn, CODEC_TLV); + assertNotNull(codec_conn_state, "%s got null private data\n", __func__); + + if (DEBUG_OUTPUT) { + printf("%s connection %p codec signer %p private %p\n", + __func__, + (void *) conn, + (void *) codec_conn_state->signer, + (void *) codec_conn_state); + } +} + +// ================== diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Flowcontrol.h b/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Flowcontrol.h new file mode 100644 index 00000000..6a3bcb44 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Flowcontrol.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// component_Flowcontrol.h +// Libccnx +// +// +// + +#ifndef Libccnx_component_flow_h +#define Libccnx_component_flow_h + +// Function structs for component variations +extern RtaComponentOperations flow_vegas_ops; +extern RtaComponentOperations flow_null_ops; +#endif // Libccnx_component_flow_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Testing.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Testing.c new file mode 100644 index 00000000..51a71285 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Testing.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * the Testing component does not implement any of its methods. This means it may be inserted above and + * below another component so a unit test can look at its queues + */ + +#include <config.h> +#include <stdio.h> + +#include <LongBow/runtime.h> + +#include <ccnx/transport/transport_rta/rta_Transport.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> + +#include <ccnx/transport/transport_rta/components/component_Testing.h> + +RtaComponentOperations testing_null_ops = { + .init = NULL, /* init */ + .open = NULL, /* open */ + .upcallRead = NULL, /* upcall read */ + .upcallEvent = NULL, + .downcallRead = NULL, /* downcall read */ + .downcallEvent = NULL, + .close = NULL, /* closer */ + .release = NULL /* release */ +}; + +// ================== +// NULL diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Testing.h b/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Testing.h new file mode 100644 index 00000000..4cec0ff2 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Testing.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file component_Testing.h + * @brief A component that takes no actions, not even reads. + * + * This is useful to put in the stack around a component under test to + * isolate it from the system and then intercept the queues. + * + * This component may be used as both TESTING_UPPER and TESTING_LOWER + * to surround another component: + * + * { SYSTEM : COMPONENTS : [TESTING_UPPER, component under test, TESTING_LOWER] } + * + * In your test code, you would then have something like this to operate in + * the "down" direction: + * PARCEventQueue *upper = rtaProtocolStack_GetPutQueue(stack, TESTING_UPPER, RTA_DOWN); + * PARCEventQueue *rea = rtaProtocolStack_GetPutQueue(stack, component under test, RTA_UP); + * PARCEventQueue *lower = rtaProtocolStack_GetPutQueue(stack, TESTING_LOWER, RTA_UP); + * + */ +#ifndef Libccnx_component_Testing_h +#define Libccnx_component_Testing_h + +#include <ccnx/transport/transport_rta/core/rta_Component.h> + +extern RtaComponentOperations testing_null_ops; +#endif // Libccnx_component_Testing_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/CMakeLists.txt b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/CMakeLists.txt new file mode 100644 index 00000000..0b4416f7 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/CMakeLists.txt @@ -0,0 +1,16 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_codec_Signing + test_component_Codec_Tlv + test_component_Codec_Tlv_Hmac + test_component_Testing +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_codec_Signing.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_codec_Signing.c new file mode 100644 index 00000000..3740994b --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_codec_Signing.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../codec_Signing.c" + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(codec_Signing) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(codec_Signing) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(codec_Signing) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(codec_Signing); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Codec_Tlv.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Codec_Tlv.c new file mode 100644 index 00000000..1990d8ae --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Codec_Tlv.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. + +#include "../component_Codec_Tlv.c" +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> +#include <ccnx/api/control/cpi_ControlFacade.h> + +#include <parc/security/parc_Pkcs12KeyStore.h> +#include <parc/security/parc_Security.h> +#include <ccnx/transport/transport_rta/config/config_All.h> + +#include <ccnx/transport/test_tools/traffic_tools.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_nameA.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route_crc32c.h> + + +#include <ccnx/common/ccnx_WireFormatMessage.h> + +#include "testrig_MockFramework.c" + +typedef struct test_data { + MockFramework *mock; + char keystore_filename[MAXPATH]; + char keystore_password[MAXPATH]; +} TestData; + +static CCNxTransportConfig * +codecTlv_CreateParams(const char *keystore_filename, const char *keystore_password) +{ + assertNotNull(keystore_filename, "Got null keystore name\n"); + assertNotNull(keystore_password, "Got null keystore passwd\n"); + + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + + apiConnector_ProtocolStackConfig(stackConfig); + testingUpper_ProtocolStackConfig(stackConfig); + tlvCodec_ProtocolStackConfig(stackConfig); + testingLower_ProtocolStackConfig(stackConfig); + protocolStack_ComponentsConfigArgs(stackConfig, apiConnector_GetName(), testingUpper_GetName(), tlvCodec_GetName(), testingLower_GetName(), NULL); + + CCNxConnectionConfig *connConfig = apiConnector_ConnectionConfig(ccnxConnectionConfig_Create()); + testingUpper_ConnectionConfig(connConfig); + tlvCodec_ConnectionConfig(connConfig); + testingLower_ConnectionConfig(connConfig); + + unlink(keystore_filename); + + bool success = parcPkcs12KeyStore_CreateFile(keystore_filename, keystore_password, "alice", 1024, 30); + assertTrue(success, "parcPkcs12KeyStore_CreateFile() failed."); + + publicKeySigner_ConnectionConfig(connConfig, keystore_filename, keystore_password); + + CCNxTransportConfig *result = ccnxTransportConfig_Create(stackConfig, connConfig); + ccnxStackConfig_Release(&stackConfig); + return result; +} + +static TestData * +_commonSetup(void) +{ + parcSecurity_Init(); + + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + sprintf(data->keystore_filename, "/tmp/alice_keystore.p12.XXXXXX"); + mktemp(data->keystore_filename); + sprintf(data->keystore_password, "12345"); + + CCNxTransportConfig *config = codecTlv_CreateParams(data->keystore_filename, data->keystore_password); + data->mock = mockFramework_Create(config); + ccnxTransportConfig_Destroy(&config); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + mockFramework_Destroy(&data->mock); + unlink(data->keystore_filename); + parcMemory_Deallocate((void **) &data); + + parcSecurity_Fini(); +} + +LONGBOW_TEST_RUNNER(component_Codec_Tlv) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Dictionary); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(component_Codec_Tlv) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(component_Codec_Tlv) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ================================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ================================================================================== + +static TransportMessage * +sendDown(TestData *data, TransportMessage *tm_going_down) +{ + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + rtaComponent_PutMessage(in, tm_going_down); + // turn the handle enough times, the message will pass all the way out the bottom + rtaFramework_NonThreadedStepCount(data->mock->framework, 10); + return rtaComponent_GetMessage(out); +} + +static TransportMessage * +sendUp(TestData *data, TransportMessage *tm_going_down) +{ + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + rtaComponent_PutMessage(in, tm_going_down); + // turn the handle enough times, the message will pass all the way out the bottom + rtaFramework_NonThreadedStepCount(data->mock->framework, 10); + return rtaComponent_GetMessage(out); +} + + +// ================================================================================== + +LONGBOW_TEST_FIXTURE(Dictionary) +{ + LONGBOW_RUN_TEST_CASE(Dictionary, component_Codec_Tlv_Downcall_Read_Interest); + LONGBOW_RUN_TEST_CASE(Dictionary, component_Codec_Tlv_Downcall_Read_Control); + LONGBOW_RUN_TEST_CASE(Dictionary, component_Codec_Tlv_Downcall_Read_Raw); + + LONGBOW_RUN_TEST_CASE(Dictionary, component_Codec_Tlv_Upcall_Read_Control); +} + +LONGBOW_TEST_FIXTURE_SETUP(Dictionary) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Dictionary) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * Makes sure an interest going down the stack gets encoded. Does not test + * the actual wire format -- that's the job of the tlv unit tests. + */ +LONGBOW_TEST_CASE(Dictionary, component_Codec_Tlv_Downcall_Read_Interest) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + TransportMessage *tm = trafficTools_CreateTransportMessageWithDictionaryInterest(data->mock->connection, CCNxTlvDictionary_SchemaVersion_V1); + TransportMessage *test_tm = sendDown(data, tm); + CCNxCodecNetworkBufferIoVec *vec = + ccnxTlvDictionary_GetIoVec(transportMessage_GetDictionary(test_tm), CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_WireFormat); + assertNotNull(vec, "Output of coded did not have a raw format message"); + transportMessage_Destroy(&test_tm); +} + +/** + * control message should be passed through + */ +LONGBOW_TEST_CASE(Dictionary, component_Codec_Tlv_Downcall_Read_Control) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + TransportMessage *tm = trafficTools_CreateTransportMessageWithDictionaryControl(data->mock->connection, + CCNxTlvDictionary_SchemaVersion_V1); + TransportMessage *test_tm = sendDown(data, tm); + PARCJSON *json = ccnxControlFacade_GetJson(transportMessage_GetDictionary(test_tm)); + assertNotNull(json, "Output of codec did not have a control message"); + transportMessage_Destroy(&test_tm); +} + +LONGBOW_TEST_CASE(Dictionary, component_Codec_Tlv_Downcall_Read_Raw) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + TransportMessage *tm = trafficTools_CreateTransportMessageWithDictionaryRaw(data->mock->connection, + CCNxTlvDictionary_SchemaVersion_V1); + TransportMessage *test_tm = sendDown(data, tm); + PARCBuffer *buffer = ccnxWireFormatMessage_GetWireFormatBuffer(transportMessage_GetDictionary(test_tm)); + assertNotNull(buffer, "Output of codec did not have a raw format message"); + transportMessage_Destroy(&test_tm); +} + +LONGBOW_TEST_CASE(Dictionary, component_Codec_Tlv_Upcall_Read_Control) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCBuffer *wireFormat = parcBuffer_Wrap(v1_cpi_add_route_crc32c, sizeof(v1_cpi_add_route_crc32c), + 0, sizeof(v1_cpi_add_route_crc32c)); + CCNxTlvDictionary *dictionary = ccnxWireFormatMessage_FromControlPacketType(CCNxTlvDictionary_SchemaVersion_V1, wireFormat); + parcBuffer_Release(&wireFormat); + + // We have not set the message type or schema + TransportMessage *tm = transportMessage_CreateFromDictionary(dictionary); + transportMessage_SetInfo(tm, data->mock->connection, NULL); + ccnxTlvDictionary_Release(&dictionary); + + // ------ + // Now do the actual test of sending the transport message up the stack + + TransportMessage *test_tm = sendUp(data, tm); + + // It should now be parsed into an control message + CCNxTlvDictionary *testdict = transportMessage_GetDictionary(test_tm); + assertNotNull(testdict, "Failed to get dictionary from the transport message"); + + assertTrue(ccnxTlvDictionary_IsControl(testdict), "Dictionary says it is not a Control"); + assertTrue(ccnxTlvDictionary_GetSchemaVersion(testdict) == CCNxTlvDictionary_SchemaVersion_V1, + "Wrong schema, got %d expected %d", + ccnxTlvDictionary_GetSchemaVersion(testdict), CCNxTlvDictionary_SchemaVersion_V1); + + transportMessage_Destroy(&tm); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(component_Codec_Tlv); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Codec_Tlv_Hmac.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Codec_Tlv_Hmac.c new file mode 100644 index 00000000..4934a266 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Codec_Tlv_Hmac.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + */ + +#include "../component_Codec_Tlv.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <stdio.h> +#include <sys/un.h> +#include <strings.h> + +#include <LongBow/unit-test.h> +#include <LongBow/runtime.h> + +#define DEBUG_OUTPUT 0 + +#include <parc/security/parc_Security.h> +#include <parc/security/parc_SymmetricKeyStore.h> + +#include <ccnx/transport/transport_rta/config/config_All.h> +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> + +#include <ccnx/transport/test_tools/traffic_tools.h> + +#include "testrig_MockFramework.c" + +#ifndef MAXPATH +#define MAXPATH 1024 +#endif + +typedef struct test_data { + MockFramework *mock; + char keystore_filename[MAXPATH]; + char keystore_password[MAXPATH]; +} TestData; + +LONGBOW_TEST_RUNNER(System) +{ + LONGBOW_RUN_TEST_FIXTURE(Component_Codec_Tlv_Hmac); +} + +LONGBOW_TEST_RUNNER_SETUP(System) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(System) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ================================ + +static CCNxTransportConfig * +codecTlv_CreateParams(const char *keystore_name, const char *keystore_passwd) +{ + assertNotNull(keystore_name, "Got null keystore name\n"); + assertNotNull(keystore_passwd, "Got null keystore passwd\n"); + + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + + apiConnector_ProtocolStackConfig(stackConfig); + testingUpper_ProtocolStackConfig(stackConfig); + tlvCodec_ProtocolStackConfig(stackConfig); + testingLower_ProtocolStackConfig(stackConfig); + protocolStack_ComponentsConfigArgs(stackConfig, + apiConnector_GetName(), + testingUpper_GetName(), + tlvCodec_GetName(), + testingLower_GetName(), + NULL); + + CCNxConnectionConfig *connConfig = apiConnector_ConnectionConfig(ccnxConnectionConfig_Create()); + testingUpper_ConnectionConfig(connConfig); + tlvCodec_ConnectionConfig(connConfig); + testingLower_ConnectionConfig(connConfig); + + symmetricKeySigner_ConnectionConfig(connConfig, keystore_name, keystore_passwd); + CCNxTransportConfig *result = ccnxTransportConfig_Create(stackConfig, connConfig); + ccnxStackConfig_Release(&stackConfig); + return result; +} + +static TestData * +_commonSetup(void) +{ + parcSecurity_Init(); + + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + sprintf(data->keystore_filename, "/tmp/alice_keystore.p12.XXXXXX"); + mktemp(data->keystore_filename); + sprintf(data->keystore_password, "12345"); + + unlink(data->keystore_filename); + PARCBuffer *secret_key = parcSymmetricKeyStore_CreateKey(256); + parcSymmetricKeyStore_CreateFile(data->keystore_filename, data->keystore_password, secret_key); + parcBuffer_Release(&secret_key); + + CCNxTransportConfig *config = codecTlv_CreateParams(data->keystore_filename, data->keystore_password); + data->mock = mockFramework_Create(config); + ccnxTransportConfig_Destroy(&config); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + mockFramework_Destroy(&data->mock); + unlink(data->keystore_filename); + parcMemory_Deallocate((void **) &data); + + parcSecurity_Fini(); +} + +// ====================================== + + +LONGBOW_TEST_FIXTURE(Component_Codec_Tlv_Hmac) +{ + LONGBOW_RUN_TEST_CASE(Component_Codec_Tlv_Hmac, open_close); + LONGBOW_RUN_TEST_CASE(Component_Codec_Tlv_Hmac, Component_Codec_Tlv_Hmac_Downcall_Read); +} + +LONGBOW_TEST_FIXTURE_SETUP(Component_Codec_Tlv_Hmac) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Component_Codec_Tlv_Hmac) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Component_Codec_Tlv_Hmac, open_close) +{ + // dont actually do anything. make sure no memory leaks in setup and teardown. +} + +// ============================================ +// TLV tests + +static TransportMessage * +sendDown(TestData *data, TransportMessage *tm_going_down) +{ + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_UPPER, RTA_DOWN); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(data->mock->stack, TESTING_LOWER, RTA_UP); + + rtaComponent_PutMessage(in, tm_going_down); + // turn the handle enough times, the message will pass all the way out the bottom + rtaFramework_NonThreadedStepCount(data->mock->framework, 5); + return rtaComponent_GetMessage(out); +} + +LONGBOW_TEST_CASE(Component_Codec_Tlv_Hmac, Component_Codec_Tlv_Hmac_Downcall_Read) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + TransportMessage *tm = trafficTools_CreateTransportMessageWithSignedContentObject(data->mock->connection); + + TransportMessage *test_tm = sendDown(data, tm); + + // we should now have a RawFormat message + CCNxCodecNetworkBufferIoVec *vec = ccnxWireFormatMessage_GetIoVec(transportMessage_GetDictionary(test_tm)); + assertNotNull(vec, "Output of coded message did not have a raw format message") + { + ccnxTlvDictionary_Display(transportMessage_GetDictionary(test_tm), 0); + } + + transportMessage_Destroy(&test_tm); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(System); + exit(longBowMain(argc, argv, testRunner, NULL)); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Testing.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Testing.c new file mode 100644 index 00000000..6593972f --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Testing.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../component_Testing.c" + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(component_Testing) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(component_Testing) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(component_Testing) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(component_Testing); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/testrig_MockFramework.c b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/testrig_MockFramework.c new file mode 100644 index 00000000..0ca07ab1 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/components/test/testrig_MockFramework.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This test rig sets up a mock RTA Framework for testing Components and Connectors. + * + * + */ + +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_Commands.c> +#include <ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.h> + +#ifndef MAXPATH +#define MAXPATH 1024 +#endif + +typedef struct mock_framework { + PARCRingBuffer1x1*commandRingBuffer; + PARCNotifier *commandNotifier; + RtaFramework *framework; + + int stackId; + RtaProtocolStack *stack; + + int connection_fds[2]; + RtaConnection *connection; + + CCNxTransportConfig *transport_config; +} MockFramework; + +static MockFramework * +mockFramework_Create(CCNxTransportConfig *config) +{ + MockFramework *mock = parcMemory_AllocateAndClear(sizeof(MockFramework)); + assertNotNull(mock, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MockFramework)); + + mock->transport_config = ccnxTransportConfig_Copy(config); + assertNotNull(mock->transport_config, "%s got null params from createParams\n", __func__); + + mock->commandRingBuffer = parcRingBuffer1x1_Create(128, NULL); + mock->commandNotifier = parcNotifier_Create(); + mock->framework = rtaFramework_Create(mock->commandRingBuffer, mock->commandNotifier); + + // Create the protocol stack + + mock->stackId = 1; + RtaCommandCreateProtocolStack *createStack = + rtaCommandCreateProtocolStack_Create(mock->stackId, ccnxTransportConfig_GetStackConfig(mock->transport_config)); + _rtaFramework_ExecuteCreateStack(mock->framework, createStack); + rtaCommandCreateProtocolStack_Release(&createStack); + + // peek inside and get the protocol stack reference + FrameworkProtocolHolder *fph = rtaFramework_GetProtocolStackByStackId(mock->framework, mock->stackId); + mock->stack = fph->stack; + + int error = socketpair(AF_UNIX, SOCK_STREAM, 0, mock->connection_fds); + assertFalse(error, "Error creating socket pair: (%d) %s", errno, strerror(errno)); + + RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(mock->stackId, mock->connection_fds[0], mock->connection_fds[1], + ccnxConnectionConfig_GetJson(ccnxTransportConfig_GetConnectionConfig(mock->transport_config))); + _rtaFramework_ExecuteOpenConnection(mock->framework, openConnection); + rtaCommandOpenConnection_Release(&openConnection); + + mock->connection = rtaConnectionTable_GetByApiFd(mock->framework->connectionTable, mock->connection_fds[0]); + + // Uses the non-threaded forwarder, make sure we step at least once + rtaFramework_NonThreadedStep(mock->framework); + + return mock; +} + +static void +mockFramework_Destroy(MockFramework **mockPtr) +{ + MockFramework *mock = *mockPtr; + + rtaFramework_Teardown(mock->framework); + + parcRingBuffer1x1_Release(&mock->commandRingBuffer); + parcNotifier_Release(&mock->commandNotifier); + + rtaFramework_Destroy(&mock->framework); + ccnxTransportConfig_Destroy(&mock->transport_config); + + parcMemory_Deallocate((void **) &mock); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_All.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_All.h new file mode 100644 index 00000000..54d068fe --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_All.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * This is a convenience header for an API that wishes to import all the standard + * component configuration headers. + */ + +#ifndef Libccnx_config_all_h +#define Libccnx_config_all_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> + +#include <ccnx/transport/transport_rta/config/config_ApiConnector.h> + +#include <ccnx/transport/transport_rta/config/config_Codec_Tlv.h> +#include <ccnx/transport/transport_rta/config/config_CryptoCache.h> + +#include <ccnx/transport/transport_rta/config/config_FlowControl_Vegas.h> +#include <ccnx/transport/transport_rta/config/config_Forwarder_Local.h> +#include <ccnx/transport/transport_rta/config/config_Forwarder_Metis.h> + +#include <ccnx/transport/transport_rta/config/config_InMemoryVerifier.h> + +#include <ccnx/transport/transport_rta/config/config_ProtocolStack.h> +#include <ccnx/transport/transport_rta/config/config_PublicKeySigner.h> + +#include <ccnx/transport/transport_rta/config/config_Signer.h> +#include <ccnx/transport/transport_rta/config/config_SymmetricKeySigner.h> + +#include <ccnx/transport/transport_rta/config/config_TestingComponent.h> +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ApiConnector.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ApiConnector.c new file mode 100644 index 00000000..6af665bb --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ApiConnector.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> + +#include "config_ApiConnector.h" + +#include <ccnx/transport/transport_rta/core/components.h> + +/** + * Generates: + * + * { "API_CONNECTOR" : { } } + */ +CCNxStackConfig * +apiConnector_ProtocolStackConfig(CCNxStackConfig *stackConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxStackConfig *result = ccnxStackConfig_Add(stackConfig, apiConnector_GetName(), value); + parcJSONValue_Release(&value); + + return result; +} + +CCNxConnectionConfig * +apiConnector_ConnectionConfig(CCNxConnectionConfig *connectionConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxConnectionConfig *result = ccnxConnectionConfig_Add(connectionConfig, apiConnector_GetName(), value); + parcJSONValue_Release(&value); + + return result; +} + +const char * +apiConnector_GetName(void) +{ + return RtaComponentNames[API_CONNECTOR]; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ApiConnector.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ApiConnector.h new file mode 100644 index 00000000..7e0867ad --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ApiConnector.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_ApiConnector.h + * @brief Generates stack and connection configuration information + * + * Each component in the protocol stack must have a configuration element. + * This module generates the configuration elements for the API Connector. + * + * @code + * { + * // Configure a stack with {APIConnector,TLVCodec,MetisConnector} + * + * CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + * CCNxConnectionConfig *connConfig = ccnxConnectionConfig_Create(); + * + * apiConnector_ProtocolStackConfig(stackConfig); + * apiConnector_ConnectionConfig(connConfig); + * tlvCodec_ProtocolStackConfig(stackConfig); + * tlvCodec_ConnectionConfig(connConfig); + * metisForwarder_ProtocolStackConfig(stackConfig); + * metisForwarder_ConnectionConfig(connConfig, metisForwarder_GetDefaultPort()); + * + * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connConfig); + * } + * + */ +#ifndef Libccnx_config_ApiConnector_h +#define Libccnx_config_ApiConnector_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> + +/** + * Generates the configuration settings included in the Protocol Stack configuration + * + * Adds configuration elements to the Protocol Stack configuration + * { "API_CONNECTOR" : { } } + * + * @param [in] stackConfig The protocl stack configuration to update + * + * @return non-null The updated protocol stack configuration + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxStackConfig *apiConnector_ProtocolStackConfig(CCNxStackConfig *stackConfig); + +/** + * Generates the configuration settings included in the Connection configuration + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * @param [in] config A pointer to a valid CCNxConnectionConfig instance. + * + * @return non-null The modified `CCNxConnectionConfig` + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxConnectionConfig *apiConnector_ConnectionConfig(CCNxConnectionConfig *config); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *apiConnector_GetName(void); +#endif + diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Ccnb.xc b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Ccnb.xc new file mode 100644 index 00000000..04820d6e --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Ccnb.xc @@ -0,0 +1,31 @@ + +#include <config.h> +#include <stdio.h> +#include "config_Codec_Ccnb.h" + +#include "components.h" + +/** + * Generates: + + { "CCNB_CODEC" : { } } + */ +ProtocolStackConfig * +ccnbCodec_ProtocolStackConfig(ProtocolStackConfig *stackConfig) +{ + return protocolStackConfig_Add(stackConfig, ccnbCodec_GetName(), parcJSONValue_CreateNULL());//ccnxJson_CreateObject()); +} + +ConnectionConfig * +ccnbCodec_ConnectionConfig(ConnectionConfig *connectionConfig) +{ + return connectionConfig_Add(connectionConfig, ccnbCodec_GetName(), parcJSONValue_CreateNULL());//ccnxJson_CreateObject()); +} + + +const char * ccnbCodec_GetName() +{ + return RtaComponentNames[CODEC_CCNB]; + +} + diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Tlv.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Tlv.c new file mode 100644 index 00000000..b3eeb6a7 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Tlv.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <config.h> +#include <stdio.h> +#include "config_Codec_Tlv.h" +#include <LongBow/runtime.h> + +#include <ccnx/transport/transport_rta/core/components.h> + +//static const char param_SCHEMA[] = "SCHEMA"; +//static const char param_CODEC[] = "CODEC"; +//static const int default_schema = 0; + +/** + * Generates: + * + * { "CODEC_TLV" : { } } + */ +CCNxStackConfig * +tlvCodec_ProtocolStackConfig(CCNxStackConfig *stackConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxStackConfig *result = ccnxStackConfig_Add(stackConfig, tlvCodec_GetName(), value); + parcJSONValue_Release(&value); + + return result; +} + +/** + * Generates: + * + * { "CODEC_TLV" : { } } + */ + +CCNxConnectionConfig * +tlvCodec_ConnectionConfig(CCNxConnectionConfig *connectionConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxConnectionConfig *result = ccnxConnectionConfig_Add(connectionConfig, tlvCodec_GetName(), value); + parcJSONValue_Release(&value); + return result; +} + +const char * +tlvCodec_GetName(void) +{ + return RtaComponentNames[CODEC_TLV]; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Tlv.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Tlv.h new file mode 100644 index 00000000..213fc1c2 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Tlv.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_Codec_Tlv.h + * @brief Generates stack and connection configuration information + * + * Each component in the protocol stack must have a configuration element. + * This module generates the configuration elements for the TLV codec. + * + * @code + * { + * // Configure a stack with {APIConnector,TLVCodec,MetisConnector} + * + * stackConfig = ccnxStackConfig_Create(); + * connConfig = ccnxConnectionConfig_Create(); + * + * apiConnector_ProtocolStackConfig(stackConfig); + * apiConnector_ConnectionConfig(connConfig); + * tlvCodec_ProtocolStackConfig(stackConfig); + * tlvCodec_ConnectionConfig(connConfig); + * metisForwarder_ProtocolStackConfig(stackConfig); + * metisForwarder_ConnectionConfig(connConfig, metisForwarder_GetDefaultPort()); + * + * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connConfig); + * } + * + */ + +#ifndef Libccnx_config_Codec_Tlv_h +#define Libccnx_config_Codec_Tlv_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +/** + * Generates the configuration settings included in the Protocol Stack configuration + * + * Adds configuration elements to the Protocol Stack configuration + * + * { "TLV_CODEC" : { } } + * + * @param [in] stackConfig The protocl stack configuration to update + * + * @return non-null The updated protocol stack configuration + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxStackConfig *tlvCodec_ProtocolStackConfig(CCNxStackConfig *stackConfig); + +/** + * Creates a connection configuration based on CCNxMessages wrapping an CCNxTlvDictionary + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * @param [in] config A pointer to a valid CCNxConnectionConfig instance. + * + * @return non-null The modified `CCNxConnectionConfig` + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxConnectionConfig *tlvCodec_ConnectionConfig(CCNxConnectionConfig *config); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *tlvCodec_GetName(void); + +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_CryptoCache.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_CryptoCache.h new file mode 100644 index 00000000..f707c324 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_CryptoCache.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef Libccnx_config_CryptoCache_h +#define Libccnx_config_CryptoCache_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> + +/** + * Generates: + * + * { "KEYS" : + * [ + * { "KEYID" : base64{ keyhash }, + * "KEY" : base64{ derEncodedKey }, + * "PREFIXES" : [ uripath, uripath, ... uripath ] + * }, + * { "KEYID" : base64{ keyhash }, + * "KEY" : base64{ derEncodedKey }, + * "PREFIXES" : [ uripath, uripath, ... uripath ] + * }, + * ... + * ] + * } + */ +CCNxConnectionConfig *cryptoCache_ConnectionConfig(CCNxConnectionConfig *connConfig, const char *filename, const char *password); + +const char *cryptoCache_GetName(void); +#endif // Libccnx_config_CryptoCache_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_FlowControl_Vegas.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_FlowControl_Vegas.c new file mode 100644 index 00000000..9b31d26e --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_FlowControl_Vegas.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <config.h> +#include <stdio.h> +#include "config_FlowControl_Vegas.h" + +#include <ccnx/transport/transport_rta/core/components.h> + +/** + * Generates: + * + * { "FC_VEGAS" : { } } + */ +CCNxStackConfig * +vegasFlowController_ProtocolStackConfig(CCNxStackConfig *stackConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxStackConfig *result = ccnxStackConfig_Add(stackConfig, vegasFlowController_GetName(), value); + parcJSONValue_Release(&value); + return result; +} + +CCNxConnectionConfig * +vegasFlowController_ConnectionConfig(CCNxConnectionConfig *connectionConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxConnectionConfig *result = ccnxConnectionConfig_Add(connectionConfig, vegasFlowController_GetName(), value); + parcJSONValue_Release(&value); + return result; +} + +const char * +vegasFlowController_GetName(void) +{ + return RtaComponentNames[FC_VEGAS]; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_FlowControl_Vegas.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_FlowControl_Vegas.h new file mode 100644 index 00000000..28bcd992 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_FlowControl_Vegas.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_FlowControl_Vegas.h + * @brief Generates stack and connection configuration information + * + * Each component in the protocol stack must have a configuration element. + * This module generates the configuration elements for the API Connector. + * + * @code + * { + * // Configure a stack with {APIConnector,Vegas,TLVCodec,MetisConnector} + * + * stackConfig = ccnxStackConfig_Create(); + * connConfig = ccnxConnectionConfig_Create(); + * + * apiConnector_ProtocolStackConfig(stackConfig); + * apiConnector_ConnectionConfig(connConfig); + * vegasFlowController_ProtocolStackConfig(stackConfig); + * vegasFlowController_ConnectionConfig(connConfig); + * tlvCodec_ProtocolStackConfig(stackConfig); + * tlvCodec_ConnectionConfig(connConfig); + * metisForwarder_ProtocolStackConfig(stackConfig); + * metisForwarder_ConnectionConfig(connConfig, metisForwarder_GetDefaultPort()); + * + * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connConfig); + * } + * + */ + +#ifndef Libccnx_config_FlowControl_Vegas_h +#define Libccnx_config_FlowControl_Vegas_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> + +/** + * Generates the configuration settings included in the Protocol Stack configuration + * + * Adds configuration elements to the Protocol Stack configuration + * + * { "FC_VEGAS" : { } } + * + * @param [in] stackConfig The protocl stack configuration to update + * + * @return non-null The updated protocol stack configuration + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxStackConfig *vegasFlowController_ProtocolStackConfig(CCNxStackConfig *stackConfig); + +/** + * Generates the configuration settings included in the Connection configuration + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * @param [in] config The CCNxConnectionConfig instance + * + * @return non-null The modified `CCNxConnectionConfig` + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxConnectionConfig *vegasFlowController_ConnectionConfig(CCNxConnectionConfig *config); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *vegasFlowController_GetName(void); +#endif // Libccnx_config_FlowControl_Vegas_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Local.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Local.c new file mode 100644 index 00000000..d5c94854 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Local.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> +#include "config_Forwarder_Local.h" + +#include <ccnx/transport/transport_rta/core/components.h> +#include <LongBow/runtime.h> + +static const char param_FWD_LOCAL_NAME[] = "LOCAL_NAME"; + +CCNxStackConfig * +localForwarder_ProtocolStackConfig(CCNxStackConfig *stackConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxStackConfig *result = ccnxStackConfig_Add(stackConfig, localForwarder_GetName(), value); + parcJSONValue_Release(&value); + return result; +} + +/** + * Generates: + * + * { "FWD_LOCAL" : { "path" : pipePath } } + */ +CCNxConnectionConfig * +localForwarder_ConnectionConfig(CCNxConnectionConfig *connConfig, const char *pipePath) +{ + PARCJSON *json = parcJSON_Create(); + parcJSON_AddString(json, param_FWD_LOCAL_NAME, pipePath); + PARCJSONValue *value = parcJSONValue_CreateFromJSON(json); + parcJSON_Release(&json); + CCNxConnectionConfig *result = ccnxConnectionConfig_Add(connConfig, localForwarder_GetName(), value); + parcJSONValue_Release(&value); + return result; +} + +const char * +localForwarder_GetName() +{ + return RtaComponentNames[FWD_LOCAL]; +} + +const char * +localForwarder_GetPath(PARCJSON *json) +{ + PARCJSONValue *value = parcJSON_GetValueByName(json, localForwarder_GetName()); + assertNotNull(value, "Got null for %s json", localForwarder_GetName()); + PARCJSON *localJson = parcJSONValue_GetJSON(value); + + value = parcJSON_GetValueByName(localJson, param_FWD_LOCAL_NAME); + assertNotNull(value, "Must specify a path for the PF_UNIX pipe for local forwarder"); + assertTrue(parcJSONValue_IsString(value), "JSON key %s must be type STRING", localForwarder_GetName()); + + PARCBuffer *sBuf = parcJSONValue_GetString(value); + const char *path = parcBuffer_Overlay(sBuf, 0); + + return path; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Local.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Local.h new file mode 100644 index 00000000..6b865aa8 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Local.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_Forwarder_Local.h + * @brief Generates stack and connection configuration information + * + * Each component in the protocol stack must have a configuration element. + * This module generates the configuration elements for the local testing forwarder. + * + * The local forwarder requires one parameter for the path to the unix socket. + * + * @code + * { + * // Configure a stack with {APIConnector,TLVCodec,LocalForwarder} + * + * stackConfig = ccnxStackConfig_Create(); + * connConfig = ccnxConnectionConfig_Create(); + * + * apiConnector_ProtocolStackConfig(stackConfig); + * apiConnector_ConnectionConfig(connConfig); + * tlvCodec_ProtocolStackConfig(stackConfig); + * tlvCodec_ConnectionConfig(connConfig); + * localForwarder_ProtocolStackConfig(stackConfig); + * localForwarder_ConnectionConfig(connConfig, "/var/run/bentpipe.sock"); + * + * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connConfig); + * } + * + */ + +#ifndef Libccnx_config_Forwarder_Local_h +#define Libccnx_config_Forwarder_Local_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> + +/** + * Generates the configuration settings included in the Protocol Stack configuration + * + * Adds configuration elements to the Protocol Stack configuration + * + * { "FWD_LOCAL" : { } } + * + * @param [in] stackConfig The protocl stack configuration to update + * + * @return non-null The updated protocol stack configuration + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxStackConfig *localForwarder_ProtocolStackConfig(CCNxStackConfig *stackConfig); + +/** + * Generates: + * + */ + +/** + * Generates the configuration settings included in the Connection configuration + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * { "FWD_LOCAL" : { "path" : pipePath } } + * + * @param [in] config A pointer to a valid CCNxConnectionConfig instance. + * @param [in] pipePath The filesystem path to the unix domain socket + * + * @return non-null The modified `CCNxConnectionConfig` + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxConnectionConfig *localForwarder_ConnectionConfig(CCNxConnectionConfig *config, const char *pipePath); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *localForwarder_GetName(void); + +const char *localForwarder_GetPath(PARCJSON *connectionJson); +#endif // Libccnx_config_Forwarder_Local_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Metis.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Metis.c new file mode 100644 index 00000000..511f738f --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Metis.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <LongBow/runtime.h> + +#include <stdio.h> +#include "config_Forwarder_Metis.h" +#include <ccnx/transport/transport_rta/core/components.h> + +static const char param_METIS_PORT[] = METIS_PORT_ENV; // integer, e.g. 9695 +static const short default_port = 9695; + +/** + * Generates: + * + * { "FWD_METIS" : { "port" : port } } + */ +CCNxStackConfig * +metisForwarder_ProtocolStackConfig(CCNxStackConfig *stackConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxStackConfig *result = ccnxStackConfig_Add(stackConfig, metisForwarder_GetName(), value); + parcJSONValue_Release(&value); + + return result; +} + +/** + * The metis forwarder port may be set per connection in the stack + * + * { "FWD_METIS" : { "port" : port } } + */ +CCNxConnectionConfig * +metisForwarder_ConnectionConfig(CCNxConnectionConfig *connConfig, uint16_t port) +{ + PARCJSON *json = parcJSON_Create(); + parcJSON_AddInteger(json, param_METIS_PORT, port); + + PARCJSONValue *value = parcJSONValue_CreateFromJSON(json); + parcJSON_Release(&json); + CCNxConnectionConfig *result = ccnxConnectionConfig_Add(connConfig, metisForwarder_GetName(), value); + parcJSONValue_Release(&value); + + return result; +} + +uint16_t +metisForwarder_GetDefaultPort() +{ + return default_port; +} + +const char * +metisForwarder_GetName() +{ + return RtaComponentNames[FWD_METIS]; +} + +uint16_t +metisForwarder_GetPortFromConfig(PARCJSON *json) +{ + PARCJSONValue *value = parcJSON_GetValueByName(json, metisForwarder_GetName()); + assertNotNull(value, "Got null for %s json", metisForwarder_GetName()); + PARCJSON *metisJson = parcJSONValue_GetJSON(value); + + value = parcJSON_GetValueByName(metisJson, param_METIS_PORT); + return (uint16_t) parcJSONValue_GetInteger(value); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Metis.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Metis.h new file mode 100644 index 00000000..78a0669a --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Metis.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_Forwarder_Metis.h + * @brief Generates stack and connection configuration information + * + * Each component in the protocol stack must have a configuration element. + * This module generates the configuration elements for the Metis connector. + * + * The Metis connector requires one parameter to specify the port. + * + * @code + * { + * // Configure a stack with {APIConnector,TLVCodec,MetisConnector} + * + * stackConfig = ccnxStackConfig_Create(); + * connConfig = ccnxConnectionConfig_Create(); + * + * apiConnector_ProtocolStackConfig(stackConfig); + * apiConnector_ConnectionConfig(connConfig); + * tlvCodec_ProtocolStackConfig(stackConfig); + * tlvCodec_ConnectionConfig(connConfig); + * metisForwarder_ProtocolStackConfig(stackConfig); + * metisForwarder_ConnectionConfig(connConfig, metisForwarder_GetDefaultPort()); + * + * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connConfig); + * } + * + */ + +#ifndef Libccnx_config_Forwarder_Metis_h +#define Libccnx_config_Forwarder_Metis_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> + +#define METIS_PORT_ENV "METIS_PORT" +#define FORWARDER_CONNECTION_ENV "CCNX_FORWARDER" + +/** + * Generates the configuration settings included in the Protocol Stack configuration + * + * Adds configuration elements to the Protocol Stack configuration + * + * { "FWD_METIS" : { } } + * + * @param [in] stackConfig The protocl stack configuration to update + * + * @return non-null The updated protocol stack configuration + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxStackConfig *metisForwarder_ProtocolStackConfig(CCNxStackConfig *stackConfig); + +/** + * Generates the configuration settings included in the Connection configuration + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * { "FWD_METIS" : { "port" : port } } + * + * @param [in] config A pointer to a valid CCNxConnectionConfig instance. + * + * @return non-null The modified `CCNxConnectionConfig` + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxConnectionConfig *metisForwarder_ConnectionConfig(CCNxConnectionConfig *config, uint16_t port); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *metisForwarder_GetName(void); + +/** + * Returns the IANA assigned port for the CCN forwarder + * + * <#Paragraphs Of Explanation#> + * + * @return 9695 The IANA assigned port for CCN + * + * Example: + * @code + * <#example#> + * @endcode + */ +uint16_t metisForwarder_GetDefaultPort(void); + +/** + * Return the metis port ot use (or the default 9695) based on the setting + * in the per-connection configuration + */ +uint16_t metisForwarder_GetPortFromConfig(PARCJSON *json); + +#endif // Libccnx_config_Forwarder_Metis_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_InMemoryVerifier.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_InMemoryVerifier.c new file mode 100644 index 00000000..8f76c184 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_InMemoryVerifier.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// config_InMemoryVerifier.c +// Libccnx +// +// + +#include <config.h> +#include <stdio.h> +#include "config_InMemoryVerifier.h" + +#include <ccnx/transport/transport_rta/core/components.h> + +#include <LongBow/runtime.h> + +static const char name[] = "InMemoryVerifier"; + +/** + * Generates: + * + * { "SIGNER" : "InMemoryVerifier", + * } + */ +CCNxConnectionConfig * +inMemoryVerifier_ConnectionConfig(CCNxConnectionConfig *connConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxConnectionConfig *result = ccnxConnectionConfig_Add(connConfig, inMemoryVerifier_GetName(), value); + parcJSONValue_Release(&value); + + return result; +} + +const char * +inMemoryVerifier_GetName(void) +{ + return name; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_InMemoryVerifier.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_InMemoryVerifier.h new file mode 100644 index 00000000..3cd5bd65 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_InMemoryVerifier.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_InMemoryVerifier.h + * @brief Generates stack and connection configuration information + * + * Each component in the protocol stack must have a configuration element. + * This module generates the configuration elements for the InMemoryVerifier. + * + * The InMemoryVerifier sits beside the Codec. The user sends ControlPlaneInformation (CPI) + * messages down to the InMemoryVerifier to configure it with keys. Only those keys specified + * as trusted will verify content objects. + * + * @code + * { + * // Configure a stack with {APIConnector,TLVCodec,MetisConnector} + * + * stackConfig = ccnxStackConfig_Create(); + * connConfig = ccnxConnectionConfig_Create(); + * + * apiConnector_ProtocolStackConfig(stackConfig); + * apiConnector_ConnectionConfig(connConfig); + * tlvCodec_ProtocolStackConfig(stackConfig); + * tlvCodec_ConnectionConfig(connConfig); + * + * inMemoryVerifier_ConnectionConfig(connConfig); + * + * metisForwarder_ProtocolStackConfig(stackConfig); + * metisForwarder_ConnectionConfig(connConfig, metisForwarder_GetDefaultPort()); + * + * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connConfig); + * } + * + */ + +#ifndef Libccnx_config_InMemoryVerifier_h +#define Libccnx_config_InMemoryVerifier_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> + +/** + * Generates the configuration settings included in the Connection configuration + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * { "VERIFIER" : "InMemoryVerifier" } + * + * @param [in] config A pointer to a valid CCNxConnectionConfig instance. + * + * @return non-null The modified `CCNxConnectionConfig` + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxConnectionConfig *inMemoryVerifier_ConnectionConfig(CCNxConnectionConfig *config); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *inMemoryVerifier_GetName(void); +#endif // Libccnx_config_InMemoryVerifier_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ProtocolStack.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ProtocolStack.c new file mode 100644 index 00000000..de52c805 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ProtocolStack.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> + +#include "config_ProtocolStack.h" + +#include <ccnx/transport/transport_rta/core/components.h> + +#include <LongBow/runtime.h> + +static const char param_STACK[] = "STACK"; +static const char param_COMPONENTS[] = "COMPONENTS"; + +/* + * Call with the names of each component, terminated by a NULL, for example: + * + * <code> + * ccnxStackConfig_AppendComponents(stackConfig, apiConnector_GetName(), vegasFlowController_GetName(), + * tlvCodec_GetName(), localForwarder_GetName(), NULL); + * </code> + * + * Generates: + * + * { "STACK" : { "COMPONENTS" : [ name1, name2, ... ] } + */ +CCNxStackConfig * +protocolStack_ComponentsConfigArgs(CCNxStackConfig *stackConfig, ...) +{ + PARCArrayList *list = parcArrayList_Create(NULL); + + va_list ap; + const char *componentName; + va_start(ap, stackConfig); + + while ((componentName = va_arg(ap, const char *)) != NULL) { + parcArrayList_Add(list, (char *) componentName); + } + + va_end(ap); + + stackConfig = protocolStack_ComponentsConfigArrayList(stackConfig, list); + parcArrayList_Destroy(&list); + + return stackConfig; +} + +/** + * Same as <code>protocolStack_ComponentsConfigArgs</code>, except uses + * an ArrayList of <code>const char *</code> component names. + */ +CCNxStackConfig * +protocolStack_ComponentsConfigArrayList(CCNxStackConfig *stackConfig, const PARCArrayList *listOfComponentNames) +{ + PARCJSON *stackJson = parcJSON_Create(); + PARCJSONArray *arrayJson = parcJSONArray_Create(); + + for (int i = 0; i < parcArrayList_Size(listOfComponentNames); i++) { + char *componentName = parcArrayList_Get(listOfComponentNames, i); + PARCJSONValue *value = parcJSONValue_CreateFromCString(componentName); + parcJSONArray_AddValue(arrayJson, value); + parcJSONValue_Release(&value); + } + + parcJSON_AddArray(stackJson, param_COMPONENTS, arrayJson); + parcJSONArray_Release(&arrayJson); + + PARCJSONValue *value = parcJSONValue_CreateFromJSON(stackJson); + parcJSON_Release(&stackJson); + + CCNxStackConfig *result = ccnxStackConfig_Add(stackConfig, param_STACK, value); + parcJSONValue_Release(&value); + return result; +} + +const char * +protocolStack_GetName(void) +{ + return param_STACK; +} + +/** + * Parse the protocol stack json to extract an array list of the component names + */ +PARCArrayList * +protocolStack_GetComponentNameArray(PARCJSON *protocolStackJson) +{ + // created with NULL destroyer because we're putting in const char * + PARCArrayList *arraylist = parcArrayList_Create_Capacity(NULL, NULL, 16); + + PARCJSONValue *value = parcJSON_GetValueByName(protocolStackJson, param_STACK); + assertNotNull(value, "Cannot have null %s key in json", param_STACK); + PARCJSON *stackJson = parcJSONValue_GetJSON(value); + + value = parcJSON_GetValueByName(stackJson, param_COMPONENTS); + assertNotNull(value, "Cannot have null %s key in json", param_COMPONENTS); + assertTrue(parcJSONValue_IsArray(value), "key %s not type ARRAY", param_COMPONENTS); + PARCJSONArray *componentsJson = parcJSONValue_GetArray(value); + + size_t length = parcJSONArray_GetLength(componentsJson); + + for (size_t i = 0; i < length; i++) { + value = parcJSONArray_GetValue(componentsJson, i); + PARCBuffer *sBuf = parcJSONValue_GetString(value); + parcArrayList_Add(arraylist, parcBuffer_Overlay(sBuf, 0)); + } + return arraylist; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ProtocolStack.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ProtocolStack.h new file mode 100644 index 00000000..8248f4a3 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ProtocolStack.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_ProtocolStack.h + * @brief Generates stack and connection configuration information + * + * Each component in the protocol stack must have a configuration element. + * This module generates the configuration elements for the ProtocolStack. + * + * The ProtocolStack configuration is a list of key names for the components + * in the stack. It is an in-order list of the components to configure in the + * stack. + * + * @code + * { + * // Configure a stack with {APIConnector,TLVCodec,MetisConnector} + * + * stackConfig = ccnxStackConfig_Create(); + * connConfig = ccnxConnectionConfig_Create(); + * + * apiConnector_ProtocolStackConfig(stackConfig); + * apiConnector_ConnectionConfig(connConfig); + * tlvCodec_ProtocolStackConfig(stackConfig); + * tlvCodec_ConnectionConfig(connConfig); + * inMemoryVerifier_ConnectionConfig(connConfig); + * metisForwarder_ProtocolStackConfig(stackConfig); + * metisForwarder_ConnectionConfig(connConfig, metisForwarder_GetDefaultPort()); + * + * protocolStack_ComponentsConfigArgs(stackConfig, apiConnector_Name(), tlvCodec_Name(), metisForwarder_Name(), NULL); + * + * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connConfig); + * } + * + */ + +#ifndef Libccnx_config_ProtocolStack_h +#define Libccnx_config_ProtocolStack_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> +#include <parc/algol/parc_ArrayList.h> + +/** + * Generates the configuration settings included in the Protocol Stack configuration + * + * Adds configuration elements to the Protocol Stack configuration + * + * { "COMPONENTS" : [ name1, name2, ... ] } + * + * The ProtocolStack function adds a configuration element that enumerates each component + * that will be in the protocol stack, in order. These names must match the names + * used by each component in its own particular configuration. + * + * @param [in] stackConfig The protocl stack configuration to update + * + * @return non-null The updated protocol stack configuration + * + * Example: + * @code + * { + * protocolStack_ComponentsConfigArgs(stackConfig, apiConnector_Name(), tlvCodec_Name(), metisForwarder_Name(), NULL); + * } + * @endcode + */ +CCNxStackConfig *protocolStack_ComponentsConfigArgs(CCNxStackConfig *stackConfig, ...); + +/** + * Generates the configuration settings included in the Protocol Stack configuration + * + * Adds configuration elements to the Protocol Stack configuration + * + * { "COMPONENTS" : [ name1, name2, ... ] } + * + * The ProtocolStack function adds a configuration element that enumerates each component + * that will be in the protocol stack, in order. These names must match the names + * used by each component in its own particular configuration. + * + * @param [in] stackConfig The protocl stack configuration to update + * + * @return non-null The updated protocol stack configuration + * + * Example: + * @code + * @endcode + */ +CCNxStackConfig *protocolStack_ComponentsConfigArrayList(CCNxStackConfig *stackConfig, const PARCArrayList *listOfComponentNames); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *protocolStack_GetName(void); + +/** + * Parse the protocol stack json to extract an array list of the component names + */ +PARCArrayList *protocolStack_GetComponentNameArray(PARCJSON *stackJson); +#endif // Libccnx_config_ProtocolStack_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_PublicKeySigner.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_PublicKeySigner.c new file mode 100644 index 00000000..3114e7b6 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_PublicKeySigner.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// config_PublicKeySigner.c +// Libccnx +// +// +// +#include <config.h> +#include <LongBow/runtime.h> + +#include <stdio.h> + +#include <parc/security/parc_Identity.h> +#include "config_PublicKeySigner.h" + +#include <ccnx/transport/transport_rta/core/components.h> + +static const char name[] = "publicKeySigner"; + +static const char param_KEYSTORE_NAME[] = "KEYSTORE_NAME"; +static const char param_KEYSTORE_PASSWD[] = "KEYSTORE_PASSWD"; +static const char param_SIGNER[] = "SIGNER"; + +/** + * Generates: + * + * { "SIGNER" : "publicKeySigner", + * "publicKeySigner" : { "filename" : filename, "password" : password } + * } + */ +CCNxConnectionConfig * +configPublicKeySigner_SetIdentity(CCNxConnectionConfig *connConfig, const PARCIdentity *identity) +{ + return publicKeySigner_ConnectionConfig(connConfig, + parcIdentity_GetFileName(identity), + parcIdentity_GetPassWord(identity)); +} + +CCNxConnectionConfig * +publicKeySigner_ConnectionConfig(CCNxConnectionConfig *connConfig, const char *filename, const char *password) +{ + assertNotNull(connConfig, "Parameter connConfig must be non-null"); + assertNotNull(filename, "Parameter filename must be non-null"); + assertNotNull(password, "Parameter password must be non-null"); + + PARCJSONValue *signerNameJson = parcJSONValue_CreateFromCString((char *) publicKeySigner_GetName()); + ccnxConnectionConfig_Add(connConfig, param_SIGNER, signerNameJson); + parcJSONValue_Release(&signerNameJson); + + PARCJSON *keystoreJson = parcJSON_Create(); + parcJSON_AddString(keystoreJson, param_KEYSTORE_NAME, filename); + parcJSON_AddString(keystoreJson, param_KEYSTORE_PASSWD, password); + + PARCJSONValue *value = parcJSONValue_CreateFromJSON(keystoreJson); + parcJSON_Release(&keystoreJson); + CCNxConnectionConfig *result = ccnxConnectionConfig_Add(connConfig, publicKeySigner_GetName(), value); + parcJSONValue_Release(&value); + return result; +} + +const char * +publicKeySigner_GetName() +{ + return name; +} + +/** + * If successful, return true and fill in the parameters in the output argument + */ +bool +publicKeySigner_GetConnectionParams(PARCJSON *connectionJson, struct publickeysigner_params *output) +{ + assertNotNull(connectionJson, "Parameter connectionJson must be non-null"); + assertNotNull(output, "Parameter output must be non-null"); + + PARCJSONValue *value = parcJSON_GetValueByName(connectionJson, publicKeySigner_GetName()); + assertNotNull(value, "JSON key %s missing", publicKeySigner_GetName()); + PARCJSON *keystoreJson = parcJSONValue_GetJSON(value); + + value = parcJSON_GetValueByName(keystoreJson, param_KEYSTORE_NAME); + assertNotNull(value, "JSON key %s missing inside %s", param_KEYSTORE_NAME, publicKeySigner_GetName()); + PARCBuffer *sBuf = parcJSONValue_GetString(value); + output->filename = parcBuffer_Overlay(sBuf, 0); + + value = parcJSON_GetValueByName(keystoreJson, param_KEYSTORE_PASSWD); + assertNotNull(value, "JSON key %s missing inside %s", param_KEYSTORE_PASSWD, publicKeySigner_GetName()); + sBuf = parcJSONValue_GetString(value); + output->password = parcBuffer_Overlay(sBuf, 0); + + + return true; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_PublicKeySigner.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_PublicKeySigner.h new file mode 100644 index 00000000..61fe893c --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_PublicKeySigner.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_PublicKeySigner.h + * @brief Generates stack and connection configuration information + * + * Each component in the protocol stack must have a configuration element. + * This module generates the configuration elements for the PKCS12 Signer. + * + * The signer only as a Connection configuration. + * + * @code + * { + * // Configure a stack with {APIConnector,TLVCodec,PKCS12Signer,MetisConnector} + * + * stackConfig = ccnxStackConfig_Create(); + * connConfig = ccnxConnectionConfig_Create(); + * + * apiConnector_ProtocolStackConfig(stackConfig); + * apiConnector_ConnectionConfig(connConfig); + * tlvCodec_ProtocolStackConfig(stackConfig); + * tlvCodec_ConnectionConfig(connConfig); + * + * publicKeySigner_ConnectionConfig(connConfig, "~/.ccnx/keystore.p12", "foobar password"); + * + * metisForwarder_ProtocolStackConfig(stackConfig); + * metisForwarder_ConnectionConfig(connConfig, metisForwarder_GetDefaultPort()); + * + * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connConfig); + * } + * + */ +#ifndef Libccnx_config_PublicKeySigner_h +#define Libccnx_config_PublicKeySigner_h +#include <stdbool.h> + +#include <parc/security/parc_Identity.h> + +#include <ccnx/transport/common/ccnx_TransportConfig.h> + +struct publickeysigner_params { + const char *filename; + const char *password; +}; + +/** + * Generates the configuration settings included in the Connection configuration + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * { "SIGNER" : "publicKeySigner", + * "publicKeySigner" : { "filename" : filename, "password" : password } + * } + * + * @param [in] config A pointer to a valid CCNxConnectionConfig instance. + * + * @return non-null The modified `CCNxConnectionConfig` + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxConnectionConfig *publicKeySigner_ConnectionConfig(CCNxConnectionConfig *config, const char *filename, const char *password); + +/** + * Generates the configuration settings included in the CCNxConnectionConfig for the identity of the configuration 'owner' + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * { "SIGNER" : "publicKeySigner", + * "publicKeySigner" : { "filename" : filename, "password" : password } + * } + * + * @param [in] connConfig The pointer to a valid CCNxConnectionConfig instance. + * @param [in] identity A pointer to a valid PARCIdentity instance. + * + * @return non-null The modified `CCNxConnectionConfig` + */ +CCNxConnectionConfig *configPublicKeySigner_SetIdentity(CCNxConnectionConfig *connConfig, const PARCIdentity *identity); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *publicKeySigner_GetName(void); + +/** + * If successful, return true and fill in the parameters in the output argument + * + * Parses the JSON created by publicKeySigner_ConnectionConfig() and fills in the + * output parameter. The output parameter must be allocated by the caller. + */ +bool publicKeySigner_GetConnectionParams(PARCJSON *connectionJson, struct publickeysigner_params *output); +#endif // Libccnx_config_PublicKeySigner_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Signer.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Signer.c new file mode 100644 index 00000000..ae386c3b --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Signer.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// config_Signer.c +// Libccnx +// +// + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include "config_Signer.h" +#include "config_PublicKeySigner.h" +#include "config_SymmetricKeySigner.h" + +#include <ccnx/transport/transport_rta/core/components.h> + +#include <LongBow/runtime.h> + +static const char param_SIGNER[] = "SIGNER"; + +const char * +signer_GetName() +{ + return param_SIGNER; +} + +/** + * Determine which signer is configured + */ +SignerType +signer_GetImplementationType(PARCJSON *connectionJson) +{ + assertNotNull(connectionJson, "Parameter must be non-null"); + + PARCJSONValue *value = parcJSON_GetValueByName(connectionJson, signer_GetName()); + assertNotNull(value, "signer must be specified in the connection configuration"); + PARCBuffer *sBuf = parcJSONValue_GetString(value); + const char *signer_name = parcBuffer_Overlay(sBuf, 0); + + assertNotNull(signer_name, "Name of signer must be non-null in connection configuration"); + + if (strcasecmp(signer_name, publicKeySigner_GetName()) == 0) { + return SignerType_PublicKeySigner; + } + + if (strcasecmp(signer_name, symmetricKeySigner_GetName()) == 0) { + return SignerType_SymmetricKeySigner; + } + + return SignerType_Unknown; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Signer.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Signer.h new file mode 100644 index 00000000..230c55f3 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Signer.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_Signer.h + * @brief Queries the configuration to determine which signer is used + * + * <#Detailed Description#> + * + */ +#ifndef Libccnx_config_Signer_h +#define Libccnx_config_Signer_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> +#include "config_SymmetricKeySigner.h" +#include "config_PublicKeySigner.h" + +typedef enum { + SignerType_Unknown, + SignerType_PublicKeySigner, + SignerType_SymmetricKeySigner +} SignerType; + +/** + * Determine which signer is configured. Each specific implementation will emit a line + * such as { "SIGNER" : "signer_name" } + */ +SignerType signer_GetImplementationType(PARCJSON *connectionJson); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *signer_GetName(void); +#endif // Libccnx_config_Signer_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_SymmetricKeySigner.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_SymmetricKeySigner.c new file mode 100644 index 00000000..5326af31 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_SymmetricKeySigner.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// config_SymmetricKeySigner.c +// Libccnx +// +// + +#include <config.h> +#include <stdio.h> +#include "config_SymmetricKeySigner.h" + +#include <ccnx/transport/transport_rta/core/components.h> + +#include <LongBow/runtime.h> + +static const char name[] = "SymmetricKeySigner"; + +static const char param_KEYSTORE_NAME[] = "KEYSTORE_NAME"; +static const char param_KEYSTORE_PASSWD[] = "KEYSTORE_PASSWD"; +static const char param_SIGNER[] = "SIGNER"; + +/** + * Generates: + * + * { "SIGNER" : "SymmetricKeySigner", + * "SymmetricKeySigner" : { "filename" : filename, "password" : password } + * } + */ +CCNxConnectionConfig * +symmetricKeySigner_ConnectionConfig(CCNxConnectionConfig *connConfig, const char *filename, const char *password) +{ + assertNotNull(connConfig, "Parameter connConfig must be non-null"); + assertNotNull(filename, "Parameter filename must be non-null"); + assertNotNull(password, "Parameter password must be non-null"); + + PARCJSONValue *signerNameJson = parcJSONValue_CreateFromCString((char *) symmetricKeySigner_GetName()); + ccnxConnectionConfig_Add(connConfig, param_SIGNER, signerNameJson); + parcJSONValue_Release(&signerNameJson); + + PARCJSON *keystoreJson = parcJSON_Create(); + parcJSON_AddString(keystoreJson, param_KEYSTORE_NAME, filename); + parcJSON_AddString(keystoreJson, param_KEYSTORE_PASSWD, password); + + PARCJSONValue *value = parcJSONValue_CreateFromJSON(keystoreJson); + parcJSON_Release(&keystoreJson); + + ccnxConnectionConfig_Add(connConfig, symmetricKeySigner_GetName(), value); + parcJSONValue_Release(&value); + return connConfig; +} + +const char * +symmetricKeySigner_GetName() +{ + return name; +} + +/** + * If successful, return true and fill in the parameters in the output argument + */ +bool +symmetricKeySigner_GetConnectionParams(PARCJSON *connectionJson, struct symmetrickeysigner_params *output) +{ + assertNotNull(connectionJson, "Parameter connectionJson must be non-null"); + assertNotNull(output, "Parameter output must be non-null"); + + PARCJSONValue *value = parcJSON_GetValueByName(connectionJson, symmetricKeySigner_GetName()); + assertNotNull(value, "JSON key %s missing", symmetricKeySigner_GetName()); + PARCJSON *keystoreJson = parcJSONValue_GetJSON(value); + + value = parcJSON_GetValueByName(keystoreJson, param_KEYSTORE_NAME); + assertNotNull(value, "JSON key %s missing inside %s", param_KEYSTORE_NAME, symmetricKeySigner_GetName()); + PARCBuffer *sBuf = parcJSONValue_GetString(value); + output->filename = parcBuffer_Overlay(sBuf, 0); + + value = parcJSON_GetValueByName(keystoreJson, param_KEYSTORE_PASSWD); + assertNotNull(value, "JSON key %s missing inside %s", param_KEYSTORE_PASSWD, symmetricKeySigner_GetName()); + sBuf = parcJSONValue_GetString(value); + output->password = parcBuffer_Overlay(sBuf, 0); + return true; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_SymmetricKeySigner.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_SymmetricKeySigner.h new file mode 100644 index 00000000..0de4be3f --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_SymmetricKeySigner.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_SymmetricKeySigner.h + * @brief Generates stack and connection configuration information + * + * Each component in the protocol stack must have a configuration element. + * This module generates the configuration elements for the Symmetric Keystore. + * The keystore is specific to a Connection, so there is no Protocol Stack configuration. + * + * @code + * { + * // Configure a stack with {APIConnector,TLVCodec,MetisConnector} + * // The coded will use a symmetric keystore + * + * stackConfig = ccnxStackConfig_Create(); + * connConfig = ccnxConnectionConfig_Create(); + * + * apiConnector_ProtocolStackConfig(stackConfig); + * apiConnector_ConnectionConfig(connConfig); + * tlvCodec_ProtocolStackConfig(stackConfig); + * tlvCodec_ConnectionConfig(connConfig); + * symmetricKeySigner_ConnectionConfig(connConfig, "~/.ccnx/keystore.p12", "foobar password"); + * + * metisForwarder_ProtocolStackConfig(stackConfig); + * metisForwarder_ConnectionConfig(connConfig, metis_port); + * + * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connConfig); + * } + * + */ + +#ifndef Libccnx_config_SymmetricKeySigner_h +#define Libccnx_config_SymmetricKeySigner_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> +#include <stdbool.h> + +struct symmetrickeysigner_params { + const char *filename; + const char *password; +}; + +/** + * Generates the configuration settings included in the Connection configuration + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * { "SIGNER" : "SymmetricFileStore", + * "SymmetricFileStore" : { "filename" : filename, password : password } + * } + * + * @param [in] config A pointer to a valid CCNxConnectionConfig instance. + * + * @return non-null The modified `CCNxConnectionConfig` + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxConnectionConfig *symmetricKeySigner_ConnectionConfig(CCNxConnectionConfig *config, const char *filename, const char *password); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *symmetricKeySigner_GetName(void); + +/** + * Look inside a JSON configuration and extract the Signer parameters + * + * <#Paragraphs Of Explanation#> + * + * @param [out] output The filename and password passed down the stack + * + * @return true Configuration item found and output set + * @return false Configuration item not found, output not set + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool symmetricKeySigner_GetConnectionParams(PARCJSON *connectionJson, struct symmetrickeysigner_params *output); +#endif // Libccnx_config_SymmetricKeySigner_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_TestingComponent.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_TestingComponent.c new file mode 100644 index 00000000..1f12bc9b --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_TestingComponent.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <config.h> +#include <stdio.h> +#include "config_TestingComponent.h" + +#include <ccnx/transport/transport_rta/core/components.h> + +CCNxStackConfig * +testingUpper_ProtocolStackConfig(CCNxStackConfig *stackConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxStackConfig *result = ccnxStackConfig_Add(stackConfig, testingUpper_GetName(), value); + parcJSONValue_Release(&value); + + return result; +} + +CCNxStackConfig * +testingLower_ProtocolStackConfig(CCNxStackConfig *stackConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxStackConfig *result = ccnxStackConfig_Add(stackConfig, testingLower_GetName(), value); + parcJSONValue_Release(&value); + + return result; +} + +CCNxConnectionConfig * +testingUpper_ConnectionConfig(CCNxConnectionConfig *connConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxConnectionConfig *result = ccnxConnectionConfig_Add(connConfig, testingUpper_GetName(), value); + parcJSONValue_Release(&value); + + return result; +} + +CCNxConnectionConfig * +testingLower_ConnectionConfig(CCNxConnectionConfig *connConfig) +{ + PARCJSONValue *value = parcJSONValue_CreateFromNULL(); + CCNxConnectionConfig *result = ccnxConnectionConfig_Add(connConfig, testingLower_GetName(), value); + parcJSONValue_Release(&value); + + return result; +} + +const char * +testingUpper_GetName() +{ + return RtaComponentNames[TESTING_UPPER]; +} + +const char * +testingLower_GetName() +{ + return RtaComponentNames[TESTING_LOWER]; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_TestingComponent.h b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_TestingComponent.h new file mode 100644 index 00000000..a8c6e83b --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/config_TestingComponent.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file config_TestingComponent.h + * @brief Generates stack and connection configuration information + * + * Each component in the protocol stack must have a configuration element. + * This module generates the configuration elements for a testing component + * to be used in unit tests. The upper and lower testing components surround + * a component under test to simulate feeding in or sending out messages. + * + * @code + * { + * // Configure a stack with {TestingUpper,TLVCodec,TestingLower} + * + * stackConfig = ccnxStackConfig_Create(); + * connConfig = ccnxConnectionConfig_Create(); + * + * testingUpper_ProtocolStackConfig(stackConfig); + * testingUpper_ConnectionConfig(connConfig); + * tlvCodec_ProtocolStackConfig(stackConfig); + * tlvCodec_ConnectionConfig(connConfig); + * testingLower_ProtocolStackConfig(stackConfig); + * testingLower_ConnectionConfig(connConfig, metis_port); + * + * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connConfig); + * } + * + */ + +#ifndef Libccnx_config_TestingComponent_h +#define Libccnx_config_TestingComponent_h + +#include <ccnx/transport/common/ccnx_TransportConfig.h> + +/** + * Generates the configuration settings included in the Protocol Stack configuration + * + * Adds configuration elements to the Protocol Stack configuration + * + * { "TESTING UPPER" : { } } + * + * @param [in] stackConfig The protocl stack configuration to update + * + * @return non-null The updated protocol stack configuration + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxStackConfig *testingUpper_ProtocolStackConfig(CCNxStackConfig *stackConfig); + +/** + * Generates the configuration settings included in the Protocol Stack configuration + * + * Adds configuration elements to the Protocol Stack configuration + * + * { "TESTING LOWER" : { } } + * + * @param [in] stackConfig The protocl stack configuration to update + * + * @return non-null The updated protocol stack configuration + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxStackConfig *testingLower_ProtocolStackConfig(CCNxStackConfig *stackConfig); + +/** + * Generates the configuration settings included in the Connection configuration + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * @param [in] config A pointer to a valid `CCNxConnectionConfig` instance. + * + * @return non-null A pointer to the modified `CCNxConnectionConfig` instance + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxConnectionConfig *testingUpper_ConnectionConfig(CCNxConnectionConfig *config); + +/** + * Generates the configuration settings included in the Connection configuration + * + * Adds configuration elements to the `CCNxConnectionConfig` + * + * @param [in] config A pointer to a valid `CCNxConnectionConfig` instance. + * + * @return non-null The modified `CCNxConnectionConfig` + * + * Example: + * @code + * <#example#> + * @endcode + */ +CCNxConnectionConfig *testingLower_ConnectionConfig(CCNxConnectionConfig *config); + + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *testingUpper_GetName(void); + +/** + * Returns the text string for this component + * + * Used as the text key to a JSON block. You do not need to free it. + * + * @return non-null A text string unique to this component + * + */ +const char *testingLower_GetName(void); +#endif // Libccnx_config_TestingComponent_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/CMakeLists.txt b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/CMakeLists.txt new file mode 100644 index 00000000..235ef4be --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/CMakeLists.txt @@ -0,0 +1,22 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_config_ApiConnector + test_config_Codec_Tlv + test_config_FlowControl_Vegas + test_config_Forwarder_Local + test_config_Forwarder_Metis + test_config_InMemoryVerifier + test_config_ProtocolStack + test_config_PublicKeySigner + test_config_Signer + test_config_SymmetricKeySigner + test_config_TestingComponent +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_ApiConnector.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_ApiConnector.c new file mode 100644 index 00000000..10abcbd8 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_ApiConnector.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Rta component configuration class unit test + * + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_ApiConnector.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "testrig_RtaConfigCommon.c" + +LONGBOW_TEST_RUNNER(config_ApiConnector) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_ApiConnector) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_ApiConnector) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, apiConnector_ConnectionConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, apiConnector_ConnectionConfig_ReturnValue); + LONGBOW_RUN_TEST_CASE(Global, apiConnector_GetName); + LONGBOW_RUN_TEST_CASE(Global, apiConnector_ProtocolStackConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, apiConnector_ProtocolStackConfig_ReturnValue); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, testRtaConfiguration_CommonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + testRtaConfiguration_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, apiConnector_ConnectionConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxConnectionConfig *test = apiConnector_ConnectionConfig(data->connConfig); + + assertTrue(test == data->connConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->connConfig); +} + +LONGBOW_TEST_CASE(Global, apiConnector_ConnectionConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ConnectionJsonKey(apiConnector_ConnectionConfig(data->connConfig), + apiConnector_GetName()); +} + +LONGBOW_TEST_CASE(Global, apiConnector_GetName) +{ + testRtaConfiguration_ComponentName(apiConnector_GetName, RtaComponentNames[API_CONNECTOR]); +} + +LONGBOW_TEST_CASE(Global, apiConnector_ProtocolStackConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ProtocolStackJsonKey(apiConnector_ProtocolStackConfig(data->stackConfig), + apiConnector_GetName()); +} + +LONGBOW_TEST_CASE(Global, apiConnector_ProtocolStackConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxStackConfig *test = apiConnector_ProtocolStackConfig(data->stackConfig); + + assertTrue(test == data->stackConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->stackConfig); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_ApiConnector); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Codec_Tlv.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Codec_Tlv.c new file mode 100644 index 00000000..d3159bb1 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Codec_Tlv.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Rta component configuration class unit test + * + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_Codec_Tlv.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "testrig_RtaConfigCommon.c" + +LONGBOW_TEST_RUNNER(config_Codec_Tlv) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_Codec_Tlv) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_Codec_Tlv) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, Codec_Tlv_ConnectionConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, Codec_Tlv_ConnectionConfig_ReturnValue); + LONGBOW_RUN_TEST_CASE(Global, Codec_Tlv_GetName); + LONGBOW_RUN_TEST_CASE(Global, Codec_Tlv_ProtocolStackConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, Codec_Tlv_ProtocolStackConfig_ReturnValue); + + LONGBOW_RUN_TEST_CASE(Global, tlvCodec_ConnectionConfig); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, testRtaConfiguration_CommonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + testRtaConfiguration_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, Codec_Tlv_ConnectionConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxConnectionConfig *test = tlvCodec_ConnectionConfig(data->connConfig); + + assertTrue(test == data->connConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->connConfig); +} + +LONGBOW_TEST_CASE(Global, Codec_Tlv_ConnectionConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ConnectionJsonKey(tlvCodec_ConnectionConfig(data->connConfig), + tlvCodec_GetName()); +} + +LONGBOW_TEST_CASE(Global, Codec_Tlv_GetName) +{ + testRtaConfiguration_ComponentName(tlvCodec_GetName, RtaComponentNames[CODEC_TLV]); +} + +LONGBOW_TEST_CASE(Global, Codec_Tlv_ProtocolStackConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ProtocolStackJsonKey(tlvCodec_ProtocolStackConfig(data->stackConfig), + tlvCodec_GetName()); +} + +LONGBOW_TEST_CASE(Global, Codec_Tlv_ProtocolStackConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxStackConfig *test = tlvCodec_ProtocolStackConfig(data->stackConfig); + + assertTrue(test == data->stackConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->stackConfig); +} + +LONGBOW_TEST_CASE(Global, tlvCodec_ConnectionConfig) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ConnectionJsonKey(tlvCodec_ConnectionConfig(data->connConfig), + tlvCodec_GetName()); + + PARCJSON *json = ccnxConnectionConfig_GetJson(data->connConfig); + assertNotNull(json, "Expected a non-NULL connectionConfig."); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_Codec_Tlv); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_FlowControl_Vegas.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_FlowControl_Vegas.c new file mode 100644 index 00000000..84e8349d --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_FlowControl_Vegas.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Rta component configuration class unit test + * + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_FlowControl_Vegas.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "testrig_RtaConfigCommon.c" + +LONGBOW_TEST_RUNNER(config_FlowControl_Vegas) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_FlowControl_Vegas) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_FlowControl_Vegas) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, FlowControl_Vegas_ConnectionConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, FlowControl_Vegas_ConnectionConfig_ReturnValue); + LONGBOW_RUN_TEST_CASE(Global, FlowControl_Vegas_GetName); + LONGBOW_RUN_TEST_CASE(Global, FlowControl_Vegas_ProtocolStackConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, FlowControl_Vegas_ProtocolStackConfig_ReturnValue); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, testRtaConfiguration_CommonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + testRtaConfiguration_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, FlowControl_Vegas_ConnectionConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxConnectionConfig *test = vegasFlowController_ConnectionConfig(data->connConfig); + + assertTrue(test == data->connConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->connConfig); +} + +LONGBOW_TEST_CASE(Global, FlowControl_Vegas_ConnectionConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ConnectionJsonKey(vegasFlowController_ConnectionConfig(data->connConfig), + vegasFlowController_GetName()); +} + +LONGBOW_TEST_CASE(Global, FlowControl_Vegas_GetName) +{ + testRtaConfiguration_ComponentName(vegasFlowController_GetName, RtaComponentNames[FC_VEGAS]); +} + +LONGBOW_TEST_CASE(Global, FlowControl_Vegas_ProtocolStackConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ProtocolStackJsonKey(vegasFlowController_ProtocolStackConfig(data->stackConfig), + vegasFlowController_GetName()); +} + +LONGBOW_TEST_CASE(Global, FlowControl_Vegas_ProtocolStackConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxStackConfig *test = vegasFlowController_ProtocolStackConfig(data->stackConfig); + + assertTrue(test == data->stackConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->stackConfig); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_FlowControl_Vegas); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Forwarder_Local.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Forwarder_Local.c new file mode 100644 index 00000000..39d82172 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Forwarder_Local.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Rta component configuration class unit test + * + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_Forwarder_Local.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "testrig_RtaConfigCommon.c" + +LONGBOW_TEST_RUNNER(config_Forwarder_Local) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_Forwarder_Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_Forwarder_Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Local_ConnectionConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Local_ConnectionConfig_ReturnValue); + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Local_GetName); + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Local_ProtocolStackConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Local_ProtocolStackConfig_ReturnValue); + + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Local_GetPath); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, testRtaConfiguration_CommonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + testRtaConfiguration_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, Forwarder_Local_ConnectionConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxConnectionConfig *test = localForwarder_ConnectionConfig(data->connConfig, "/path/to/socket"); + + assertTrue(test == data->connConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->connConfig); +} + +LONGBOW_TEST_CASE(Global, Forwarder_Local_ConnectionConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ConnectionJsonKey(localForwarder_ConnectionConfig(data->connConfig, "/path/to/socket"), + localForwarder_GetName()); +} + +LONGBOW_TEST_CASE(Global, Forwarder_Local_GetName) +{ + testRtaConfiguration_ComponentName(localForwarder_GetName, RtaComponentNames[FWD_LOCAL]); +} + +LONGBOW_TEST_CASE(Global, Forwarder_Local_GetPath) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + const char *truth = "/path/to/socket"; + localForwarder_ConnectionConfig(data->connConfig, truth); + const char *test = localForwarder_GetPath(ccnxConnectionConfig_GetJson(data->connConfig)); + assertTrue(strcmp(truth, test) == 0, "Got wrong socket path, got %s expected %s", test, truth); +} + +LONGBOW_TEST_CASE(Global, Forwarder_Local_ProtocolStackConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ProtocolStackJsonKey(localForwarder_ProtocolStackConfig(data->stackConfig), + localForwarder_GetName()); +} + +LONGBOW_TEST_CASE(Global, Forwarder_Local_ProtocolStackConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxStackConfig *test = localForwarder_ProtocolStackConfig(data->stackConfig); + + assertTrue(test == data->stackConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->stackConfig); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_Forwarder_Local); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Forwarder_Metis.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Forwarder_Metis.c new file mode 100644 index 00000000..b4c2c8c4 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Forwarder_Metis.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Rta component configuration class unit test + * + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_Forwarder_Metis.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "testrig_RtaConfigCommon.c" + +LONGBOW_TEST_RUNNER(config_Forwarder_Metis) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Metis); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_Forwarder_Metis) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_Forwarder_Metis) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Metis_ConnectionConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Metis_ConnectionConfig_ReturnValue); + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Metis_GetName); + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Metis_ProtocolStackConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Metis_ProtocolStackConfig_ReturnValue); + + LONGBOW_RUN_TEST_CASE(Global, Forwarder_Metis_GetPath); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, testRtaConfiguration_CommonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + testRtaConfiguration_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, Forwarder_Metis_ConnectionConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxConnectionConfig *test = metisForwarder_ConnectionConfig(data->connConfig, 9999); + + assertTrue(test == data->connConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->connConfig); +} + +LONGBOW_TEST_CASE(Global, Forwarder_Metis_ConnectionConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ConnectionJsonKey(metisForwarder_ConnectionConfig(data->connConfig, 9999), + metisForwarder_GetName()); +} + +LONGBOW_TEST_CASE(Global, Forwarder_Metis_GetName) +{ + testRtaConfiguration_ComponentName(metisForwarder_GetName, RtaComponentNames[FWD_METIS]); +} + +LONGBOW_TEST_CASE(Global, Forwarder_Metis_GetPath) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + int truth = 9999; + metisForwarder_ConnectionConfig(data->connConfig, truth); + int test = metisForwarder_GetPortFromConfig(ccnxConnectionConfig_GetJson(data->connConfig)); + assertTrue(truth == test, "Got wrong socket path, got %d expected %d", test, truth); +} + +LONGBOW_TEST_CASE(Global, Forwarder_Metis_ProtocolStackConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ProtocolStackJsonKey(metisForwarder_ProtocolStackConfig(data->stackConfig), + metisForwarder_GetName()); +} + +LONGBOW_TEST_CASE(Global, Forwarder_Metis_ProtocolStackConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxStackConfig *test = metisForwarder_ProtocolStackConfig(data->stackConfig); + + assertTrue(test == data->stackConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->stackConfig); +} + +LONGBOW_TEST_FIXTURE(Metis) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Metis) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Metis) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_Forwarder_Metis); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_InMemoryVerifier.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_InMemoryVerifier.c new file mode 100644 index 00000000..f4455b48 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_InMemoryVerifier.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Rta component configuration class unit test + * + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_InMemoryVerifier.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "testrig_RtaConfigCommon.c" + +LONGBOW_TEST_RUNNER(config_InMemoryVerifier) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_InMemoryVerifier) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_InMemoryVerifier) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, InMemoryVerifier_ConnectionConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, InMemoryVerifier_ConnectionConfig_ReturnValue); + LONGBOW_RUN_TEST_CASE(Global, InMemoryVerifier_GetName); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, testRtaConfiguration_CommonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + testRtaConfiguration_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, InMemoryVerifier_ConnectionConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxConnectionConfig *test = inMemoryVerifier_ConnectionConfig(data->connConfig); + + assertTrue(test == data->connConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->connConfig); +} + +LONGBOW_TEST_CASE(Global, InMemoryVerifier_ConnectionConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ConnectionJsonKey(inMemoryVerifier_ConnectionConfig(data->connConfig), + inMemoryVerifier_GetName()); +} + +LONGBOW_TEST_CASE(Global, InMemoryVerifier_GetName) +{ + testRtaConfiguration_ComponentName(inMemoryVerifier_GetName, "InMemoryVerifier"); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_InMemoryVerifier); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_ProtocolStack.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_ProtocolStack.c new file mode 100644 index 00000000..d784b2be --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_ProtocolStack.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_ProtocolStack.c" +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> +#include "testrig_RtaConfigCommon.c" + +#include <ccnx/transport/common/ccnx_TransportConfig.h> + +LONGBOW_TEST_RUNNER(config_ProtocolStack) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_ProtocolStack) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_ProtocolStack) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, protocolStack_ComponentsConfigArgs); + LONGBOW_RUN_TEST_CASE(Global, protocolStack_ComponentsConfigArrayList); + LONGBOW_RUN_TEST_CASE(Global, protocolStack_GetComponentNameArray); + LONGBOW_RUN_TEST_CASE(Global, protocolStack_GetName); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, protocolStack_ComponentsConfigArgs) +{ + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + + const char truth[] = "{\"STACK\":{\"COMPONENTS\":[\"Apple\",\"Bananna\",\"Cherry\"]}}"; + + protocolStack_ComponentsConfigArgs(stackConfig, "Apple", "Bananna", "Cherry", NULL); + PARCJSON *json = ccnxStackConfig_GetJson(stackConfig); + char *str = parcJSON_ToCompactString(json); + assertTrue(strcmp(truth, str) == 0, "Got wrong config, got %s expected %s", str, truth); + parcMemory_Deallocate((void **) &str); + ccnxStackConfig_Release(&stackConfig); +} + +LONGBOW_TEST_CASE(Global, protocolStack_ComponentsConfigArrayList) +{ + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + PARCArrayList *names = parcArrayList_Create(NULL); + parcArrayList_Add(names, "Apple"); + parcArrayList_Add(names, "Bananna"); + parcArrayList_Add(names, "Cherry"); + + const char truth[] = "{\"STACK\":{\"COMPONENTS\":[\"Apple\",\"Bananna\",\"Cherry\"]}}"; + + protocolStack_ComponentsConfigArrayList(stackConfig, names); + PARCJSON *json = ccnxStackConfig_GetJson(stackConfig); + char *str = parcJSON_ToCompactString(json); + assertTrue(strcmp(truth, str) == 0, "Got wrong config, got %s expected %s", str, truth); + + parcMemory_Deallocate((void **) &str); + ccnxStackConfig_Release(&stackConfig); + parcArrayList_Destroy(&names); +} + +LONGBOW_TEST_CASE(Global, protocolStack_GetComponentNameArray) +{ + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + PARCArrayList *names = parcArrayList_Create(NULL); + parcArrayList_Add(names, "Apple"); + parcArrayList_Add(names, "Bananna"); + parcArrayList_Add(names, "Cherry"); + + protocolStack_ComponentsConfigArrayList(stackConfig, names); + + char truth[] = "{\"STACK\":{\"COMPONENTS\":[\"Apple\",\"Bananna\",\"Cherry\"]}}"; + PARCJSON *json = parcJSON_ParseString(truth); + + PARCArrayList *test = protocolStack_GetComponentNameArray(json); + + assertTrue(parcArrayList_Size(test) == parcArrayList_Size(names), + "wrong array list size, got %zu expected %zu", + parcArrayList_Size(test), parcArrayList_Size(names)); + for (int i = 0; i < parcArrayList_Size(test); i++) { + char *a = parcArrayList_Get(test, i); + char *b = parcArrayList_Get(names, i); + assertTrue(strcmp(a, b) == 0, "mismatch elements %d, got %s expected %s", i, a, b); + } + + ccnxStackConfig_Release(&stackConfig); + parcArrayList_Destroy(&names); + parcJSON_Release(&json); + parcArrayList_Destroy(&test); +} + +LONGBOW_TEST_CASE(Global, protocolStack_GetName) +{ + const char *name = protocolStack_GetName(); + assertTrue(strcmp(name, param_STACK) == 0, "Got wrong name, got %s expected %s", name, param_STACK); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_ProtocolStack); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_PublicKeySigner.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_PublicKeySigner.c new file mode 100644 index 00000000..325d4b86 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_PublicKeySigner.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_PublicKeySigner.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include "testrig_RtaConfigCommon.c" +#include <ccnx/transport/transport_rta/config/config_Signer.h> + +LONGBOW_TEST_RUNNER(config_PublicKeySignerPkcs12Store) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_PublicKeySignerPkcs12Store) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_PublicKeySignerPkcs12Store) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, publicKeySignerPkcs12Store_ConnectionConfig); + LONGBOW_RUN_TEST_CASE(Global, publicKeySignerPkcs12Store_GetConnectionParams); + LONGBOW_RUN_TEST_CASE(Global, publicKeySignerPkcs12Store_GetName); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, publicKeySignerPkcs12Store_ConnectionConfig) +{ + CCNxConnectionConfig *connConfig = ccnxConnectionConfig_Create(); + const char *filename = "filename"; + const char *password = "password"; + publicKeySigner_ConnectionConfig(connConfig, filename, password); + + // make sure our stuff is in there + testRtaConfiguration_ConnectionJsonKey(connConfig, publicKeySigner_GetName()); + + // make sure the SIGNER parameter is in there + testRtaConfiguration_ConnectionJsonKey(connConfig, signer_GetName()); + + ccnxConnectionConfig_Destroy(&connConfig); +} + +LONGBOW_TEST_CASE(Global, publicKeySignerPkcs12Store_GetConnectionParams) +{ + CCNxConnectionConfig *connConfig = ccnxConnectionConfig_Create(); + const char *filename = "filename"; + const char *password = "password"; + publicKeySigner_ConnectionConfig(connConfig, filename, password); + + PARCJSON *json = ccnxConnectionConfig_GetJson(connConfig); + struct publickeysigner_params params; + + publicKeySigner_GetConnectionParams(json, ¶ms); + + assertTrue(strncmp(params.filename, filename, strlen(filename)) == 0, "wrong filename, got %s expected %s", params.filename, filename); + assertTrue(strncmp(params.password, password, strlen(password)) == 0, "wrong password, got %s expected %s", params.password, password); + + ccnxConnectionConfig_Destroy(&connConfig); +} + +LONGBOW_TEST_CASE(Global, publicKeySignerPkcs12Store_GetName) +{ + testRtaConfiguration_ComponentName(&publicKeySigner_GetName, name); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_PublicKeySignerPkcs12Store); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Signer.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Signer.c new file mode 100644 index 00000000..cf8f3e98 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Signer.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_Signer.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include "testrig_RtaConfigCommon.c" + +LONGBOW_TEST_RUNNER(config_Signer) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_Signer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_Signer) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, signer_GetImplementationType_PublicKey); + LONGBOW_RUN_TEST_CASE(Global, signer_GetImplementationType_SymmetricKey); + LONGBOW_RUN_TEST_CASE(Global, signer_GetImplementationType_Unknown); + LONGBOW_RUN_TEST_CASE(Global, signer_GetName); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, signer_GetImplementationType_PublicKey) +{ + CCNxConnectionConfig *connConfig = ccnxConnectionConfig_Create(); + const char *filename = "filename"; + const char *password = "password"; + publicKeySigner_ConnectionConfig(connConfig, filename, password); + + PARCJSON *json = ccnxConnectionConfig_GetJson(connConfig); + + SignerType type = signer_GetImplementationType(json); + assertTrue(type == SignerType_PublicKeySigner, "Got wrong signer type, got %d expected %d", type, SignerType_PublicKeySigner); + + ccnxConnectionConfig_Destroy(&connConfig); +} + +LONGBOW_TEST_CASE(Global, signer_GetImplementationType_SymmetricKey) +{ + CCNxConnectionConfig *connConfig = ccnxConnectionConfig_Create(); + const char *filename = "filename"; + const char *password = "password"; + symmetricKeySigner_ConnectionConfig(connConfig, filename, password); + + PARCJSON *json = ccnxConnectionConfig_GetJson(connConfig); + + SignerType type = signer_GetImplementationType(json); + assertTrue(type == SignerType_SymmetricKeySigner, "Got wrong signer type, got %d expected %d", type, SignerType_SymmetricKeySigner); + + ccnxConnectionConfig_Destroy(&connConfig); +} + +LONGBOW_TEST_CASE(Global, signer_GetImplementationType_Unknown) +{ + char *bogusSignerString = "{\"SIGNER\":\"BogusSigner\",\"BogusSigner\":{}}"; + + PARCJSON *json = parcJSON_ParseString(bogusSignerString); + + SignerType type = signer_GetImplementationType(json); + assertTrue(type == SignerType_Unknown, "Got wrong signer type, got %d expected %d", type, SignerType_Unknown); + + parcJSON_Release(&json); +} + +LONGBOW_TEST_CASE(Global, signer_GetName) +{ + testRtaConfiguration_ComponentName(&signer_GetName, param_SIGNER); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_Signer); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_SymmetricKeySigner.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_SymmetricKeySigner.c new file mode 100644 index 00000000..4d6244e1 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_SymmetricKeySigner.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_SymmetricKeySigner.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include "testrig_RtaConfigCommon.c" +#include <ccnx/transport/transport_rta/config/config_Signer.h> + +LONGBOW_TEST_RUNNER(config_SymmetricKeySignerFileStore) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_SymmetricKeySignerFileStore) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_SymmetricKeySignerFileStore) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, symmetricKeySignerFileStore_ConnectionConfig); + LONGBOW_RUN_TEST_CASE(Global, symmetricKeySignerFileStore_GetConnectionParams); + LONGBOW_RUN_TEST_CASE(Global, symmetricKeySignerFileStore_GetName); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, symmetricKeySignerFileStore_ConnectionConfig) +{ + CCNxConnectionConfig *connConfig = ccnxConnectionConfig_Create(); + const char *filename = "filename"; + const char *password = "password"; + symmetricKeySigner_ConnectionConfig(connConfig, filename, password); + + // make sure our stuff is in there + testRtaConfiguration_ConnectionJsonKey(connConfig, symmetricKeySigner_GetName()); + + // make sure the SIGNER parameter is in there + testRtaConfiguration_ConnectionJsonKey(connConfig, signer_GetName()); + + ccnxConnectionConfig_Destroy(&connConfig); +} + +LONGBOW_TEST_CASE(Global, symmetricKeySignerFileStore_GetConnectionParams) +{ + CCNxConnectionConfig *connConfig = ccnxConnectionConfig_Create(); + const char *filename = "filename"; + const char *password = "password"; + symmetricKeySigner_ConnectionConfig(connConfig, filename, password); + + PARCJSON *json = ccnxConnectionConfig_GetJson(connConfig); + struct symmetrickeysigner_params params; + + symmetricKeySigner_GetConnectionParams(json, ¶ms); + + assertTrue(strncmp(params.filename, filename, strlen(filename)) == 0, "wrong filename, got %s expected %s", params.filename, filename); + assertTrue(strncmp(params.password, password, strlen(password)) == 0, "wrong password, got %s expected %s", params.password, password); + + ccnxConnectionConfig_Destroy(&connConfig); +} + +LONGBOW_TEST_CASE(Global, symmetricKeySignerFileStore_GetName) +{ + testRtaConfiguration_ComponentName(&symmetricKeySigner_GetName, name); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_SymmetricKeySignerFileStore); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_TestingComponent.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_TestingComponent.c new file mode 100644 index 00000000..ac8ba52e --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_TestingComponent.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Rta component configuration class unit test + * + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../config_TestingComponent.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include "testrig_RtaConfigCommon.c" + +LONGBOW_TEST_RUNNER(config_TestingComponent) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(config_TestingComponent) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(config_TestingComponent) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, testingUpper_ConnectionConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, testingUpper_ConnectionConfig_ReturnValue); + LONGBOW_RUN_TEST_CASE(Global, testingUpper_GetName); + LONGBOW_RUN_TEST_CASE(Global, testingUpper_ProtocolStackConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, testingUpper_ProtocolStackConfig_ReturnValue); + + LONGBOW_RUN_TEST_CASE(Global, testingLower_ConnectionConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, testingLower_ConnectionConfig_ReturnValue); + LONGBOW_RUN_TEST_CASE(Global, testingLower_GetName); + LONGBOW_RUN_TEST_CASE(Global, testingLower_ProtocolStackConfig_JsonKey); + LONGBOW_RUN_TEST_CASE(Global, testingLower_ProtocolStackConfig_ReturnValue); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, testRtaConfiguration_CommonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + testRtaConfiguration_CommonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, testingUpper_ConnectionConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxConnectionConfig *test = testingUpper_ConnectionConfig(data->connConfig); + + assertTrue(test == data->connConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->connConfig); +} + +LONGBOW_TEST_CASE(Global, testingUpper_ConnectionConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ConnectionJsonKey(testingUpper_ConnectionConfig(data->connConfig), + testingUpper_GetName()); +} + +LONGBOW_TEST_CASE(Global, testingUpper_GetName) +{ + testRtaConfiguration_ComponentName(testingUpper_GetName, RtaComponentNames[TESTING_UPPER]); +} + +LONGBOW_TEST_CASE(Global, testingUpper_ProtocolStackConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ProtocolStackJsonKey(testingUpper_ProtocolStackConfig(data->stackConfig), + testingUpper_GetName()); +} + +LONGBOW_TEST_CASE(Global, testingUpper_ProtocolStackConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxStackConfig *test = testingUpper_ProtocolStackConfig(data->stackConfig); + + assertTrue(test == data->stackConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->stackConfig); +} + +LONGBOW_TEST_CASE(Global, testingLower_ConnectionConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxConnectionConfig *test = testingLower_ConnectionConfig(data->connConfig); + + assertTrue(test == data->connConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->connConfig); +} + +LONGBOW_TEST_CASE(Global, testingLower_ConnectionConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ConnectionJsonKey(testingUpper_ConnectionConfig(data->connConfig), + testingUpper_GetName()); +} + +LONGBOW_TEST_CASE(Global, testingLower_GetName) +{ + testRtaConfiguration_ComponentName(testingLower_GetName, RtaComponentNames[TESTING_LOWER]); +} + +LONGBOW_TEST_CASE(Global, testingLower_ProtocolStackConfig_JsonKey) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + testRtaConfiguration_ProtocolStackJsonKey(testingLower_ProtocolStackConfig(data->stackConfig), + testingLower_GetName()); +} + +LONGBOW_TEST_CASE(Global, testingLower_ProtocolStackConfig_ReturnValue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxStackConfig *test = testingLower_ProtocolStackConfig(data->stackConfig); + + assertTrue(test == data->stackConfig, + "Did not return pointer to argument for chaining, got %p expected %p", + (void *) test, (void *) data->stackConfig); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(config_TestingComponent); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/testrig_RtaConfigCommon.c b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/testrig_RtaConfigCommon.c new file mode 100644 index 00000000..29f2da24 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/config/test/testrig_RtaConfigCommon.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common test routines for the RTA component configuration functions + * + */ + +typedef struct test_data { + CCNxConnectionConfig *connConfig; + CCNxStackConfig *stackConfig; +} TestData; + +TestData * +testRtaConfiguration_CommonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->connConfig = ccnxConnectionConfig_Create(); + data->stackConfig = ccnxStackConfig_Create(); + return data; +} + +void +testRtaConfiguration_CommonTeardown(TestData *data) +{ + ccnxStackConfig_Release(&data->stackConfig); + ccnxConnectionConfig_Destroy(&data->connConfig); + parcMemory_Deallocate((void **) &data); +} + +void +testRtaConfiguration_ComponentName(const char * (*getname)(void), const char *truth) +{ + const char *name = getname(); + assertTrue(strcmp(name, truth) == 0, + "Got wrong name, got %s expected %s", name, truth); +} + +void +testRtaConfiguration_ConnectionJsonKey(CCNxConnectionConfig *configToTest, const char *key) +{ + PARCJSON *json = ccnxConnectionConfig_GetJson(configToTest); + PARCJSONValue *value = parcJSON_GetValueByName(json, key); + assertNotNull(value, "Could not find key %s in configuration json", key); +} + +void +testRtaConfiguration_ProtocolStackJsonKey(CCNxStackConfig *configToTest, const char *key) +{ + PARCJSON *json = ccnxStackConfig_GetJson(configToTest); + PARCJSONValue *value = parcJSON_GetValueByName(json, key); + assertNotNull(value, "Could not find key %s in configuration json", key); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Api.c b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Api.c new file mode 100644 index 00000000..7b810adc --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Api.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Interface between the event dispatcher and component callbacks to + * the RtaApiConnection. The API connector, per se, is implemented in rta_ApiConnection. This + * module is the scaffolding to work within the RTA component framework. + * + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> +#include <sys/socket.h> +#include <errno.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> + +#include <ccnx/transport/transport_rta/connectors/rta_ApiConnection.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework_Services.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> +#include <ccnx/transport/transport_rta/connectors/connector_Api.h> + +#include <ccnx/api/control/controlPlaneInterface.h> + +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +static int connector_Api_Init(RtaProtocolStack *stack); +static int connector_Api_Opener(RtaConnection *conn); +static void connector_Api_Upcall_Read(PARCEventQueue *, PARCEventType, void *conn); +static int connector_Api_Closer(RtaConnection *conn); +static int connector_Api_Release(RtaProtocolStack *stack); +static void connector_Api_StateChange(RtaConnection *conn); + +RtaComponentOperations api_ops = +{ + .init = connector_Api_Init, + .open = connector_Api_Opener, + .upcallRead = connector_Api_Upcall_Read, + .upcallEvent = NULL, + .downcallRead = NULL, + .downcallEvent = NULL, + .close = connector_Api_Closer, + .release = connector_Api_Release, + .stateChange = connector_Api_StateChange +}; + +// ======================== + +static int +connector_Api_Init(RtaProtocolStack *stack) +{ + // nothing to do here + if (DEBUG_OUTPUT) { + printf("%s init stack %p\n", + __func__, + (void *) stack); + } + return 0; +} + +/* + * Api_Open will put the RtaConnection as the callback parameter in the UpcallRead, + * because its a per-connection descriptor. + * + * Returns 0 on success, -1 on error + */ +static int +connector_Api_Opener(RtaConnection *connection) +{ + RtaComponentStats *stats; + RtaApiConnection *apiConnection = rtaApiConnection_Create(connection); + + rtaConnection_SetPrivateData(connection, API_CONNECTOR, apiConnection); + + stats = rtaConnection_GetStats(connection, API_CONNECTOR); + assertNotNull(stats, "%s returned null stats\n", __func__); + rtaComponentStats_Increment(stats, STATS_OPENS); + + rtaConnection_SetState(connection, CONN_OPEN); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s opened transport_fd %d\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(connection))), + __func__, + rtaConnection_GetTransportFd(connection)); + + printf("%9" PRIu64 " %s open conn %p state %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(connection))), + __func__, + (void *) connection, + (void *) apiConnection); + } + + return 0; +} + +/* + * Read a message from below in stack + * Write a message up to the API + */ +static void +connector_Api_Upcall_Read(PARCEventQueue *eventBuffer, PARCEventType type, void *protocolStackVoid) +{ + TransportMessage *tm; + + assertNotNull(protocolStackVoid, "%s called with null ProtocolStack\n", __func__); + + while ((tm = rtaComponent_GetMessage(eventBuffer)) != NULL) { + RtaConnection *conn = rtaConnection_GetFromTransport(tm); + assertNotNull(conn, "got null connection from transport message\n"); + + RtaComponentStats *stats = rtaConnection_GetStats(conn, API_CONNECTOR); + assertNotNull(stats, "returned null stats\n"); + + rtaComponentStats_Increment(stats, STATS_UPCALL_IN); + + RtaApiConnection *apiConnection = rtaConnection_GetPrivateData(conn, API_CONNECTOR); + assertNotNull(apiConnection, "got null apiConnection\n"); + + // If we are blocked, only pass control messages + if (!rtaConnection_BlockedUp(conn) || transportMessage_IsControl(tm)) { + if (!rtaApiConnection_SendToApi(apiConnection, tm, stats)) { + // memory is freed at bottom of function + } + } else { + // closed connection, just destroy the message + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s conn %p destroying transport message %p due to closed connection\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn, + (void *) tm); + } + } + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s conn %p total upcall reads in %" PRIu64 " out %" PRIu64 "\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn, + rtaComponentStats_Get(stats, STATS_UPCALL_IN), + rtaComponentStats_Get(stats, STATS_UPCALL_OUT)); + } + + // This is the end of life for the transport message. If the inner TlvDictionary + // was put in a CCNxMessage and sent up the stack, then we made another reference to it + // so this destroy will not destroy that part. + transportMessage_Destroy(&tm); + } +} + +/* + * The higher layer should no longer be writing to this + * socketpair, so we can drain it then close it. + */ +static int +connector_Api_Closer(RtaConnection *conn) +{ + RtaComponentStats *stats; + RtaApiConnection *apiConnection = rtaConnection_GetPrivateData(conn, API_CONNECTOR); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s starting close conn %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn); + } + + stats = rtaConnection_GetStats(conn, API_CONNECTOR); + assertNotNull(stats, "%s returned null stats\n", __func__); + rtaComponentStats_Increment(stats, STATS_CLOSES); + + // This will prevent any new data going in to queues for the connection + // Existing messages will be destroyed + rtaConnection_SetState(conn, CONN_CLOSED); + + rtaApiConnection_Destroy(&apiConnection); + rtaConnection_SetPrivateData(conn, API_CONNECTOR, NULL); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s close conn %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn); + } + + return 0; +} + +static int +connector_Api_Release(RtaProtocolStack *stack) +{ + // nothing to do here, there's no ProtocolStack state + if (DEBUG_OUTPUT) { + printf("%s release stack %p\n", + __func__, + (void *) stack); + } + + return 0; +} + +/** + * Respond to events for the connection + * + * Typcially, the forwarder connector will block and unblock the DOWN direction. We need + * to stop putting new data in the down directon if its blocked. + * + * The API connector (us) is generally the thing blocking the UP direction, so we don't need + * to respond to those (our own) events. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * ComponentOperations api_ops = { + * // [other settings] + * .stateChange = connector_Api_StateChange + * }; + * } + * @endcode + */ +static void +connector_Api_StateChange(RtaConnection *conn) +{ + RtaApiConnection *apiConnection = rtaConnection_GetPrivateData(conn, API_CONNECTOR); + + // we do not test the rtaConnection_BlockedUp() because we are the one setting those + + // If we are blocked in the DOWN direction, disable events on the read queue + if (rtaConnection_BlockedDown(conn)) { + rtaApiConnection_BlockDown(apiConnection); + } else { + rtaApiConnection_UnblockDown(apiConnection); + } +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Api.h b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Api.h new file mode 100644 index 00000000..6299116b --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Api.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef Libccnx_connector_api_h +#define Libccnx_connector_api_h + +// Function structs for component variations +extern RtaComponentOperations api_ops; +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder.h b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder.h new file mode 100644 index 00000000..64a3c6e9 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// connector_Forwarder.h +// Libccnx +// +// + +#ifndef Libccnx_connector_fwd_h +#define Libccnx_connector_fwd_h + +// Function structs for component variations +extern RtaComponentOperations fwd_flan_ops; +extern RtaComponentOperations fwd_local_ops; +extern RtaComponentOperations fwd_tlvrtr_ops; +extern RtaComponentOperations fwd_metis_ops; +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder_Local.c b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder_Local.c new file mode 100644 index 00000000..a434600a --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder_Local.c @@ -0,0 +1,552 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * PF_LOCAL forwarder glue, mostly for testing. This uses a + * STREAM socket with a user specified coding. Each message + * on the stream is of this format: + * + * uint32_t process pid + * uint32_t user_socket_fd + * uint32_t message bytes that follow + * uint8_t[] message encoded with user specified codec + * + * The user_socket_fd will be the same number that the API was assigned + * in transportRta_Socket->api_socket_pair[PAIR_OTHER]. + * + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <parc/algol/parc_EventBuffer.h> + +#include <LongBow/runtime.h> +#include <LongBow/debugging.h> + +#include <parc/algol/parc_Memory.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework_Services.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> +#include <ccnx/transport/transport_rta/connectors/connector_Forwarder.h> + +#include <ccnx/transport/transport_rta/config/config_Forwarder_Local.h> +#include <ccnx/api/control/controlPlaneInterface.h> +#include <ccnx/api/control/cpi_ControlFacade.h> + +#include <ccnx/common/ccnx_WireFormatMessage.h> + +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +static int connector_Fwd_Local_Init(RtaProtocolStack *stack); +static int connector_Fwd_Local_Opener(RtaConnection *conn); +static void connector_Fwd_Local_Upcall_Read(PARCEventQueue *, PARCEventType, void *conn); +static void connector_Fwd_Local_Upcall_Event(PARCEventQueue *, PARCEventQueueEventType, void *stack); +static void connector_Fwd_Local_Downcall_Read(PARCEventQueue *, PARCEventType, void *conn); +static int connector_Fwd_Local_Closer(RtaConnection *conn); +static int connector_Fwd_Local_Release(RtaProtocolStack *stack); +static void connector_Fwd_Local_StateChange(RtaConnection *conn); + +RtaComponentOperations fwd_local_ops = { + .init = connector_Fwd_Local_Init, + .open = connector_Fwd_Local_Opener, + .upcallRead = connector_Fwd_Local_Upcall_Read, + .upcallEvent = connector_Fwd_Local_Upcall_Event, + .downcallRead = connector_Fwd_Local_Downcall_Read, + .downcallEvent = NULL, + .close = connector_Fwd_Local_Closer, + .release = connector_Fwd_Local_Release, + .stateChange = connector_Fwd_Local_StateChange +}; + +struct fwd_local_state { + int fd; + PARCEventQueue *bev_local; + int connected; +}; + +typedef struct { + uint32_t pid; + uint32_t fd; + uint32_t length; + uint32_t pad; // make it 16 bytes +} __attribute__ ((packed)) localhdr; + +// ================================ +// NULL + +static int +connector_Fwd_Local_Init(RtaProtocolStack *stack) +{ + // no stack-wide initialization + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s init stack %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(stack)), + __func__, + (void *) stack); + } + return 0; +} + +/* + * Create a PF_LOCAL socket + * Set it non-blocking + * Wrap it in a buffer event + * Set Read and Event callbacks + * connect to LOCAL_NAME + * + * Return 0 success, -1 failure + */ +static int +connector_Fwd_Local_Opener(RtaConnection *conn) +{ + PARCEventScheduler *base; + RtaProtocolStack *stack; + const char *sock_name; + + stack = rtaConnection_GetStack(conn); + base = rtaFramework_GetEventScheduler(rtaProtocolStack_GetFramework(stack)); + + sock_name = localForwarder_GetPath(rtaConnection_GetParameters(conn)); + assertNotNull(sock_name, "connector_Fwd_Local_Opener called without setting LOCAL_NAME"); + + if (sock_name == NULL) { + return -1; + } + + struct fwd_local_state *fwd_state = parcMemory_Allocate(sizeof(struct fwd_local_state)); + assertNotNull(fwd_state, "parcMemory_Allocate(%zu) returned NULL", sizeof(struct fwd_local_state)); + + rtaConnection_SetPrivateData(conn, FWD_LOCAL, fwd_state); + + fwd_state->fd = socket(PF_LOCAL, SOCK_STREAM, 0); + if (fwd_state->fd < 0) { + perror("socket PF_LOCAL"); + } + assertFalse(fwd_state->fd < 0, "socket PF_LOCAL error"); + + struct sockaddr_un addr_unix; + memset(&addr_unix, 0, sizeof(struct sockaddr_un)); + addr_unix.sun_family = AF_UNIX; + + trapIllegalValueIf(sizeof(addr_unix.sun_path) <= strlen(sock_name), "sock_name too long, maximum length %zu", sizeof(addr_unix.sun_path) - 1); + strcpy(addr_unix.sun_path, sock_name); + + // Setup the socket as non-blocking then wrap in a parcEventQueue. + + int flags = fcntl(fwd_state->fd, F_GETFL, NULL); + assertFalse(flags < 0, "fcntl failed to obtain file descriptor flags (%d)\n", errno); + + int failure = fcntl(fwd_state->fd, F_SETFL, flags | O_NONBLOCK); + assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno); + + assertTrue(failure == 0, "could not make socket non-blocking"); + if (failure < 0) { + rtaConnection_SetPrivateData(conn, FWD_LOCAL, NULL); + close(fwd_state->fd); + parcMemory_Deallocate((void **) &fwd_state); + return -1; + } + + fwd_state->bev_local = parcEventQueue_Create(base, fwd_state->fd, PARCEventQueueOption_CloseOnFree); + + assertNotNull(fwd_state->bev_local, "Null buffer event for local socket."); + + parcEventQueue_SetCallbacks(fwd_state->bev_local, + connector_Fwd_Local_Upcall_Read, + NULL, + connector_Fwd_Local_Upcall_Event, + conn); + + parcEventQueue_Enable(fwd_state->bev_local, PARCEventType_Read); + + memset(&addr_unix, 0, sizeof(addr_unix)); + addr_unix.sun_family = AF_UNIX; + + trapIllegalValueIf(sizeof(addr_unix.sun_path) <= strlen(sock_name), "sock_name too long, maximum length %zu", sizeof(addr_unix.sun_path) - 1); + strcpy(addr_unix.sun_path, sock_name); + + // This will deliver a PARCEventQueue_Connected on connect success + if (parcEventQueue_ConnectSocket(fwd_state->bev_local, + (struct sockaddr*) &addr_unix, + (socklen_t) sizeof(addr_unix)) < 0) { + perror("connect PF_LOCAL"); + assertTrue(0, "connect PF_LOCAL"); + rtaConnection_SetPrivateData(conn, FWD_LOCAL, NULL); + close(fwd_state->fd); + parcMemory_Deallocate((void **) &fwd_state); + return -1; + } + + // Socket will be ready for use once we get PARCEventQueueEventType_Connected + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s open conn %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn); + } + + return 0; +} + +/* + * Read from bev_local. We are passed the connection on the ptr. + */ +static void +connector_Fwd_Local_Upcall_Read(PARCEventQueue *bev, PARCEventType type, void *ptr) +{ + RtaConnection *conn = (RtaConnection *) ptr; + RtaProtocolStack *stack = rtaConnection_GetStack(conn); + PARCEventBuffer *in = parcEventBuffer_GetQueueBufferInput(bev); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(stack, FWD_LOCAL, RTA_UP); + RtaComponentStats *stats = rtaConnection_GetStats(conn, FWD_LOCAL); + TransportMessage *tm; + + unsigned char *mem; + int res; + + // only move forward if enough bytes available + + while (parcEventBuffer_GetLength(in) >= sizeof(localhdr)) { + size_t msg_length; + + mem = parcEventBuffer_Pullup(in, sizeof(localhdr)); + if (mem == NULL) { + // not enough bytes + parcEventBuffer_Destroy(&in); + return; + } + + msg_length = ((localhdr *) mem)->length; + if (parcEventBuffer_GetLength(in) < msg_length + sizeof(localhdr)) { + // not enough bytes + parcEventBuffer_Destroy(&in); + return; + } + + PARCBuffer *wireFormat = parcBuffer_Allocate(msg_length); + assertNotNull(wireFormat, "parcBuffer_Allocate(%zu) returned NULL", msg_length); + + rtaComponentStats_Increment(stats, STATS_UPCALL_IN); + + // we can read a whole message. Read it directly in to a buffer + // Skip the FWD_LOCAL header + res = parcEventBuffer_Read(in, NULL, sizeof(localhdr)); + assertTrue(res == 0, "Got error draining header from buffer"); + + uint8_t *overlay = parcBuffer_Overlay(wireFormat, msg_length); + res = parcEventBuffer_Read(in, overlay, msg_length); + overlay = NULL; + + assertTrue(res == msg_length, + "parcEventBuffer_Read returned wrong size, expected %zu got %d", + msg_length, res); + + parcBuffer_Flip(wireFormat); + + if (rtaConnection_GetState(conn) == CONN_OPEN) { + CCNxWireFormatMessage *wireFormatMessage = ccnxWireFormatMessage_Create(wireFormat); + CCNxTlvDictionary *dictionary = ccnxWireFormatMessage_GetDictionary(wireFormatMessage); + if (dictionary != NULL) { + // wrap it for transport module + tm = transportMessage_CreateFromDictionary(dictionary); + + // add the connection info to the transport message before sending up stack + transportMessage_SetInfo(tm, rtaConnection_Copy(conn), rtaConnection_FreeFunc); + + // send it up the stack + if (rtaComponent_PutMessage(out, tm)) { + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + } + + // Now release our hold on the wireFormatMessage (aka dictionary) + ccnxWireFormatMessage_Release(&wireFormatMessage); + } else { + printf("Failed to create CCNxTlvDictionary from wireformat\n"); + parcBuffer_Display(wireFormat, 3); + } + } else { + //drop packets + } + + parcBuffer_Release(&wireFormat); + } + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s total upcall reads in %" PRIu64 " out %" PRIu64 "\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaComponentStats_Get(stats, STATS_UPCALL_IN), + rtaComponentStats_Get(stats, STATS_UPCALL_OUT)); + } + parcEventBuffer_Destroy(&in); +} + +/* + * Event on connection to forwarder. + * Passed the RtaConnection in the pointer + */ +static void +connector_Fwd_Local_Upcall_Event(PARCEventQueue *queue, PARCEventQueueEventType events, void *ptr) +{ + RtaConnection *conn = (RtaConnection *) ptr; + + struct fwd_local_state *fwd_state = rtaConnection_GetPrivateData(conn, FWD_LOCAL); + + if (events & PARCEventQueueEventType_Connected) { + if (DEBUG_OUTPUT) { + struct timeval tv; + gettimeofday(&tv, NULL); + printf("%6lu.%06ld %s (pid %d) connected socket %d\n", + tv.tv_sec, (long) tv.tv_usec, + __func__, + getpid(), + rtaConnection_GetTransportFd(conn)); + } + + fwd_state->connected = 1; + rtaConnection_SendStatus(conn, FWD_LOCAL, RTA_UP, notifyStatusCode_CONNECTION_OPEN, NULL, NULL); + } else if (events & PARCEventQueueEventType_Error) { + struct timeval tv; + gettimeofday(&tv, NULL); + + longBowRuntime_StackTrace(1); + + if (events & PARCEventQueueEventType_Reading) { + printf("%6lu.%06ld %s (pid %d) Got read error on PF_LOCAL, transport socket %d: (%d) %s\n", + tv.tv_sec, (long) tv.tv_usec, + __func__, + getpid(), + rtaConnection_GetTransportFd(conn), + errno, + strerror(errno)); + } else if (events & PARCEventQueueEventType_Writing) { + printf("%6lu.%06ld %s (pid %d) Got write error on PF_LOCAL, transport socket %d: (%d) %s\n", + tv.tv_sec, (long) tv.tv_usec, + __func__, + getpid(), + rtaConnection_GetTransportFd(conn), + errno, + strerror(errno)); + } else { + printf("%6lu.%06ld %s (pid %d) Got error on PF_LOCAL, transport socket %d: (%d) %s\n", + tv.tv_sec, (long) tv.tv_usec, + __func__, + getpid(), + rtaConnection_GetTransportFd(conn), + errno, + strerror(errno)); + } + + /* An error occured while connecting. */ + rtaConnection_SendStatus(conn, FWD_LOCAL, RTA_UP, notifyStatusCode_FORWARDER_NOT_AVAILABLE, NULL, NULL); + } +} + +static void +_ackRequest(RtaConnection *conn, PARCJSON *request) +{ + PARCJSON *response = cpiAcks_CreateAck(request); + CCNxTlvDictionary *ackDict = ccnxControlFacade_CreateCPI(response); + + TransportMessage *tm_ack = transportMessage_CreateFromDictionary(ackDict); + ccnxTlvDictionary_Release(&ackDict); + parcJSON_Release(&response); + + transportMessage_SetInfo(tm_ack, rtaConnection_Copy(conn), rtaConnection_FreeFunc); + + RtaProtocolStack *stack = rtaConnection_GetStack(conn); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(stack, FWD_LOCAL, RTA_UP); + if (rtaComponent_PutMessage(out, tm_ack)) { + RtaComponentStats *stats = rtaConnection_GetStats(conn, FWD_LOCAL); + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + } +} + +static void +connector_Fwd_Local_ProcessControl(RtaConnection *conn, TransportMessage *tm) +{ + CCNxTlvDictionary *controlDictionary = transportMessage_GetDictionary(tm); + + if (ccnxControlFacade_IsCPI(controlDictionary)) { + PARCJSON *json = ccnxControlFacade_GetJson(controlDictionary); + if (controlPlaneInterface_GetCPIMessageType(json) == CPI_REQUEST) { + if (cpi_getCPIOperation2(json) == CPI_PAUSE) { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s conn %p recieved PAUSE\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn); + } + _ackRequest(conn, json); + } else if (cpi_getCPIOperation2(json) == CPI_FLUSH) { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s conn %p recieved FLUSH\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn); + } + _ackRequest(conn, json); + } else { + // some other message. We just ACK everything in the local connector. + _ackRequest(conn, json); + } + } + } +} + +static void +connector_Fwd_Local_WriteIovec(struct fwd_local_state *fwdConnState, RtaConnection *conn, CCNxCodecNetworkBufferIoVec *vec, RtaComponentStats *stats) +{ + localhdr lh; + + memset(&lh, 0, sizeof(localhdr)); + lh.pid = getpid(); + lh.fd = rtaConnection_GetTransportFd(conn); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s total downcall reads %" PRIu64 "\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaComponentStats_Get(stats, STATS_DOWNCALL_IN)); + } + + int iovcnt = ccnxCodecNetworkBufferIoVec_GetCount(vec); + const struct iovec *array = ccnxCodecNetworkBufferIoVec_GetArray(vec); + + lh.length = 0; + for (int i = 0; i < iovcnt; i++) { + lh.length += array[i].iov_len; + } + + if (parcEventQueue_Write(fwdConnState->bev_local, &lh, sizeof(lh)) < 0) { + trapUnrecoverableState("%s error writing to bev_local", __func__); + } + + for (int i = 0; i < iovcnt; i++) { + if (parcEventQueue_Write(fwdConnState->bev_local, array[i].iov_base, array[i].iov_len) < 0) { + trapUnrecoverableState("%s error writing iovec to bev_local", __func__); + } + } +} + +/* send raw packet from codec to forwarder */ +static void +connector_Fwd_Local_Downcall_Read(PARCEventQueue *in, PARCEventType event, void *ptr) +{ + TransportMessage *tm; + + while ((tm = rtaComponent_GetMessage(in)) != NULL) { + RtaConnection *conn; + struct fwd_local_state *fwdConnState; + RtaComponentStats *stats; + + CCNxTlvDictionary *messageDictionary = transportMessage_GetDictionary(tm); + + conn = rtaConnection_GetFromTransport(tm); + fwdConnState = rtaConnection_GetPrivateData(conn, FWD_LOCAL); + stats = rtaConnection_GetStats(conn, FWD_LOCAL); + rtaComponentStats_Increment(stats, STATS_DOWNCALL_IN); + + // ignore configuration messages for the send + if (ccnxTlvDictionary_IsControl(messageDictionary)) { + connector_Fwd_Local_ProcessControl(conn, tm); + } else { + CCNxCodecNetworkBufferIoVec *vec = ccnxWireFormatMessage_GetIoVec(messageDictionary); + assertNotNull(vec, "%s got null wire format\n", __func__); + + connector_Fwd_Local_WriteIovec(fwdConnState, conn, vec, stats); + + rtaComponentStats_Increment(stats, STATS_DOWNCALL_OUT); + } + + // we can release everything here. connector_Fwd_Local_WriteIovec made its own references + // to the wire format if it needed them. + transportMessage_Destroy(&tm); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s total downcall reads in %" PRIu64 " out %" PRIu64 "\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaComponentStats_Get(stats, STATS_DOWNCALL_IN), + rtaComponentStats_Get(stats, STATS_DOWNCALL_OUT)); + } + } +} + +static int +connector_Fwd_Local_Closer(RtaConnection *conn) +{ + struct fwd_local_state *fwd_state = rtaConnection_GetPrivateData(conn, FWD_LOCAL); + RtaComponentStats *stats; + + assertNotNull(fwd_state, "invalid state"); + assertNotNull(fwd_state->bev_local, "invalid PARCEventQueue pointer"); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s called on fwd_state %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), __func__, (void *) fwd_state); + } + + stats = rtaConnection_GetStats(conn, FWD_LOCAL); + + // this will close too + parcEventQueue_Destroy(&(fwd_state->bev_local)); + memset(fwd_state, 0, sizeof(struct fwd_local_state)); + parcMemory_Deallocate((void **) &fwd_state); + + rtaConnection_SetPrivateData(conn, FWD_LOCAL, NULL); + rtaComponentStats_Increment(stats, STATS_CLOSES); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s closed fwd_state %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), __func__, (void *) fwd_state); + } + + return 0; +} + +static int +connector_Fwd_Local_Release(RtaProtocolStack *stack) +{ + // no stack-wide initialization + if (DEBUG_OUTPUT) { + printf("%s release stack %p\n", + __func__, + (void *) stack); + } + + return 0; +} + +static void +connector_Fwd_Local_StateChange(RtaConnection *conn) +{ + //not implemented +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder_Metis.c b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder_Metis.c new file mode 100644 index 00000000..59ad1dcb --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder_Metis.c @@ -0,0 +1,1712 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The metis connector does the following per connection: + * - Opens a TCP socket to Metis + * - Creates an "event" for the socket, does not use the buffer to avoid doing extra copy. + * - On read events, uses direct socket operations to read in data + * + * - DOES NOT HANDLE FRAMING ERRORS. If somehow metis and the connector get + * out of whack (technical term), there is no recovery. + * + * - The connection to metis is started in the Opener, but may not complete by the time + * the user sends data down in the Downcall_Read. We should not process the Downcall_Read + * until we get the Upcall_Event of connected. When we finally get the connected event, + * we should make the Downcall_Read pending again (or just call it) to flush the pending + * user data out to metis. + * + * - Because of how we get scheduled, there might be a large batch of messages waiting at the + * forwarder. We don't want to put a giant blob up the stack. So, we keep a deque of TransportMessage + * and only feed a few at a time up. + * + * - Accepts both a PARCBuffer or a CCNxCodecNetworkBufferIoVec as the wire format in the DOWN direction. + * - The UP direction is always a PARCBuffer right now + * + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <arpa/inet.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netdb.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Deque.h> +#include <parc/algol/parc_EventBuffer.h> +#include <parc/algol/parc_EventTimer.h> +#include <parc/algol/parc_Network.h> + +#include <ccnx/transport/common/transport_Message.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework_Services.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> + +#include "connector_Forwarder.h" + +#include <ccnx/transport/transport_rta/config/config_Forwarder_Metis.h> + +#include <ccnx/api/control/controlPlaneInterface.h> +#include <ccnx/api/control/cpi_ControlFacade.h> + +#include <ccnx/common/codec/ccnxCodec_TlvEncoder.h> +#include <ccnx/common/codec/ccnxCodec_TlvDecoder.h> +#include <ccnx/common/internal/ccnx_TlvDictionary.h> + +#include <ccnx/common/codec/ccnxCodec_TlvPacket.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> + +#include <ccnx/common/ccnx_WireFormatMessage.h> + +#define MINIMUM_READ_LENGTH 8 + +// The message type for a Metis control packet +#define METIS_CONTROL_TYPE 0xA4 + +// at most 10MB, this is used as the output buffer down to metis +#define METIS_OUTPUT_QUEUE_BYTES (10 * 1024 * 1024) + +// How big should we try to make the output socket size? +#define METIS_SEND_SOCKET_BUFFER 65536 + +// Maximum input backlog in messages, not bytes +#define METIS_INPUT_QUEUE_MESSAGES 100 + +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +static int connector_Fwd_Metis_Init(RtaProtocolStack *stack); +static int connector_Fwd_Metis_Opener(RtaConnection *conn); + +static void _eventCallback(int fd, PARCEventType what, void *connectionVoid); +static void connector_Fwd_Metis_Dequeue(int fd, PARCEventType which_event, void *metisStateVoid); + +static void connector_Fwd_Metis_Downcall_Read(PARCEventQueue *, PARCEventType, void *conn); +static int connector_Fwd_Metis_Closer(RtaConnection *conn); +static int connector_Fwd_Metis_Release(RtaProtocolStack *stack); +static void connector_Fwd_Metis_StateChange(RtaConnection *conn); + +RtaComponentOperations fwd_metis_ops = { + .init = connector_Fwd_Metis_Init, + .open = connector_Fwd_Metis_Opener, + .upcallRead = NULL, + .upcallEvent = NULL, + .downcallRead = connector_Fwd_Metis_Downcall_Read, + .downcallEvent = NULL, + .close = connector_Fwd_Metis_Closer, + .release = connector_Fwd_Metis_Release, + .stateChange = connector_Fwd_Metis_StateChange +}; + +typedef enum { + PacketType_Interest, + PacketType_ContentObject, + PacketType_Control, + PacketType_InterestReturn, + PacketType_Unknown +} _PacketType; + +typedef struct metis_connector_stats { + unsigned countUpcallReads; + unsigned countUpcallWriteDataOk; + unsigned countUpcallWriteDataError; + unsigned countUpcallWriteDataBlocked; + unsigned countUpcallWriteDataQueueFull; + + unsigned countUpcallWriteControlOk; + unsigned countUpcallWriteControlError; + + unsigned countDowncallReads; + unsigned countDowncallWrites; + unsigned countDowncallControl; +} _MetisConnectorStats; + +/** + * This structure holds the read-ahead data for the next message being read based + * on its fixed header + */ +typedef struct next_message_header { + // this is how we frame received messages on a stream connection. We + // wait until we read a complete fixed header, then we can set the length + // of that message and keep waiting until we receive at least that many bytes. + size_t length; + + // at the time when we parse out the message length from the fixed header, + // we also parse out the TLV message type from the fixed header + _PacketType packetType; + uint8_t version; + + // we will read bytes into this structure + union _hdr { + CCNxCodecSchemaV1FixedHeader v1; + uint8_t buffer[MINIMUM_READ_LENGTH]; + } fixedHeader; + + uint8_t *readLocation; + size_t remainingReadLength; + + // The whole message + PARCBuffer *packet; +} NextMessage; + +typedef struct fwd_metis_state { + uint16_t port; + int fd; + + // separate events for read and write on fd so we can individually enable them + PARCEvent *readEvent; + PARCEvent *writeEvent; + + bool isConnected; + + // This is our read-ahead of the next message fixed header + NextMessage nextMessage; + + // the transportMessageQueueEvent is used to dequeue from the queue. + // we make sure its scheduled so long as there's messages in the queue, even if there's + // nothing else being read + PARCDeque *transportMessageQueue; + PARCEventTimer *transportMessageQueueEvent; + + // This buffer is the queue of stuff we need to send to the network + PARCEventBuffer *metisOutputQueue; + + _MetisConnectorStats stats; +} FwdMetisState; + +/** + * @typedef PacketData + * @brief Used to pass a record between reading a packet and sending it up the stack + * @discussion Used internally to pass data between functions + */ +typedef struct packet_data { + FwdMetisState *fwd_state; + RtaConnection *conn; + PARCEventQueue *out; + RtaComponentStats *stats; +} PacketData; + + +// for debugging +static unsigned fwd_metis_references_queued = 0; +static unsigned fwd_metis_references_dequeued = 0; +static unsigned fwd_metis_references_notqueued = 0; + + +typedef enum { + ReadReturnCode_Finished, // read all needed bytes + ReadReturnCode_PartialRead, // still need some bytes + ReadReturnCode_Closed, // the socket is closed + ReadReturnCode_Error, // An error on the socket +} ReadReturnCode; + +// ================================ + +static void +_nextMessage_Display(const NextMessage *next, unsigned indent) +{ + printf("NextMessage %p length %zu type %d version %u readLocation %p remaining %zu\n", + (void *) next, next->length, next->packetType, next->version, (void *) next->readLocation, next->remainingReadLength); + + printf("fixedHeader\n"); + longBowDebug_MemoryDump((const char *) next->fixedHeader.buffer, MINIMUM_READ_LENGTH); + + if (next->packet) { + parcBuffer_Display(next->packet, 3); + } +} + +static int +connector_Fwd_Metis_Init(RtaProtocolStack *stack) +{ + struct sigaction ignore_action; + ignore_action.sa_handler = SIG_IGN; + sigemptyset(&ignore_action.sa_mask); + ignore_action.sa_flags = 0; + sigaction(SIGPIPE, &ignore_action, NULL); + + return 0; +} + + +/** + * Setup the NextMessage structure to begin reading a fixed header + * + * All fields are zeroed and the readLocation is set to the first byte of the fixedHeader. + * The remainingReadLength is set to the size of the fixedHeader. + * + * @param [in] next An allocated NextMessage to initialize + * + * Example: + * @code + * { + * NextMessage nextMessage; + * _initializeNextMessage(&nextMessage); + * } + * @endcode + */ +static void +_initializeNextMessage(NextMessage *next) +{ + memset(next, 0, sizeof(NextMessage)); + next->version = 0xFF; + next->packetType = PacketType_Unknown; + next->readLocation = next->fixedHeader.buffer; + next->remainingReadLength = MINIMUM_READ_LENGTH; +} + +static FwdMetisState * +connector_Fwd_Metis_CreateConnectionState(PARCEventScheduler *scheduler) +{ + FwdMetisState *fwd_state = parcMemory_Allocate(sizeof(FwdMetisState)); + assertNotNull(fwd_state, "parcMemory_Allocate(%zu) returned NULL", sizeof(FwdMetisState)); + + memset(fwd_state, 0, sizeof(FwdMetisState)); + _initializeNextMessage(&fwd_state->nextMessage); + + fwd_state->fd = 0; + fwd_state->readEvent = NULL; + fwd_state->writeEvent = NULL; + fwd_state->transportMessageQueue = parcDeque_Create(); + fwd_state->transportMessageQueueEvent = parcEventTimer_Create(scheduler, 0, connector_Fwd_Metis_Dequeue, fwd_state); + fwd_state->isConnected = false; + fwd_state->metisOutputQueue = parcEventBuffer_Create(); + + return fwd_state; +} + +static bool +_openSocket(FwdMetisState *fwd_state, uint16_t port) +{ + fwd_state->port = port; + fwd_state->fd = socket(PF_INET, SOCK_STREAM, 0); + + if (fwd_state->fd < 0) { + if (DEBUG_OUTPUT) { + printf("%9c %s failed to open PF_INET SOCK_STREAM socket: (%d) %s\n", + ' ', __func__, errno, strerror(errno)); + } + return false; + } + + if (DEBUG_OUTPUT) { + printf("%9c %s create socket %d port %u\n", + ' ', __func__, fwd_state->fd, fwd_state->port); + } + + return true; +} + +/** + * @function connector_Fwd_Metis_SetupSocket + * @abstract Creates the socket and sets the port, but does not call connect + * @discussion + * Creates and sets up the socket descriptor. makes it non-blocking. + * Sets the port in FwdMetisState. + * + * This is a full PF_INET socket, not forced to PF_LOCAL. + * + * The sendbuffer size is set to METIS_OUTPUT_QUEUE_BYTES + * + * precondition: called _openSocket + * + * @param <#param1#> + * @return <#return#> + */ +static bool +_setupSocket(FwdMetisState *fwd_state) +{ + trapUnexpectedStateIf(fwd_state->fd < 1, "Invalid socket %d", fwd_state->fd); + + // Set non-blocking flag + int flags = fcntl(fwd_state->fd, F_GETFL, NULL); + assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno); + int res = fcntl(fwd_state->fd, F_SETFL, flags | O_NONBLOCK); + + if (res < 0) { + if (DEBUG_OUTPUT) { + printf("%9c %s failed to make socket non-blocking: (%d) %s\n", + ' ', __func__, errno, strerror(errno)); + } + + close(fwd_state->fd); + return false; + } + + const int sendBufferSize = METIS_SEND_SOCKET_BUFFER; + res = setsockopt(fwd_state->fd, SOL_SOCKET, SO_SNDBUF, &sendBufferSize, sizeof(int)); + if (res < 0) { + if (DEBUG_OUTPUT) { + printf("%9c %s failed to set SO_SNDBUF to %d: (%d) %s\n", + ' ', __func__, sendBufferSize, errno, strerror(errno)); + } + // This is a non-fatal error + } + +#if defined(SO_NOSIGPIPE) + // turn off SIGPIPE, return EPIPE + const int on = 1; + res = setsockopt(fwd_state->fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); + if (res < 0) { + if (DEBUG_OUTPUT) { + printf("%9c %s failed to set SO_NOSIGPIPE to %d: (%d) %s\n", + ' ', __func__, sendBufferSize, errno, strerror(errno)); + } + // this is not a fatal error, so keep going + } +#endif + + return true; +} + +/** + * @function connector_Fwd_Metis_SetupConnectionBuffer + * @abstract Creates the connection buffer and adds it to libevent + * @discussion + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + */ +static bool +_setupSocketEvents(FwdMetisState *fwd_state, RtaConnection *conn) +{ + RtaProtocolStack *stack = rtaConnection_GetStack(conn); + PARCEventScheduler *scheduler = rtaFramework_GetEventScheduler(rtaProtocolStack_GetFramework(stack)); + + // the connect() call will be asynchrnous because the socket is non-blocking, so we + // need ET_WRITE to trigger a callback when the socket becomes writable (i.e. connected). + // If there's an error on connect it will be an ET_READ | ET_WRITE event with an error on the socket. + fwd_state->readEvent = parcEvent_Create(scheduler, fwd_state->fd, PARCEventType_Read | PARCEventType_Persist | PARCEventType_EdgeTriggered, _eventCallback, conn); + assertNotNull(fwd_state->readEvent, "Got a null readEvent for socket %d", fwd_state->fd); + + fwd_state->writeEvent = parcEvent_Create(scheduler, fwd_state->fd, PARCEventType_Write | PARCEventType_Persist | PARCEventType_EdgeTriggered, _eventCallback, conn); + assertNotNull(fwd_state->writeEvent, "Got a null readEvent for socket %d", fwd_state->fd); + + // Start the write event. It will be signaled on a connect error or when we are connected. + // The read event is not enabled until after connect. + + int failure = parcEvent_Start(fwd_state->writeEvent); + assertFalse(failure < 0, "Error starting writeEvent event %p: (%d) %s", (void *) fwd_state->writeEvent, errno, strerror(errno)); + + return true; +} + +/** + * The connection to the forwarder succeeded, step the state machine + * + * Change the state of the connection to connected and notify the user that it's ready. + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_connectionSucceeded(FwdMetisState *fwd_state, RtaConnection *conn) +{ + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s Connection %p connected fd %d\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn, fwd_state->fd); + } + + fwd_state->isConnected = true; + + // enable read events + parcEvent_Start(fwd_state->readEvent); + + rtaConnection_SendStatus(conn, FWD_METIS, RTA_UP, notifyStatusCode_CONNECTION_OPEN, NULL, NULL); +} + +static void +_readInEnvironmentConnectionSpecification(struct sockaddr_in *addr_in) +{ + char *forwarderIpEnv = getenv(FORWARDER_CONNECTION_ENV); + if (forwarderIpEnv == NULL) { + return; + } + + char forwarderIpAddress[NI_MAXHOST] = { 0 }; + in_port_t forwarderIpPort = 0; + + // Currently, we only support tcp control connections to the forwarder + sscanf(forwarderIpEnv, "tcp://%[^:]:%hu", forwarderIpAddress, &forwarderIpPort); + + // If provided, use the specified address in a canonical form + if (forwarderIpAddress[0] != '\0') { + // Normalize the provided hostname + struct sockaddr_in *addr = (struct sockaddr_in *) parcNetwork_SockAddress(forwarderIpAddress, forwarderIpPort); + char *ipAddress = inet_ntoa(addr->sin_addr); + parcMemory_Deallocate(&addr); + if (ipAddress) { + addr_in->sin_addr.s_addr = inet_addr(ipAddress); + } else { + addr_in->sin_addr.s_addr = inet_addr(forwarderIpAddress); + } + } + + // If provided, use the specified port + if (forwarderIpPort != 0) { + addr_in->sin_port = htons(forwarderIpPort); + } +} + +/** + * @function connector_Fwd_Metis_BeginConnect + * @abstract Begins the non-blocking connect() call to 127.0.0.1 on the port in FwdMetisState + * @discussion + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + */ +static bool +connector_Fwd_Metis_BeginConnect(FwdMetisState *fwd_state, RtaConnection *conn) +{ + bool success = false; + + struct sockaddr_in addr_in; + memset(&addr_in, 0, sizeof(addr_in)); + addr_in.sin_port = htons(fwd_state->port); + addr_in.sin_family = AF_INET; + addr_in.sin_addr.s_addr = inet_addr("127.0.0.1"); + + // Override defaults if specified + _readInEnvironmentConnectionSpecification(&addr_in); + + if (DEBUG_OUTPUT) { + char inetAddress[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(addr_in.sin_addr), inetAddress, INET_ADDRSTRLEN); + printf("%9" PRIu64 " %s beginning connect socket %d to port %d on %s\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + fwd_state->fd, + fwd_state->port, + inetAddress); + } + + // This will deliver a PARCEventType_Write event on connect success + int res = connect(fwd_state->fd, (struct sockaddr*) &addr_in, (socklen_t) sizeof(addr_in)); + + if (res == 0) { + // connect succeded immediately + _connectionSucceeded(fwd_state, conn); + success = true; + } else if (errno == EINPROGRESS) { + // connection is deferred + success = true; + } else { + // a hard error + printf("Error connecting: (%d) %s\n", errno, strerror(errno)); + } + + return success; +} + +/** + * We maintain an input queue going up the stack and only dequeue a small number of packets + * with each call from the dispatch loop. THis is to avoid bursting a bunch of packets up the stack. + */ +static void +connector_Fwd_Metis_Dequeue(int fd, PARCEventType which_event, void *metisStateVoid) +{ + FwdMetisState *fwd_state = (FwdMetisState *) metisStateVoid; + + // random small number. What is right value for this? + unsigned max_loops = 6; + + if (DEBUG_OUTPUT) { + printf("%9d %s deque size %zu\n", + 0, + __func__, + parcDeque_Size(fwd_state->transportMessageQueue)); + } + + while (max_loops > 0 && !parcDeque_IsEmpty(fwd_state->transportMessageQueue)) { + max_loops--; + TransportMessage *tm = parcDeque_RemoveFirst(fwd_state->transportMessageQueue); + + RtaConnection *conn = rtaConnection_GetFromTransport(tm); + RtaProtocolStack *stack = rtaConnection_GetStack(conn); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(stack, FWD_METIS, RTA_UP); + RtaComponentStats *stats = rtaConnection_GetStats(conn, FWD_METIS); + + if (rtaComponent_PutMessage(out, tm)) { + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + } + } + + // If there are still messages in there, re-schedule + if (!parcDeque_IsEmpty(fwd_state->transportMessageQueue)) { + if (DEBUG_OUTPUT) { + printf("%9d %s rescheduling output queue timer %p\n", + 0, + __func__, + (void *) fwd_state->transportMessageQueueEvent); + } + + struct timeval immediateTimeout = { 0, 0 }; + parcEventTimer_Start(fwd_state->transportMessageQueueEvent, &immediateTimeout); + } +} + +/** + * Create a TCP socket + * Set it non-blocking + * Wrap it in a buffer event + * Set Read and Event callbacks + * + * Return 0 success, -1 failure + */ +static int +connector_Fwd_Metis_Opener(RtaConnection *conn) +{ + bool success = false; + + uint16_t port = metisForwarder_GetPortFromConfig(rtaConnection_GetParameters(conn)); + + PARCEventScheduler *scheduler = rtaFramework_GetEventScheduler(rtaConnection_GetFramework(conn)); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + + if (_openSocket(fwd_state, port)) { + if (_setupSocket(fwd_state)) { + if (_setupSocketEvents(fwd_state, conn)) { + if (connector_Fwd_Metis_BeginConnect(fwd_state, conn)) { + // stash it away in the per-connection cubby hole + rtaConnection_SetPrivateData(conn, FWD_METIS, fwd_state); + success = true; + } + } + } + } + + if (!success) { + if (fwd_state->fd) { + close(fwd_state->fd); + } + if (fwd_state->readEvent) { + parcEvent_Destroy(&(fwd_state->readEvent)); + } + if (fwd_state->writeEvent) { + parcEvent_Destroy(&(fwd_state->writeEvent)); + } + parcMemory_Deallocate((void **) &fwd_state); + return -1; + } + + // Socket will be ready for use once we get PARCEventQueue_Connected + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s open conn %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn); + } + + return 0; +} + +/** + * We received a Metis control packet. Translate it to a control packet and send it up the stack. + */ +static void +receiveControlMessage(PacketData *data) +{ + CCNxTlvDictionary *packetDictionary = + ccnxWireFormatMessage_FromControlPacketType(data->fwd_state->nextMessage.version, data->fwd_state->nextMessage.packet); + + bool success = ccnxCodecTlvPacket_BufferDecode(data->fwd_state->nextMessage.packet, packetDictionary); + + if (success) { + TransportMessage *tm = transportMessage_CreateFromDictionary(packetDictionary); + transportMessage_SetInfo(tm, rtaConnection_Copy(data->conn), rtaConnection_FreeFunc); + + // send it up the stack + if (rtaComponent_PutMessage(data->out, tm)) { + rtaComponentStats_Increment(data->stats, STATS_UPCALL_OUT); + data->fwd_state->stats.countUpcallWriteControlOk++; + } else { + data->fwd_state->stats.countUpcallWriteControlError++; + } + } else { + assertTrue(success, "Error decoding a Metis control packet\n") + { + parcBuffer_Display(data->fwd_state->nextMessage.packet, 3); + } + } + + // we are now done with our references + ccnxTlvDictionary_Release(&packetDictionary); +} + + +static void +_queueNonControl(PacketData *data) +{ + CCNxTlvDictionary *packetDictionary = ccnxWireFormatMessage_Create(data->fwd_state->nextMessage.packet); + + assertNotNull(packetDictionary, "Got a null packet decode") + { + parcBuffer_Display(data->fwd_state->nextMessage.packet, 3); + } + + TransportMessage *tm = transportMessage_CreateFromDictionary(packetDictionary); + + // add the connection info to the transport message before sending up stack + transportMessage_SetInfo(tm, rtaConnection_Copy(data->conn), rtaConnection_FreeFunc); + + parcDeque_Append(data->fwd_state->transportMessageQueue, tm); + + // start if went from emtpy to 1 + if (parcDeque_Size(data->fwd_state->transportMessageQueue) == 1) { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %u schedule dequeue event %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(data->conn))), + __func__, + rtaConnection_GetConnectionId(data->conn), + (void *) data->fwd_state->transportMessageQueueEvent); + } + + struct timeval immediateTimeout = { 0, 0 }; + parcEventTimer_Start(data->fwd_state->transportMessageQueueEvent, &immediateTimeout); + } + + // we are now done with our references + ccnxTlvDictionary_Release(&packetDictionary); +} + +/** + * Receive a non-control packet + * + * Non-control messages may be dropped due to lack of input buffer space. + * If the connection has state Block Up or the up queue's length is + * too many messages deep, the non-control message will be dropped. + * + * precondition: the caller knows the message is not a control message + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_receiveNonControl(PacketData *data) +{ + if (rtaConnection_BlockedUp(data->conn)) { + data->fwd_state->stats.countUpcallWriteDataBlocked++; + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %u blocked up, drop wireFormat %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(data->conn))), + __func__, + rtaConnection_GetConnectionId(data->conn), + (void *) data->fwd_state->nextMessage.packet); + } + } else { + if (parcDeque_Size(data->fwd_state->transportMessageQueue) < METIS_INPUT_QUEUE_MESSAGES) { + _queueNonControl(data); + data->fwd_state->stats.countUpcallWriteDataOk++; + } else { + data->fwd_state->stats.countUpcallWriteDataQueueFull++; + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %u input buffer full, drop wireFormat %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(data->conn))), + __func__, + rtaConnection_GetConnectionId(data->conn), + (void *) data->fwd_state->nextMessage.packet); + } + } + } +} + +/** + * We received an entire packet, send it up the stack in a Transport message. + * + * If its a control message, we make it a CCNxControlMessage here for symmetry with us + * encoding the control messages at this level + */ +static void +connector_Fwd_Metis_SendUpStack(PacketData *data) +{ + // Always send control messages up the stack + if (data->fwd_state->nextMessage.packetType == PacketType_Control) { + receiveControlMessage(data); + } else { + _receiveNonControl(data); + } +} + +/** + * Return the SO_ERROR value for the given socket + * + * If getsockopt returns an error, the return code could be the error from getsockopt. + * + * Typically you will get ECONNREFUSED when you cannot connect and one of the many getsockopt + * errors if there's a problem with the actual socket. + * + * @param [in] fd The socket + * + * @return errno An errno value + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static int +_getSocketError(int fd) +{ + int value; + socklen_t valueLength = sizeof(value); + int res = getsockopt(fd, SOL_SOCKET, SO_ERROR, &value, &valueLength); + if (res < 0) { + value = res; + } + return value; +} + +/** + * Received an event on a socket we have marked as not yet connected + * + * Ether it's ready to go or there's an error. We will receive a PARCEventType_Read and the socket + * will have an SO_ERROR of 0 if it's now connected. If the SO_ERROR is non-zero, there + * was an error on connect. + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_disconnectedEventHandler(FwdMetisState *fwd_state, RtaConnection *conn, PARCEventType what) +{ + if (what & PARCEventType_Read) { + int socketError = _getSocketError(fwd_state->fd); + if (socketError == 0) { + // I don't think these happen, they will be write events + _connectionSucceeded(fwd_state, conn); + } else { + // error on connect + printf("%9" PRIu64 " %s Connection %p got error on SOCK_STREAM, fd %d: %s\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn, + fwd_state->fd, + strerror(errno)); + + // make the event non-pending + parcEvent_Stop(fwd_state->readEvent); + parcEvent_Stop(fwd_state->writeEvent); + + rtaConnection_SetBlockedDown(conn); + + // at least tell the API whats going on + rtaConnection_SendStatus(conn, FWD_METIS, RTA_UP, notifyStatusCode_FORWARDER_NOT_AVAILABLE, NULL, NULL); + } + } + + if (what & PARCEventType_Write) { + int socketError = _getSocketError(fwd_state->fd); + if (socketError == 0) { + _connectionSucceeded(fwd_state, conn); + } + } +} + +static void +_setupNextPacketV1(FwdMetisState *fwd_state) +{ + switch (fwd_state->nextMessage.fixedHeader.v1.packetType) { + case CCNxCodecSchemaV1Types_PacketType_Interest: + fwd_state->nextMessage.packetType = PacketType_Interest; + break; + case CCNxCodecSchemaV1Types_PacketType_ContentObject: + fwd_state->nextMessage.packetType = PacketType_ContentObject; + break; + case CCNxCodecSchemaV1Types_PacketType_Control: + fwd_state->nextMessage.packetType = PacketType_Control; + break; + case CCNxCodecSchemaV1Types_PacketType_InterestReturn: + fwd_state->nextMessage.packetType = PacketType_InterestReturn; + break; + default: + fwd_state->nextMessage.packetType = PacketType_Unknown; + break; + } + + size_t fixedHeaderLength = sizeof(CCNxCodecSchemaV1FixedHeader); + fwd_state->nextMessage.length = htons(fwd_state->nextMessage.fixedHeader.v1.packetLength); + + fwd_state->nextMessage.packet = parcBuffer_Allocate(fwd_state->nextMessage.length); + assertNotNull(fwd_state->nextMessage.packet, "Could not allocate packet of size %zu", fwd_state->nextMessage.length); + + // finally copy in the fixed header as we have already read that in + parcBuffer_PutArray(fwd_state->nextMessage.packet, fixedHeaderLength, fwd_state->nextMessage.fixedHeader.buffer); +} + +/** + * Called after reading whole FixedHeader, will setup the packet buffer + * + * After reading the fixed header, we need to allocate a PARCBuffer for the packet. Setup that + * buffer and copy the FixedHeader in to it. Remaining reads will go in to this buffer. + * + * After this function completes, the parsed version, packetType, and length of the nextMessage will + * be filled in, the packet buffer allocated and the fixedHeader copied to that packet buffer. + * + * precondition: forwarder->nextMessage.remainingReadLength == 0 && fwd_state->nextMessage.packet == NULL + * + * @param [in] fwd_state An allocated forwarder connection state that has read in the fixed header + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_setupNextPacket(FwdMetisState *fwd_state) +{ + trapUnexpectedStateIf(fwd_state->nextMessage.packet != NULL, "Calling _setupNextPacket but the packet field is not NULL"); + + fwd_state->nextMessage.version = fwd_state->nextMessage.fixedHeader.buffer[0]; + + switch (fwd_state->nextMessage.version) { + case 1: + _setupNextPacketV1(fwd_state); + break; + + default: + trapUnexpectedState("Illegal packet version %d", fwd_state->nextMessage.version) + { + _nextMessage_Display(&fwd_state->nextMessage, 0); + } + break; + } +} + +/** + * Reads the FixedHeader. If full read will setup the next packet buffer. + * + * Reads up to FixedHeader length bytes. If read whole header will allocate the next packet + * buffer to right size and copy the Fixed Header in to the buffer. + * + * preconditions: + * - fwd_state->nextMessage.packet should be NULL + * - fwd_state->nextMessage.remainingReadLength should be the remaining bytes to read of the Fixed Header + * - fwd_state->nextMessage.readLocation should point to the location in the FixedHeader to start reading + * + * postconditions: + * - fwd_state->nextMessage.remainingReadLength will be decremented by the amount read + * - If remainingReadLength is decremented to 0, will allocate fwd_state->nextMessage.packet and copy in the FixedHeader + * - The fields in fwd_state->nextMessage (length, packetType, version) will be set based on the fixed header + * + * @param [in] fwd_state An allocated forwarder connection state + * + * @retval ReadReturnCode_Finished one entire packet is ready in the buffer + * @retval ReadReturnCode_PartialRead need more bytes + * @retval ReadRetrunCode_Closed The socket to metis is closed (a special case of Error) + * @retval ReadReturnCode_Error An error occured on the socket to metis + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static ReadReturnCode +_readPacketHeader(FwdMetisState *fwd_state) +{ + ReadReturnCode returnCode = ReadReturnCode_Error; + + // This could be switched to MSG_PEEK instead of copying later, but I don't think it makes any significant change. + ssize_t nread = recv(fwd_state->fd, fwd_state->nextMessage.readLocation, fwd_state->nextMessage.remainingReadLength, 0); + if (nread > 0) { + // recv will always runturn at most fwd_state->nextMessage.remainingReadLength, so this won't wrap around to negative. + fwd_state->nextMessage.remainingReadLength -= nread; + + if (fwd_state->nextMessage.remainingReadLength == 0) { + returnCode = ReadReturnCode_Finished; + _setupNextPacket(fwd_state); + } else { + fwd_state->nextMessage.readLocation += nread; + returnCode = ReadReturnCode_PartialRead; + } + } else if (nread == 0) { + // the connection is closed + returnCode = ReadReturnCode_Closed; + } else { + switch (errno) { + case EAGAIN: + // call would block. These can happen becasue _readMessage is in a while loop and we detect + // the end of the loop because we cannot read another fixed header. + returnCode = ReadReturnCode_PartialRead; + break; + + default: + // an error. I think all errors will be hard errors and we close the connection + if (DEBUG_OUTPUT) { + printf("%9c %s socket %d recv error: (%d) %s\n", + ' ', __func__, fwd_state->fd, errno, strerror(errno)); + } + returnCode = ReadReturnCode_Error; + break; + } + } + + return returnCode; +} + + +/** + * We have finished reading the fixed header, reading the message body + * + * Will modify the nextMessage.packet buffer. When the buffer has 0 remaining, the whole packet has been read + * + * precondition: _readHeaderFromMetis read the header and allocated the packet buffer + * + * @param [in] fwd_state An allocated forwarder connection state + * + * @retval ReadReturnCode_Finished one entire packet is ready in the buffer + * @retval ReadReturnCode_PartialRead need more bytes + * @retval ReadRetrunCode_Closed The socket to metis is closed (a special case of Error) + * @retval ReadReturnCode_Error An error occured on the socket to metis + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static ReadReturnCode +_readPacketBody(FwdMetisState *fwd_state) +{ + ReadReturnCode returnCode = ReadReturnCode_Error; + + trapUnexpectedStateIf(fwd_state->nextMessage.packet == NULL, "Trying to read a message with a null packet buffer"); + + size_t remaining = parcBuffer_Remaining(fwd_state->nextMessage.packet); + + if (DEBUG_OUTPUT) { + printf("%9c %s socket %d read up to %zu bytes\n", + ' ', __func__, fwd_state->fd, remaining); + } + + void *overlay = parcBuffer_Overlay(fwd_state->nextMessage.packet, 0); + ssize_t nread = recv(fwd_state->fd, overlay, remaining, 0); + + if (nread > 0) { + // good read + parcBuffer_SetPosition(fwd_state->nextMessage.packet, parcBuffer_Position(fwd_state->nextMessage.packet) + nread); + + if (nread == remaining) { + returnCode = ReadReturnCode_Finished; + } else { + returnCode = ReadReturnCode_PartialRead; + } + } else if (nread == 0) { + // connection closed + returnCode = ReadReturnCode_Closed; + } else { + switch (errno) { + case EAGAIN: + // call would block. These can happen becasue _readMessage is in a while loop and we detect + // the end of the loop because we cannot read the entire message body. + returnCode = ReadReturnCode_PartialRead; + break; + + default: + // an error. I think all errors will be hard errors and we close the connection + if (DEBUG_OUTPUT) { + printf("%9c %s socket %d recv error: (%d) %s\n", + ' ', __func__, fwd_state->fd, errno, strerror(errno)); + } + returnCode = ReadReturnCode_Error; + } + } + + + if (DEBUG_OUTPUT) { + printf("%9c %s socket %u msg_length %zu read_length %zd remaining %zu\n", + ' ', + __func__, + fwd_state->fd, + fwd_state->nextMessage.length, + nread, + parcBuffer_Remaining(fwd_state->nextMessage.packet)); + } + + return returnCode; +} + +/** + * Read packet from metis + * + * Reads the fixed heder. Once fixed header is done, begins reading the packet body. Keeps + * all the incremental state to do partial reads. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval ReadReturnCode_Finished one entire packet is ready in the buffer + * @retval ReadReturnCode_PartialRead need more bytes + * @retval ReadRetrunCode_Closed The socket to metis is closed (a special case of Error) + * @retval ReadReturnCode_Error An error occured on the socket to metis + * + * Example: + * @code + * <#example#> + * @endcode + */ +static ReadReturnCode +_readPacket(FwdMetisState *fwd_state) +{ + ReadReturnCode returnCode = ReadReturnCode_PartialRead; + + // are we still reading the header? + if (fwd_state->nextMessage.remainingReadLength > 0) { + returnCode = _readPacketHeader(fwd_state); + } else { + returnCode = ReadReturnCode_Finished; + } + + // After reading the header, it may be possible to read the body too + if (returnCode == ReadReturnCode_Finished && fwd_state->nextMessage.remainingReadLength == 0) { + returnCode = _readPacketBody(fwd_state); + } + + return returnCode; +} + +/** + * Read as many packets as we can from Metis + * + * Will read the stream socket from metis until we get a PartialRead return code from + * either the attempt to read the header or the body. + * + * On read error, will send a notification message the connection is closed up to + * the API and will disable read and write events. + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_readFromMetis(FwdMetisState *fwd_state, RtaConnection *conn) +{ + RtaProtocolStack *stack = rtaConnection_GetStack(conn); + RtaComponentStats *stats = rtaConnection_GetStats(conn, FWD_METIS); + + ReadReturnCode readCode; + while ((readCode = _readPacket(fwd_state)) == ReadReturnCode_Finished) { + rtaComponentStats_Increment(stats, STATS_UPCALL_IN); + fwd_state->stats.countUpcallReads++; + + // setup the buffer for reading + parcBuffer_Flip(fwd_state->nextMessage.packet); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s sending packet buffer %p up stack length %zu\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) fwd_state->nextMessage.packet, + parcBuffer_Remaining(fwd_state->nextMessage.packet)); + } + + // this is just to make the signature of connector_Fwd_Metis_SendUpStack tractable, PacketData + // is not exposed outside this scope. + + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(stack, FWD_METIS, RTA_UP); + PacketData data = { + .fwd_state = fwd_state, + .conn = conn, + .out = out, + .stats = stats, + }; + + connector_Fwd_Metis_SendUpStack(&data); + + // done with the packet buffer. Release our hold on it. If it was sent up the stack + // another reference count was made. + parcBuffer_Release(&fwd_state->nextMessage.packet); + + // now setup for next packet + _initializeNextMessage(&fwd_state->nextMessage); + } + + if (readCode == ReadReturnCode_Closed) { + fwd_state->isConnected = false; + parcEvent_Stop(fwd_state->readEvent); + parcEvent_Stop(fwd_state->writeEvent); + rtaConnection_SendStatus(conn, FWD_METIS, RTA_UP, notifyStatusCode_CONNECTION_CLOSED, NULL, "Socket operation returned closed by remote"); + } else if (readCode == ReadReturnCode_Error) { + fwd_state->isConnected = false; + parcEvent_Stop(fwd_state->readEvent); + parcEvent_Stop(fwd_state->writeEvent); + rtaConnection_SendStatus(conn, FWD_METIS, RTA_UP, notifyStatusCode_CONNECTION_CLOSED, NULL, "Socket operation returned error"); + } + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s total upcall reads in %" PRIu64 " out %" PRIu64 "\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaComponentStats_Get(stats, STATS_UPCALL_IN), + rtaComponentStats_Get(stats, STATS_UPCALL_OUT)); + } +} + +/** + * Append a vector to the buffer + * + * @param [in] wireFormat The wire format packet, assumes current position is start of packet + * @param [in] fwd_output The libevent buffer to add the memory reference to + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_queueIoVecMessageToMetis(CCNxCodecNetworkBufferIoVec *vec, PARCEventBuffer *fwd_output) +{ + fwd_metis_references_queued++; + + int iovcnt = ccnxCodecNetworkBufferIoVec_GetCount(vec); + const struct iovec *array = ccnxCodecNetworkBufferIoVec_GetArray(vec); + + for (int i = 0; i < iovcnt; i++) { + if (parcEventBuffer_Append(fwd_output, array[i].iov_base, array[i].iov_len) < 0) { + trapUnrecoverableState("%s error writing to bev_local", __func__); + } + } +} + +/** + * Append to the buffer + * + * @param [in] wireFormat The wire format packet, assumes current position is start of packet + * @param [in] fwd_output The libevent buffer to add the memory reference to + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_queueBufferMessageToMetis(PARCBuffer *wireFormat, PARCEventBuffer *fwd_output) +{ + fwd_metis_references_queued++; + + void *overlay = parcBuffer_Overlay(wireFormat, 0); + size_t length = parcBuffer_Remaining(wireFormat); + + if (parcEventBuffer_Append(fwd_output, overlay, length) < 0) { + trapUnrecoverableState("%s error writing to bev_local", __func__); + } +} + +/** + * Write as much as possible from the output buffer to metis + * + * Write as much as we can to metis. If there is nothing left, deactivate the write event. + * If there is still bytes left in the output buffer, activate the write event. + * + * postconditions: + * - Write as many bytes as possible from the output buffer to metis + * - If there are still bytes remaining, enable the write event + * - If there are no bytes remaining, disable the write event. + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_dequeueMessagesToMetis(FwdMetisState *fwdConnState) +{ + // if we try to write a 0 length buffer, write will return -1 like an error + if (parcEventBuffer_GetLength(fwdConnState->metisOutputQueue) > 0) { + fwdConnState->stats.countDowncallWrites++; + int nwritten = parcEventBuffer_WriteToFileDescriptor(fwdConnState->metisOutputQueue, fwdConnState->fd, -1); + if (nwritten < 0) { + // an error + trapNotImplemented("Bugzid: 2194"); + } + + if (DEBUG_OUTPUT) { + printf("%9c %s wrote %d bytes to socket %d, %zu bytes remaining\n", + ' ', + __func__, + nwritten, + fwdConnState->fd, + parcEventBuffer_GetLength(fwdConnState->metisOutputQueue)); + } + + // if we could not write the whole buffer, make sure we have a write event pending + if (parcEventBuffer_GetLength(fwdConnState->metisOutputQueue) > 0) { + parcEvent_Start(fwdConnState->writeEvent); + if (DEBUG_OUTPUT) { + printf("%9c %s enabled write event\n", ' ', __func__); + } + } else { + parcEvent_Stop(fwdConnState->writeEvent); + if (DEBUG_OUTPUT) { + printf("%9c %s disabled write event\n", ' ', __func__); + } + } + } +} + + +/** + * Called when we get an event on a socket we believe is connected + * + * libevent will call this with an PARCEventType_Read on connection close too (the read length will be 0). + * + * @param [in] fwd_state An allocated forwarder connection state + * @param [in] conn The corresponding RTA connection + * @param [in] what The Libevent set of events + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_connectedEventHandler(FwdMetisState *fwd_state, RtaConnection *conn, short what) +{ + if (what & PARCEventType_Read) { + _readFromMetis(fwd_state, conn); + } + + if (what & PARCEventType_Write) { + _dequeueMessagesToMetis(fwd_state); + } +} + +/** + * Called for any activity on the socket. Maybe in either connected or disconnected state. + */ +static void +_eventCallback(int fd, PARCEventType what, void *connectionVoid) +{ + RtaConnection *conn = (RtaConnection *) connectionVoid; + FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);; + + if (!fwd_state->isConnected) { + _disconnectedEventHandler(fwd_state, conn, what); + + // once we connect, we should try a read immediately too + } + + if (fwd_state->isConnected) { + _connectedEventHandler(fwd_state, conn, what); + } +} + +/** + * Updates the connections's Blocked Down state + * + * If the bytes in our output buffer are greater than METIS_OUTPUT_QUEUE_BYTES, then + * we will set the Blocked Down condition on the connection. This will prevent the + * API connector from accepting more messages. + * + * Messages already in the connection queue will still be processed. + * + * @param [in] fwd_output The libevent buffer to check the backlog + * @param [in] conn The RtaConnection the set or clear the blocked down condition + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_updateBlockedDownState(PARCEventBuffer *fwd_output, RtaConnection *conn) +{ + size_t queue_bytes = parcEventBuffer_GetLength(fwd_output); + if (queue_bytes > METIS_OUTPUT_QUEUE_BYTES) { + // block down + + if (!rtaConnection_BlockedDown(conn)) { + rtaConnection_SetBlockedDown(conn); + } + + // note that we continue execution and put the packet we have in hand on the queue + // setting the blocked down state only affects the API connector. Packets already in the system + // will keep flowing down to us + } else { + // if it is blocked, unblock it + if (rtaConnection_BlockedDown(conn)) { + rtaConnection_ClearBlockedDown(conn); + } + } +} + +static void +connector_Fwd_Metis_Downcall_HandleConnected(FwdMetisState *fwdConnState, TransportMessage *tm, RtaConnection *conn, RtaComponentStats *stats) +{ + _updateBlockedDownState(fwdConnState->metisOutputQueue, conn); + + CCNxTlvDictionary *dictionary = transportMessage_GetDictionary(tm); + + bool queued = false; + + CCNxCodecNetworkBufferIoVec *vec = ccnxWireFormatMessage_GetIoVec(dictionary); + if (vec != NULL) { + _queueIoVecMessageToMetis(vec, fwdConnState->metisOutputQueue); + queued = true; + } else { + PARCBuffer *wireFormat = ccnxWireFormatMessage_GetWireFormatBuffer(dictionary); + if (wireFormat != NULL) { + _queueBufferMessageToMetis(wireFormat, fwdConnState->metisOutputQueue); + queued = true; + } + } + + if (queued) { + rtaComponentStats_Increment(stats, STATS_DOWNCALL_OUT); + + if (DEBUG_OUTPUT) { + struct timeval delay = transportMessage_GetDelay(tm); + printf("%9" PRIu64 " %s total downcall reads %" PRIu64 " references queued %u dequeued %u not queued %u last delay %.6f\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaComponentStats_Get(stats, STATS_DOWNCALL_IN), + fwd_metis_references_queued, + fwd_metis_references_dequeued, + fwd_metis_references_notqueued, + delay.tv_sec + delay.tv_usec * 1E-6); + } + } else { + fwd_metis_references_notqueued++; + } + + // The transport message is destroyed in connector_Fwd_Metis_Downcall_Read() +} + +static void +_ackRequest(RtaConnection *conn, PARCJSON *request) +{ + PARCJSON *response = cpiAcks_CreateAck(request); + CCNxTlvDictionary *ackDict = ccnxControlFacade_CreateCPI(response); + + TransportMessage *tm_ack = transportMessage_CreateFromDictionary(ackDict); + ccnxTlvDictionary_Release(&ackDict); + parcJSON_Release(&response); + + transportMessage_SetInfo(tm_ack, rtaConnection_Copy(conn), rtaConnection_FreeFunc); + + RtaProtocolStack *stack = rtaConnection_GetStack(conn); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(stack, FWD_METIS, RTA_UP); + if (rtaComponent_PutMessage(out, tm_ack)) { + RtaComponentStats *stats = rtaConnection_GetStats(conn, FWD_METIS); + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + } +} + +static bool +_handleDownControl(FwdMetisState *fwdConnState, RtaConnection *conn, TransportMessage *tm) +{ + bool consumedMessage = false; + + CCNxTlvDictionary *dict = transportMessage_GetDictionary(tm); + if (ccnxTlvDictionary_IsControl(dict)) { + if (ccnxControlFacade_IsCPI(dict)) { + PARCJSON *json = ccnxControlFacade_GetJson(dict); + if (controlPlaneInterface_GetCPIMessageType(json) == CPI_REQUEST) { + if (cpi_getCPIOperation2(json) == CPI_PAUSE) { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s conn %p recieved PAUSE\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn); + } + _ackRequest(conn, json); + consumedMessage = true; + } + + if (cpi_getCPIOperation2(json) == CPI_FLUSH) { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s conn %p recieved FLUSH\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn); + } + _ackRequest(conn, json); + consumedMessage = true; + } + } + } + } + + if (consumedMessage) { + fwdConnState->stats.countDowncallControl++; + } + + return consumedMessage; +} + +/** + * send raw packet from codec to forwarder. We are passed the ProtocolStack on the ptr. + */ +static void +connector_Fwd_Metis_Downcall_Read(PARCEventQueue *in, PARCEventType event, void *ptr) +{ + TransportMessage *tm; + + while ((tm = rtaComponent_GetMessage(in)) != NULL) { + RtaConnection *conn = rtaConnection_GetFromTransport(tm); + FwdMetisState *fwdConnState = rtaConnection_GetPrivateData(conn, FWD_METIS); + RtaComponentStats *stats = rtaConnection_GetStats(conn, FWD_METIS); + rtaComponentStats_Increment(stats, STATS_DOWNCALL_IN); + fwdConnState->stats.countDowncallReads++; + + bool consumedControl = _handleDownControl(fwdConnState, conn, tm); + if (!consumedControl) { + // we did not consume the message as a control packet for the metis connector + + if (fwdConnState->isConnected) { + // If the socket is connected, this will "do the right thing" and consume the transport message. + connector_Fwd_Metis_Downcall_HandleConnected(fwdConnState, tm, conn, stats); + } else { + // Oops, got a packet before we're connected. + printf("\nConnection %p transport message %p on fd %d that's not open\n", (void *) conn, (void *) tm, fwdConnState->fd); + } + + // now attempt to write to the network + _dequeueMessagesToMetis(fwdConnState); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s total downcall reads in %" PRIu64 " out %" PRIu64 "\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaComponentStats_Get(stats, STATS_DOWNCALL_IN), + rtaComponentStats_Get(stats, STATS_DOWNCALL_OUT)); + } + } + + transportMessage_Destroy(&tm); + } +} + +/** + * Destroy the FwdMetisState object. + * + * Destroys any packets waiting in queue, frees the libevent structures used by the connection to Metis. + * Frees the FwdMetisState object and will NULL *fwdStatePtr. + * + * @param [in,out] fwdStatePtr Double pointer to the allocated state. Will be NULL'd on output. + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +_fwdMetisState_Release(FwdMetisState **fwdStatePtr) +{ + FwdMetisState *fwd_state = *fwdStatePtr; + + while (!parcDeque_IsEmpty(fwd_state->transportMessageQueue)) { + TransportMessage *tm = parcDeque_RemoveFirst(fwd_state->transportMessageQueue); + transportMessage_Destroy(&tm); + } + + parcDeque_Release(&fwd_state->transportMessageQueue); + + if (fwd_state->readEvent) { + parcEvent_Destroy(&(fwd_state->readEvent)); + } + + if (fwd_state->writeEvent) { + parcEvent_Destroy(&(fwd_state->writeEvent)); + } + + parcEventTimer_Destroy(&(fwd_state->transportMessageQueueEvent)); + + if (fwd_state->metisOutputQueue) { + parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue)); + } + + if (fwd_state->nextMessage.packet) { + parcBuffer_Release(&fwd_state->nextMessage.packet); + } + + close(fwd_state->fd); + + parcMemory_Deallocate((void **) &fwd_state); + *fwdStatePtr = NULL; +} + +static int +connector_Fwd_Metis_Closer(RtaConnection *conn) +{ + FwdMetisState *fwd_state = rtaConnection_GetPrivateData(conn, FWD_METIS); + rtaConnection_SetPrivateData(conn, FWD_METIS, NULL); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s called on fwd_state %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), __func__, (void *) fwd_state); + } + + RtaComponentStats *stats = rtaConnection_GetStats(conn, FWD_METIS); + rtaComponentStats_Increment(stats, STATS_CLOSES); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s closed fwd_state %p deque length %zu\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) fwd_state, + parcDeque_Size(fwd_state->transportMessageQueue)); + + printf("%9" PRIu64 " %s closed fwd_state %p stats: up { reads %u wok %u werr %u wblk %u wfull %u wctrlok %u wctrlerr %u }\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) fwd_state, + fwd_state->stats.countUpcallReads, fwd_state->stats.countUpcallWriteDataOk, fwd_state->stats.countUpcallWriteDataError, + fwd_state->stats.countUpcallWriteDataBlocked, fwd_state->stats.countUpcallWriteDataQueueFull, + fwd_state->stats.countUpcallWriteControlOk, fwd_state->stats.countUpcallWriteControlError); + + printf("%9" PRIu64 " %s closed fwd_state %p stats: dn { reads %u wok %u wctrlok %u }\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) fwd_state, + fwd_state->stats.countDowncallReads, fwd_state->stats.countDowncallWrites, fwd_state->stats.countDowncallControl); + } + + _fwdMetisState_Release(&fwd_state); + + return 0; +} + +static int +connector_Fwd_Metis_Release(RtaProtocolStack *stack) +{ + return 0; +} + +/** + * Enable to disable the read event based on the Blocked Up state + * + * If we receive a Blocked Up state change and the read event is pending, make it + * not pending. If we receive a not blocked up state change and the read event is not + * pending, make it pending. + * + * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#> + * + * Example: + * @code + * { + * <#example#> + * } + * @endcode + */ +static void +connector_Fwd_Metis_StateChange(RtaConnection *conn) +{ + struct fwd_metis_state *fwd_state = rtaConnection_GetPrivateData(conn, FWD_METIS); + + int isReadPending = parcEvent_Poll(fwd_state->readEvent, PARCEventType_Read); + + + // If we are blocked in the UP direction, disable events on the read queue + if (rtaConnection_BlockedUp(conn)) { + // we only disable it and log it if it was active + if (isReadPending) { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %u blocked up, disable PARCEventType_Read\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaConnection_GetConnectionId(conn)); + } + + parcEvent_Stop(fwd_state->readEvent); + } + } else { + if ((!isReadPending) && fwd_state->isConnected) { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %u unblocked up, enable PARCEventType_Read\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaConnection_GetConnectionId(conn)); + } + parcEvent_Start(fwd_state->readEvent); + } + } + + // We do not need to do anything with DOWN direction, becasue we're the component sending + // those block down messages. +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/rta_ApiConnection.c b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/rta_ApiConnection.c new file mode 100644 index 00000000..4e5ea48f --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/rta_ApiConnection.c @@ -0,0 +1,634 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implements the API connector. The API connector is a event based component to manage the socket + * to the API. + * + * The API Connector's job is to manage the socket to the API between the RTA Framework and the + * API. It does this by using an event directly to manage that socket. It uses the same + * event scheduler base as the RTA framework, so its all part of the same event dispatcher. + * + * The RTA Transport now only speaks CCNxTlvDictionary messages. If we receive old timey Interest, + * ContentObject, etc., we translate them to the Dictionary format. The TransportMessage and CCNxMessage + * will both go away. + * + */ + +#include <config.h> +#include <stdio.h> +#include <fcntl.h> +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <errno.h> + +#include <parc/algol/parc_EventBuffer.h> + +#include <parc/algol/parc_Memory.h> +#include <LongBow/runtime.h> + +#include <ccnx/transport/transport_rta/connectors/rta_ApiConnection.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_Services.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> +#include <ccnx/api/control/controlPlaneInterface.h> +#include <ccnx/api/control/cpi_ControlFacade.h> + +#include <ccnx/transport/transport_rta/config/config_Codec_Tlv.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h> + + +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +#define PAIR_TRANSPORT 0 +#define PAIR_OTHER 1 + +// we are only putting an 8-byte pointer on the queue, so +// this should be 50 messages +#define MAX_API_QUEUE_BYTES 400 + + +unsigned api_upcall_writes = 0; +unsigned api_downcall_reads = 0; +extern unsigned rta_transport_reads; + +// per connection state +struct rta_api_connection { + // A reference to our connection + RtaConnection *connection; + + // event queue for socketpair to API + PARCEventQueue *bev_api; + + // these are assingned to us by the Transport + int api_fd; + int transport_fd; +}; + +// ========================================================================================== +// STATIC PROTOTYPES and their headerdoc + +/** + * PARCEvent calls this when the API queue falls below the watermark + * + * We watermark the write queue at MAX_API_QUEUE_BYTES bytes. When a write takes + * the queue backlog below that amount, PARCEvent calls this. + * + * @param [in] connVoid Void pointer to the RtaConnection + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void rtaApiConnection_WriteCallback(PARCEventQueue *queue, PARCEventType type, void *conn); + +/** + * PARCEvent calls this when there's a message from the API + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void rtaApiConnection_Downcall_Read(PARCEventQueue *bev, PARCEventType type, void *conn); + +/** + * PARCEvent calls this when there's a non-read/write event on the API's socket + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void rtaApiConnection_Downcall_Event(PARCEventQueue *, PARCEventQueueEventType events, void *conn); + + +/** + * Drains the input queue and output queue of a connection to the API + * + * The input queue and output queue contain pointers to CCNxMessages. On close, + * we need to drain these queues and release all the messages. + * + * The API Connector is responsible for only draining its input queue. The output + * queue up to the API is drained by the RTA Framework. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void rtaApiConnection_DrainApiConnection(RtaApiConnection *apiConnection); + +/** + * Writes a message to the API + * + * Takes ownership of the message which is passed up to the API + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void rtaApiConnection_WriteMessageToApi(RtaApiConnection *apiConnection, CCNxMetaMessage *msg); + +// ========================================================================================== +// Public API + +static void +rtaApiConnection_SetupSocket(RtaApiConnection *apiConnection, RtaConnection *connection) +{ + RtaProtocolStack *stack = rtaConnection_GetStack(connection); + PARCEventScheduler *base = rtaFramework_GetEventScheduler(rtaProtocolStack_GetFramework(stack)); + int error; + + // Set non-blocking flag + int flags = fcntl(apiConnection->transport_fd, F_GETFL, NULL); + assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno); + int failure = fcntl(apiConnection->transport_fd, F_SETFL, flags | O_NONBLOCK); + assertFalse(failure, "fcntl failed to set socket non-blocking(%d) %s\n", errno, strerror(errno)); + + apiConnection->bev_api = parcEventQueue_Create(base, apiConnection->transport_fd, 0); + assertNotNull(apiConnection->bev_api, "Got null result from parcEventQueue_Create"); + + // Set buffer size + int sendbuff = 1000 * 8; + + error = setsockopt(rtaConnection_GetTransportFd(connection), SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff)); + assertTrue(error == 0, "Got error setting SO_SNDBUF: %s", strerror(errno)); + + parcEventQueue_SetWatermark(apiConnection->bev_api, PARCEventType_Write, MAX_API_QUEUE_BYTES, 0); + parcEventQueue_SetCallbacks(apiConnection->bev_api, + rtaApiConnection_Downcall_Read, + rtaApiConnection_WriteCallback, + rtaApiConnection_Downcall_Event, + (void *) connection); + + parcEventQueue_Enable(apiConnection->bev_api, PARCEventType_Read | PARCEventType_Write); +} + +RtaApiConnection * +rtaApiConnection_Create(RtaConnection *connection) +{ + RtaApiConnection *apiConnection = parcMemory_AllocateAndClear(sizeof(RtaApiConnection)); + assertNotNull(apiConnection, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(RtaApiConnection)); + + apiConnection->connection = rtaConnection_Copy(connection); + apiConnection->api_fd = rtaConnection_GetApiFd(connection); + apiConnection->transport_fd = rtaConnection_GetTransportFd(connection); + rtaApiConnection_SetupSocket(apiConnection, connection); + + return apiConnection; +} + +void +rtaApiConnection_Destroy(RtaApiConnection **apiConnectionPtr) +{ + assertNotNull(apiConnectionPtr, "Parameter apiConnecitonPtr must be non-null"); + assertNotNull(*apiConnectionPtr, "Parameter apiConnecitonPtr must dereference to non-null"); + RtaApiConnection *apiConnection = *apiConnectionPtr; + + + // Send all the outbound messages up to the API. This at least gets them out + // of our output queue on to the API's socket. + parcEventQueue_Finished(apiConnection->bev_api, PARCEventType_Write); + rtaApiConnection_DrainApiConnection(apiConnection); + + parcEventQueue_Destroy(&(apiConnection->bev_api)); + + rtaConnection_Destroy(&apiConnection->connection); + + parcMemory_Deallocate((void **) &apiConnection); + + *apiConnectionPtr = NULL; +} + +static void +rtaApiConnection_SendToApiAsDictionary(RtaApiConnection *apiConnection, TransportMessage *tm) +{ + CCNxMetaMessage *msg = ccnxMetaMessage_Acquire(transportMessage_GetDictionary(tm)); + rtaApiConnection_WriteMessageToApi(apiConnection, msg); +} + +static CCNxName * +rtaApiConnection_GetNameFromTransportMessage(TransportMessage *tm) +{ + CCNxName *name = NULL; + CCNxTlvDictionary *dictionary = transportMessage_GetDictionary(tm); + switch (ccnxTlvDictionary_GetSchemaVersion(dictionary)) { + case CCNxTlvDictionary_SchemaVersion_V1: + name = ccnxTlvDictionary_GetName(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_NAME); + break; + + default: + break; + } + return name; +} + +/** + * Writes the CCNxMessage inside the transport message up to the API. + * Its possible that if there's no space in the socket the write will block + * and return an error. + * + * @return true if written to API, false if not (most likely would block) + */ +bool +rtaApiConnection_SendToApi(RtaApiConnection *apiConnection, TransportMessage *tm, RtaComponentStats *stats) +{ + assertNotNull(apiConnection, "Parameter apiConnection must be non-null"); + + if (DEBUG_OUTPUT) { + CCNxName *name = rtaApiConnection_GetNameFromTransportMessage(tm); + char *nameString = NULL; + if (name) { + nameString = ccnxName_ToString(name); + } + + struct timeval delay = transportMessage_GetDelay(tm); + printf("%9" PRIu64 " %s putting transport msg %p to user fd %d delay %.6f name %s\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(apiConnection->connection))), + __func__, + (void *) tm, + apiConnection->api_fd, + delay.tv_sec + delay.tv_usec * 1E-6, + nameString); + + if (nameString) { + parcMemory_Deallocate((void **) &nameString); + } + } + + rtaApiConnection_SendToApiAsDictionary(apiConnection, tm); + + rtaComponentStats_Increment(stats, STATS_UPCALL_OUT); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s conn %p fd_out %d state %p upcalls %u reads %u\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(apiConnection->connection))), + __func__, + (void *) apiConnection->connection, + apiConnection->transport_fd, + (void *) apiConnection, + api_upcall_writes, + rta_transport_reads); + } + + return true; +} + +void +rtaApiConnection_BlockDown(RtaApiConnection *apiConnection) +{ + assertNotNull(apiConnection, "Parameter apiConnection must be non-null"); + PARCEventType enabled_events = parcEventQueue_GetEnabled(apiConnection->bev_api); + + // we only disable it and log it if it was active + if (enabled_events & PARCEventType_Read) { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %u blocked down, disable PARCEventType_Read\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(apiConnection->connection))), + __func__, + rtaConnection_GetConnectionId(apiConnection->connection)); + } + + parcEventQueue_Disable(apiConnection->bev_api, PARCEventType_Read); + } +} + +void +rtaApiConnection_UnblockDown(RtaApiConnection *apiConnection) +{ + assertNotNull(apiConnection, "Parameter apiConnection must be non-null"); + PARCEventType enabled_events = parcEventQueue_GetEnabled(apiConnection->bev_api); + + if (!(enabled_events & PARCEventType_Read)) { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %u unblocked down, enable PARCEventType_Read\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(apiConnection->connection))), + __func__, + rtaConnection_GetConnectionId(apiConnection->connection)); + } + parcEventQueue_Enable(apiConnection->bev_api, PARCEventType_Read); + } +} + +// ========================================================================================== +// Internal implementation + +static void +rtaApiConnection_WriteMessageToApi(RtaApiConnection *apiConnection, CCNxMetaMessage *msg) +{ + assertNotNull(msg, "Parameter msg must be non-null"); + + int error = parcEventQueue_Write(apiConnection->bev_api, &msg, sizeof(&msg)); + assertTrue(error == 0, + "write to transport_fd %d write error: (%d) %s", + apiConnection->transport_fd, errno, strerror(errno)); + + // debugging tracking + api_upcall_writes++; +} + +static void +_rtaAPIConnection_ProcessCPIRequest(RtaConnection *conn, PARCJSON *json) +{ + // Is it a request type we know about? + + switch (cpi_getCPIOperation2(json)) { + case CPI_PAUSE: { + RtaConnectionStateType oldstate = rtaConnection_GetState(conn); + if (oldstate == CONN_OPEN) { + rtaConnection_SetState(conn, CONN_PAUSED); + } + break; + } + + default: + // do nothing, don't know about this message type + break; + } +} + +static void +connector_Api_ProcessCpiMessage(RtaConnection *conn, CCNxTlvDictionary *controlDictionary) +{ + if (ccnxControlFacade_IsCPI(controlDictionary)) { + PARCJSON *json = ccnxControlFacade_GetJson(controlDictionary); + switch (controlPlaneInterface_GetCPIMessageType(json)) { + case CPI_REQUEST: { + _rtaAPIConnection_ProcessCPIRequest(conn, json); + break; + } + + case CPI_RESPONSE: + break; + + case CPI_ACK: + break; + + default: + assertTrue(0, "Got unknown CPI message type: %d", controlPlaneInterface_GetCPIMessageType(json)); + } + } +} + +static void +rtaApiConnection_ProcessControlFromApi(RtaApiConnection *apiConnection, RtaProtocolStack *stack, CCNxTlvDictionary *controlDictionary) +{ + if (ccnxControlFacade_IsCPI(controlDictionary)) { + connector_Api_ProcessCpiMessage(apiConnection->connection, controlDictionary); + } +} + +static void +rtaApiConnection_Downcall_ProcessDictionary(RtaApiConnection *apiConnection, RtaProtocolStack *stack, + PARCEventQueue *queue_out, RtaComponentStats *stats, CCNxTlvDictionary *messageDictionary) +{ + // Look at the control message before checking for the connection closed + if (ccnxTlvDictionary_IsControl(messageDictionary)) { + rtaApiConnection_ProcessControlFromApi(apiConnection, stack, messageDictionary); + } + + // In paused or closed state, we only pass control messages + if ((rtaConnection_GetState(apiConnection->connection) == CONN_OPEN) || (ccnxTlvDictionary_IsControl(messageDictionary))) { + TransportMessage *tm = transportMessage_CreateFromDictionary(messageDictionary); + + // Set the auxiliary information to the message's connection + transportMessage_SetInfo(tm, rtaConnection_Copy(apiConnection->connection), rtaConnection_FreeFunc); + + if (DEBUG_OUTPUT) { + CCNxName *name = NULL; + if (ccnxTlvDictionary_IsInterest(messageDictionary)) { + name = ccnxInterest_GetName(messageDictionary); + } else if (ccnxTlvDictionary_IsContentObject(messageDictionary)) { + name = ccnxContentObject_GetName(messageDictionary); + } + + char *noname = "NONAME"; + char *nameString = noname; + if (name) { + nameString = ccnxName_ToString(name); + } + + printf("%9" PRIu64 " %s putting transport msg %p from user fd %d: %s\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(apiConnection->connection))), + __func__, + (void *) tm, apiConnection->api_fd, + nameString); + + if (nameString != noname) { + parcMemory_Deallocate((void **) &nameString); + } + + //ccnxTlvDictionary_Display(0, messageDictionary); + } + + // send down the stack. If it fails, it destroys the message. + if (rtaComponent_PutMessage(queue_out, tm)) { + rtaComponentStats_Increment(stats, STATS_DOWNCALL_OUT); + } + } +} + +static void +rtaApiConnection_Downcall_ProcessMessage(RtaApiConnection *apiConnection, RtaProtocolStack *stack, PARCEventBuffer *eb_in, + PARCEventQueue *queue_out, RtaComponentStats *stats) +{ + api_downcall_reads++; + CCNxMetaMessage *msg; + + int bytesRemoved = parcEventBuffer_Read(eb_in, &msg, sizeof(CCNxMetaMessage *)); + assertTrue(bytesRemoved == sizeof(CCNxMetaMessage *), + "Error, did not remove an entire pointer, expected %zu got %d", + sizeof(CCNxMetaMessage *), + bytesRemoved); + + rtaComponentStats_Increment(stats, STATS_DOWNCALL_IN); + + // This will save its own reference to the messageDictionary + rtaApiConnection_Downcall_ProcessDictionary(apiConnection, stack, queue_out, stats, msg); + + // At this point, the CCNxMetaMessage passed in by the application thread has been + // acquired rtaApiConnection_Downcall_ProcessDictionary(), so we can Release the reference we + // acquired in rtaTransport_Send(). + ccnxMetaMessage_Release(&msg); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s conn %p total downcall reads in %" PRIu64 " out %" PRIu64 "\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(apiConnection->connection))), + __func__, + (void *) apiConnection->connection, + rtaComponentStats_Get(stats, STATS_DOWNCALL_IN), + rtaComponentStats_Get(stats, STATS_DOWNCALL_OUT)); + } +} + + +/* + * Called by PARCEvent when there's a message to read from the API + * Read a message from the API. + * rtaConnectionVoid is the RtaConnection associated with the api descriptor + */ +static void +rtaApiConnection_Downcall_Read(PARCEventQueue *bev, PARCEventType type, void *rtaConnectionVoid) +{ + RtaConnection *conn = (RtaConnection *) rtaConnectionVoid; + + assertNotNull(rtaConnectionVoid, "Parameter must be a non-null void *"); + + RtaProtocolStack *stack = rtaConnection_GetStack(conn); + assertNotNull(stack, "rtaConnection_GetStack returned null"); + + RtaComponentStats *stats = rtaConnection_GetStats(conn, API_CONNECTOR); + assertNotNull(stats, "rtaConnection_GetStats returned null"); + + RtaApiConnection *apiConnection = rtaConnection_GetPrivateData(conn, API_CONNECTOR); + assertNotNull(apiConnection, "rtaConnection_GetPrivateData got null"); + + PARCEventBuffer *eb_in = parcEventBuffer_GetQueueBufferInput(bev); + + PARCEventQueue *queue_out = rtaComponent_GetOutputQueue(conn, API_CONNECTOR, RTA_DOWN); + assertNotNull(queue_out, "component_GetOutputQueue returned null"); + + while (parcEventBuffer_GetLength(eb_in) >= sizeof(TransportMessage *)) { + rtaApiConnection_Downcall_ProcessMessage(apiConnection, stack, eb_in, queue_out, stats); + } + parcEventBuffer_Destroy(&eb_in); +} + +/* + * This is used on the connection to the API out of the transport box + */ +static void +rtaApiConnection_Downcall_Event(PARCEventQueue *bev, PARCEventQueueEventType events, void *ptr) +{ +} + +/** + * Drains all the CCNxMessages off an event buffer and destroys them + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +drainBuffer(PARCEventBuffer *buffer, RtaConnection *conn) +{ + size_t length; + + while ((length = parcEventBuffer_GetLength(buffer)) > 0) { + CCNxMetaMessage *msg; + ssize_t len; + + len = parcEventBuffer_Read(buffer, &msg, sizeof(CCNxMetaMessage *)); + assertTrue(len == sizeof(CCNxMetaMessage *), + "Removed incorrect length, expected %zu got %zd: (%d) %s", + sizeof(CCNxMetaMessage *), + len, + errno, + strerror(errno)); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s conn %p drained message %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + (void *) conn, + (void *) msg); + } + ccnxMetaMessage_Release(&msg); + } +} + +/** + * Called on Destroy to clear our input buffer. This does not + * drain the output (to API) buffer, that is done by the RTA Framework + */ +static void +rtaApiConnection_DrainApiConnection(RtaApiConnection *apiConnection) +{ + // drain and free the transport_fd + parcEventQueue_Disable(apiConnection->bev_api, PARCEventType_Read); + + PARCEventBuffer *in = parcEventBuffer_GetQueueBufferInput(apiConnection->bev_api); + drainBuffer(in, apiConnection->connection); + parcEventBuffer_Destroy(&in); + + // There may be some messages in the output buffer that + // have not actually been written to the kernel socket. + // Drain those too, as the API will never see them + + if (DEBUG_OUTPUT) { + PARCEventBuffer *out = parcEventBuffer_GetQueueBufferOutput(apiConnection->bev_api); + printf("%9" PRIu64 " %s conn %p output buffer has %zu bytes\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(apiConnection->connection))), + __func__, + (void *) apiConnection->connection, + parcEventBuffer_GetLength(out)); + parcEventBuffer_Destroy(&out); + } +} + +/** + * Called by PARCEvent when we cross below the write watermark + */ +static void +rtaApiConnection_WriteCallback(PARCEventQueue *queue, PARCEventType type, void *connVoid) +{ + // we dropped below the write watermark, unblock the connection in the UP direction + RtaConnection *conn = (RtaConnection *) connVoid; + if (rtaConnection_BlockedUp(conn)) { + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %u output fell below watermark, unblocking UP\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(rtaConnection_GetStack(conn))), + __func__, + rtaConnection_GetConnectionId(conn)); + } + + rtaConnection_ClearBlockedUp(conn); + } +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/rta_ApiConnection.h b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/rta_ApiConnection.h new file mode 100644 index 00000000..799c45e6 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/rta_ApiConnection.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_ApiConnection.h + * @brief Implementation of the API connection + * + * <#Detailed Description#> + * + */ + +#ifndef TransportRTA_rta_ApiConnection_h +#define TransportRTA_rta_ApiConnection_h + +struct rta_api_connection; +typedef struct rta_api_connection RtaApiConnection; + +#include <ccnx/transport/transport_rta/core/rta_Connection.h> + +RtaApiConnection *rtaApiConnection_Create(RtaConnection *connection); +void rtaApiConnection_Destroy(RtaApiConnection **rtaApiConnectionPtr); + +/** + * Sends a TransportMessage up to the API + * + * Decapsulates the ccnx message and sends it up to the API. It will destroy the TransportMessage wrapper. + * + * @param [in] apiConnection The API connection to write to + * @param [in] tm The transport message to send + * @param [in] stats The statistics counter to increment on success + * + * @return true Transport message written + * @return false Transport message not written (but still destroyed) + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool rtaApiConnection_SendToApi(RtaApiConnection *apiConnection, TransportMessage *tm, RtaComponentStats *stats); + +/** + * Block data flow in the DOWN direction + * + * To block in the DOWN direction, we disable READ events on the API's buffer + * + * @param [in] apiConnection The API Connector's connection state + * @param [in] conn The RTA Connection + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaApiConnection_BlockDown(RtaApiConnection *apiConnection); + +/** + * Unblock data flow in the DOWN direction + * + * To unblock in the DOWN direction, we enable READ events on the API's buffer + * + * @param [in] apiConnection The API Connector's connection state + * @param [in] conn The RTA Connection + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaApiConnection_UnblockDown(RtaApiConnection *apiConnection); +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/CMakeLists.txt b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/CMakeLists.txt new file mode 100644 index 00000000..85e4812f --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/CMakeLists.txt @@ -0,0 +1,16 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_connector_Api + test_rta_ApiConnection + test_connector_Forwarder_Local + test_connector_Forwarder_Metis +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Api.c b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Api.c new file mode 100644 index 00000000..409e075d --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Api.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../connector_Api.c" + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(connector_Api) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(connector_Api) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(connector_Api) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(connector_Api); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Local.c b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Local.c new file mode 100644 index 00000000..014f4bbe --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Local.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#define DEBUG_OUTPUT 1 +#include "../connector_Forwarder_Local.c" +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/security/parc_Security.h> +#include <parc/security/parc_Pkcs12KeyStore.h> + +#include <ccnx/api/control/cpi_ControlMessage.h> +#include <ccnx/api/control/controlPlaneInterface.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework_Commands.c> +#include <ccnx/transport/transport_rta/core/rta_Framework_private.h> +#include <ccnx/transport/transport_rta/config/config_All.h> +#include <ccnx/transport/test_tools/bent_pipe.h> + +typedef struct test_data { + PARCRingBuffer1x1 *commandRingBuffer; + PARCNotifier *commandNotifier; + RtaFramework *framework; + + int api_fds[2]; + int listen_fd; + int rnd_fd; + + int stackId; + RtaConnection *connectionUnderTest; + + char bentpipe_LocalName[1024]; + BentPipeState *bentpipe; + char keystoreName[1024]; + char keystorePassword[1024]; +} TestData; + +static CCNxTransportConfig * +_createParams(const char *local_name, const char *keystore_name, const char *keystore_passwd) +{ + assertNotNull(local_name, "Got null keystore name\n"); + assertNotNull(keystore_name, "Got null keystore name\n"); + assertNotNull(keystore_passwd, "Got null keystore passwd\n"); + + CCNxStackConfig *stackConfig = apiConnector_ProtocolStackConfig( + testingUpper_ProtocolStackConfig( + localForwarder_ProtocolStackConfig( + protocolStack_ComponentsConfigArgs(ccnxStackConfig_Create(), + apiConnector_GetName(), + testingUpper_GetName(), + localForwarder_GetName(), NULL)))); + + CCNxConnectionConfig *connConfig = apiConnector_ConnectionConfig( + testingUpper_ConnectionConfig( + tlvCodec_ConnectionConfig( + localForwarder_ConnectionConfig( + ccnxConnectionConfig_Create(), local_name)))); + + publicKeySigner_ConnectionConfig(connConfig, keystore_name, keystore_passwd); + + CCNxTransportConfig *result = ccnxTransportConfig_Create(stackConfig, connConfig); + ccnxStackConfig_Release(&stackConfig); + return result; +} + +static TestData * +_commonSetup(void) +{ + parcSecurity_Init(); + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + sprintf(data->bentpipe_LocalName, "/tmp/bentpipe_%d.sock", getpid()); + data->bentpipe = bentpipe_Create(data->bentpipe_LocalName); + bentpipe_Start(data->bentpipe); + + sprintf(data->keystoreName, "/tmp/keystore_%d.p12", getpid()); + sprintf(data->keystorePassword, "23439429"); + + unlink(data->keystoreName); + + bool success = parcPkcs12KeyStore_CreateFile(data->keystoreName, data->keystorePassword, "user", 1024, 30); + assertTrue(success, "parcPkcs12KeyStore_CreateFile() failed."); + + data->commandRingBuffer = parcRingBuffer1x1_Create(128, NULL); + data->commandNotifier = parcNotifier_Create(); + data->framework = rtaFramework_Create(data->commandRingBuffer, data->commandNotifier); + + // Create a protocol stack and a connection to use + CCNxTransportConfig *params = _createParams(data->bentpipe_LocalName, data->keystoreName, data->keystorePassword); + + data->stackId = 1; + + RtaCommandCreateProtocolStack *createStack = + rtaCommandCreateProtocolStack_Create(data->stackId, ccnxTransportConfig_GetStackConfig(params)); + + _rtaFramework_ExecuteCreateStack(data->framework, createStack); + rtaCommandCreateProtocolStack_Release(&createStack); + + socketpair(PF_LOCAL, SOCK_STREAM, 0, data->api_fds); + RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(data->stackId, data->api_fds[0], data->api_fds[1], + ccnxConnectionConfig_GetJson(ccnxTransportConfig_GetConnectionConfig(params))); + _rtaFramework_ExecuteOpenConnection(data->framework, openConnection); + rtaCommandOpenConnection_Release(&openConnection); + + ccnxTransportConfig_Destroy(¶ms); + + // now poke in to the connection table to get the connection to test + data->connectionUnderTest = rtaConnectionTable_GetByApiFd(data->framework->connectionTable, data->api_fds[0]); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + rtaFramework_Teardown(data->framework); + + parcRingBuffer1x1_Release(&data->commandRingBuffer); + parcNotifier_Release(&data->commandNotifier); + rtaFramework_Destroy(&data->framework); + + bentpipe_Stop(data->bentpipe); + bentpipe_Destroy(&data->bentpipe); + unlink(data->keystoreName); + parcMemory_Deallocate((void **) &data); + parcSecurity_Fini(); +} + +// ============================================================= + +LONGBOW_TEST_RUNNER(connector_Forwarder_Local) +{ + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(connector_Forwarder_Local) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(connector_Forwarder_Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + + +// ====================================================== +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, connector_Fwd_Local_Init_Release); + LONGBOW_RUN_TEST_CASE(Local, connector_Fwd_Local_Cpi_Pause); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ==================================================================== + +LONGBOW_TEST_CASE(Local, connector_Fwd_Local_Init_Release) +{ + // nothing to do, just checking that memory is in balance in teardown +} + +/** + * Send a PAUSE CPI message to the forwarder. It should reflect + * back a CPI ACK + */ +LONGBOW_TEST_CASE(Local, connector_Fwd_Local_Cpi_Pause) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCJSON *controlPause = cpi_CreatePauseInputRequest(); + + CCNxTlvDictionary *controlDictionary = ccnxControlFacade_CreateCPI(controlPause); + TransportMessage *tm_in = transportMessage_CreateFromDictionary(controlDictionary); + + uint64_t pause_seqnum = controlPlaneInterface_GetSequenceNumber(controlPause); + parcJSON_Release(&controlPause); + ccnxTlvDictionary_Release(&controlDictionary); + + transportMessage_SetInfo(tm_in, rtaConnection_Copy(data->connectionUnderTest), (void (*)(void **))rtaConnection_Destroy); + + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(data->connectionUnderTest), TESTING_UPPER, RTA_DOWN); + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(data->connectionUnderTest), TESTING_UPPER, RTA_DOWN); + + rtaComponent_PutMessage(in, tm_in); + + // this will crank it though the forwarder and reflect back up to us + rtaFramework_NonThreadedStepCount(data->framework, 4); + + // The first message out should be a CONNECTION_OPEN + TransportMessage *tm_out = rtaComponent_GetMessage(out); + transportMessage_Destroy(&tm_out); + + tm_out = rtaComponent_GetMessage(out); + + assertTrue(transportMessage_IsControl(tm_out), "got wrong type, not a control message"); + + CCNxControl *control = ccnxMetaMessage_GetControl(transportMessage_GetDictionary(tm_out)); + + assertTrue(ccnxControl_IsACK(control), "Expected ccnxControl_IsACK to be true."); + + uint64_t _ack_original_seqnum = ccnxControl_GetAckOriginalSequenceNumber(control); + + assertTrue(_ack_original_seqnum == pause_seqnum, + "Got wrong original message seqnum, expected %" PRIu64 " got %" PRIu64, pause_seqnum, _ack_original_seqnum); + + transportMessage_Destroy(&tm_out); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(connector_Forwarder_Local); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Metis.c b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Metis.c new file mode 100644 index 00000000..6fe9e3d2 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Metis.c @@ -0,0 +1,1350 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * This test will setup a server socket so the Metis connector can connect to it. We can + * then see the packets the connector thinks it is sending to Metis. + */ + +#define DEBUG 1 +#include "../connector_Forwarder_Metis.c" + +#include <parc/algol/parc_SafeMemory.h> +#include <parc/security/parc_Pkcs12KeyStore.h> +#include <parc/security/parc_Security.h> + +#include <ccnx/api/control/cpi_ControlFacade.h> +#include <ccnx/api/control/controlPlaneInterface.h> +#include <ccnx/api/control/cpi_Forwarding.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework_Commands.c> +#include <ccnx/transport/transport_rta/core/rta_Framework_private.h> +#include <ccnx/transport/transport_rta/config/config_All.h> + +#include <ccnx/common/codec/ccnxCodec_TlvPacket.h> +#include <ccnx/transport/test_tools/traffic_tools.h> + +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_FixedHeader.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_PacketEncoder.h> +#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_Types.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_interest_nameA.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_content_nameA_crc32c.h> +#include <ccnx/common/codec/schema_v1/testdata/v1_cpi_add_route_crc32c.h> + +#include <ccnx/common/ccnx_WireFormatMessage.h> + +// inet_pton +#include <arpa/inet.h> + +#include <LongBow/unit-test.h> + +static char keystorename[1024]; +static const char keystorepass[] = "2398472983479234"; + +#ifndef INPORT_ANY +#define INPORT_ANY 0 +#endif + +typedef struct test_data { + PARCRingBuffer1x1 *commandRingBuffer; + PARCNotifier *commandNotifier; + + // we will bind to a random port, this is what we end up binding to + // Its in host byte order + uint16_t metis_port; + + // server_socket is a socket we listen to like the Metis forwarder, so + // we can see all the traffic that comes out the bottom of the connector. + int server_socket; + + // when we accept a client on the server socket, this is his socket + int client_socket; + + RtaFramework *framework; + CCNxTransportConfig *params; + + char keystoreName[1024]; + char keystorePassword[1024]; +} TestData; + +/* + * @function setup_server + * @abstract Bind to 127.0.0.1 on a random port, returns the socket and port + * @discussion + * <#Discussion#> + * + * @param portOutput is the port bound to in host byte order + * @return <#return#> + */ +static int +_setup_server(uint16_t *portOutput) +{ + struct sockaddr_in address; + + /* listen on 127.0.0.1 random port */ + address.sin_family = PF_INET; + address.sin_port = INPORT_ANY; + inet_pton(AF_INET, "127.0.0.1", &(address.sin_addr)); + + int fd = socket(PF_INET, SOCK_STREAM, 0); + assertFalse(fd < 0, "error on bind: (%d) %s", errno, strerror(errno)); + + // Set non-blocking flag + int flags = fcntl(fd, F_GETFL, NULL); + assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno); + int failure = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno); + + failure = bind(fd, (struct sockaddr *) &address, sizeof(struct sockaddr_in)); + assertFalse(failure, "error on bind: (%d) %s", errno, strerror(errno)); + + failure = listen(fd, 16); + assertFalse(failure, "error on listen: (%d) %s", errno, strerror(errno)); + + socklen_t x = sizeof(address); + failure = getsockname(fd, (struct sockaddr *) &address, &x); + assertFalse(failure, "error on getsockname: (%d) %s", errno, strerror(errno)); + + *portOutput = htons(address.sin_port); + + printf("test server setup on port %d\n", *portOutput); + return fd; +} + +static int +_accept_client(int server_socket) +{ + socklen_t addrlen; + struct sockaddr_in address; + int client_socket; + + addrlen = sizeof(struct sockaddr_in); + client_socket = accept(server_socket, (struct sockaddr *) &address, &addrlen); + assertFalse(client_socket < 0, "accept error: %s", strerror(errno)); + + printf("%s accepted client on socket %d\n", __func__, client_socket); + return client_socket; +} + +static RtaConnection * +_openConnection(TestData *data, int stack_id, int fds[2]) +{ + RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(stack_id, fds[0], fds[1], + ccnxConnectionConfig_GetJson(ccnxTransportConfig_GetConnectionConfig(data->params))); + _rtaFramework_ExecuteOpenConnection(data->framework, openConnection); + rtaCommandOpenConnection_Release(&openConnection); + + return rtaConnectionTable_GetByApiFd(data->framework->connectionTable, fds[0]); +} + +static void +_createStack(TestData *data, int stack_id) +{ + RtaCommandCreateProtocolStack *createStack = + rtaCommandCreateProtocolStack_Create(stack_id, ccnxTransportConfig_GetStackConfig(data->params)); + _rtaFramework_ExecuteCreateStack(data->framework, createStack); + rtaCommandCreateProtocolStack_Release(&createStack); +} + +static CCNxTransportConfig * +_createParams(int port, const char *keystore_name, const char *keystore_passwd) +{ + CCNxStackConfig *stackConfig; + CCNxConnectionConfig *connConfig; + + assertNotNull(keystore_name, "Got null keystore name\n"); + assertNotNull(keystore_passwd, "Got null keystore passwd\n"); + + stackConfig = apiConnector_ProtocolStackConfig( + testingUpper_ProtocolStackConfig( + metisForwarder_ProtocolStackConfig( + protocolStack_ComponentsConfigArgs(ccnxStackConfig_Create(), + apiConnector_GetName(), + testingUpper_GetName(), + metisForwarder_GetName(), NULL)))); + + connConfig = apiConnector_ConnectionConfig( + testingUpper_ConnectionConfig( + metisForwarder_ConnectionConfig( + tlvCodec_ConnectionConfig( + ccnxConnectionConfig_Create()), port))); + + publicKeySigner_ConnectionConfig(connConfig, keystore_name, keystore_passwd); + + CCNxTransportConfig *result = ccnxTransportConfig_Create(stackConfig, connConfig); + ccnxStackConfig_Release(&stackConfig); + return result; +} + +static TestData * +_commonSetup(void) +{ + parcSecurity_Init(); + + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + memset(data, 0, sizeof(TestData)); + + data->server_socket = _setup_server(&data->metis_port); + + // printf("%s listening on port %u\n", __func__, data->metis_port); + + sprintf(data->keystoreName, "%s", keystorename); + sprintf(data->keystorePassword, keystorepass); + + data->commandRingBuffer = parcRingBuffer1x1_Create(128, NULL); + data->commandNotifier = parcNotifier_Create(); + data->framework = rtaFramework_Create(data->commandRingBuffer, data->commandNotifier); + + data->params = _createParams(data->metis_port, data->keystoreName, keystorepass); + // we will always create stack #1 as the default stack + _createStack(data, 1); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + if (data != NULL) { + if (data->server_socket > 0) { + close(data->server_socket); + } + + if (data->client_socket > 0) { + close(data->client_socket); + } + + ccnxTransportConfig_Destroy(&data->params); + rtaFramework_Teardown(data->framework); + + parcRingBuffer1x1_Release(&data->commandRingBuffer); + parcNotifier_Release(&data->commandNotifier); + rtaFramework_Destroy(&data->framework); + parcMemory_Deallocate((void **) &data); + } + parcSecurity_Fini(); +} + +// ====================================================== +// helper functions + +/** + * Wait for a READ event on the specifid socket. Has a 1 second timeout. + * + * @return true if READ event + * @return false otherwise + */ +static bool +_waitForSelect(int fd) +{ + fd_set readset; + FD_ZERO(&readset); + FD_SET(fd, &readset); + int result = select(fd + 1, &readset, NULL, NULL, &(struct timeval) { 1, 0 }); + assertFalse(result < 0, "Error on select: (%d) %s", errno, strerror(errno)); + assertFalse(result == 0, "Timeout waiting for connection attempt"); + assertTrue(FD_ISSET(fd, &readset), "server_socket was not set by select"); + + return true; +} + + + +static size_t +_sendPacketToConnectorV1(int fd, size_t payloadLength) +{ + // Setup the header + uint8_t headerLength = 13; + uint16_t packetLength = payloadLength + headerLength; + uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest; + + CCNxCodecSchemaV1FixedHeader hdr = { .version = 1, .packetType = packetType, .packetLength = htons(packetLength), .headerLength = headerLength }; + + // put header in packet and write the packet + uint8_t packet[1024]; + memcpy(packet, &hdr, sizeof(hdr)); + + // write out exactly the number of bytes we need + size_t writeSize = packetLength; + + ssize_t nwritten = write(fd, packet, writeSize); + assertTrue(nwritten == writeSize, "Wrong write size, expected %zu got %zd", writeSize, nwritten); + return writeSize; +} + +static RtaConnection * +setupConnectionAndClientSocket(TestData *data, int *apiSocketOuptut, int *clientSocketOutput) +{ + // Open a listener and accept the forwarders connection + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + RtaConnection *conn = _openConnection(data, 1, fds); + assertNotNull(conn, "Got null connection opening on stack 1"); + + rtaFramework_NonThreadedStepCount(data->framework, 2); + + // we should now see a connection request + _waitForSelect(data->server_socket); + + // accept the client and set a 1 second read timeout on the socket + int client_fd = _accept_client(data->server_socket); + struct timeval readTimeout = { 1, 0 }; + setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &readTimeout, sizeof(readTimeout)); + + *apiSocketOuptut = fds[0]; + *clientSocketOutput = client_fd; + return conn; +} + +// throw away the first control message +static void +_throwAwayControlMessage(PARCEventQueue *out) +{ + TransportMessage *control_tm = rtaComponent_GetMessage(out); + assertNotNull(control_tm, "Did not receive a transport message out of the top of the connector"); + assertTrue(transportMessage_IsControl(control_tm), + "transport message is not a control message") + { + ccnxTlvDictionary_Display(transportMessage_GetDictionary(control_tm), 0); + } + transportMessage_Destroy(&control_tm); +} + +static void +_testReadPacketV1(size_t extraBytes) +{ + const int REMOTE = 0; + const int STACK = 1; + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + + // this replaces "_openSocket" + fwd_state->fd = fds[STACK]; + + _setupSocket(fwd_state); + + // Setup the header + uint16_t packetLength = 24; + uint8_t headerLength = 13; + uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest; + + CCNxCodecSchemaV1FixedHeader hdr = { .version = 1, .packetType = packetType, .packetLength = htons(packetLength), .headerLength = headerLength }; + + // put header in packet and write the packet + uint8_t packet[1024]; + memcpy(packet, &hdr, sizeof(hdr)); + + // write out exactly the number of bytes we need + size_t firstWrite = packetLength; + + ssize_t nwritten = write(fds[REMOTE], packet, firstWrite + extraBytes); + assertTrue(nwritten == firstWrite + extraBytes, "Wrong write size, expected %zu got %zd", + firstWrite + extraBytes, nwritten); + + ReadReturnCode readCode = _readPacket(fwd_state); + + assertTrue(readCode == ReadReturnCode_Finished, "readCode should be %d got %d", ReadReturnCode_Finished, readCode); + + // should indicate there's nothing left to read of the header + assertTrue(fwd_state->nextMessage.remainingReadLength == 0, "Remaining length should be 0 got %zu", fwd_state->nextMessage.remainingReadLength); + + // we should be at position "firstWrite" in the packet buffer + assertNotNull(fwd_state->nextMessage.packet, "Packet buffer is null"); + assertTrue(parcBuffer_Position(fwd_state->nextMessage.packet) == firstWrite, + "Wrong position, expected %zu got %zu", firstWrite, parcBuffer_Position(fwd_state->nextMessage.packet)); + + // cleanup + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); + close(fds[REMOTE]); +} + + + +static void +_testReadFromMetisFromArray(TestData *data, size_t length, uint8_t buffer[length]) +{ + // create our connection. This will become part of the RTA framework, so will be + // cleaned up in the teardown + int api_fd; + int client_fd; + RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd); + + ssize_t nwritten = write(client_fd, buffer, length); + assertTrue(nwritten == length, "Wrong write size, expected %zu got %zd", length, nwritten); + + FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);; + + _readFromMetis(fwd_state, conn); + + // now crank the handle to pop those messages up the stack + rtaFramework_NonThreadedStepCount(data->framework, 5); + + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(conn), TESTING_UPPER, RTA_DOWN); + _throwAwayControlMessage(out); + + // verify the wire format is what we wrote + TransportMessage *test_tm = rtaComponent_GetMessage(out); + assertNotNull(test_tm, "Did not receive a transport message out of the top of the connector"); + + CCNxTlvDictionary *testDictionary = transportMessage_GetDictionary(test_tm); + PARCBuffer *writeFormat = ccnxWireFormatMessage_GetWireFormatBuffer(testDictionary); + assertNotNull(writeFormat, + "transport message does not have a wire format"); + + PARCBuffer *truth = parcBuffer_Wrap(buffer, length, 0, length); + assertTrue(parcBuffer_Equals(truth, writeFormat), "Wire format does not match expected") + { + printf("Expected:\n"); + parcBuffer_Display(truth, 3); + printf("Received:\n"); + parcBuffer_Display(writeFormat, 3); + } + + parcBuffer_Release(&truth); + transportMessage_Destroy(&test_tm); +} + + +// ====================================================== + +LONGBOW_TEST_RUNNER(connector_Forwarder_Metis) +{ + LONGBOW_RUN_TEST_FIXTURE(Local); + + LONGBOW_RUN_TEST_FIXTURE(UpDirectionV1); + LONGBOW_RUN_TEST_FIXTURE(DownDirectionV1); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(connector_Forwarder_Metis) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + + snprintf(keystorename, 1024, "/tmp/keystore_%d.p12", getpid()); + + // init + fini here so there's no memory imbalance + parcSecurity_Init(); + parcPkcs12KeyStore_CreateFile(keystorename, keystorepass, "ccnxuser", 1024, 365); + parcSecurity_Fini(); + + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(connector_Forwarder_Metis) +{ + unlink(keystorename); + return LONGBOW_STATUS_SUCCEEDED; +} + +// ====================================================== +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, connector_Fwd_Metis_Init_Release); + LONGBOW_RUN_TEST_CASE(Local, connector_Fwd_Metis_Opener_GoodPort); + LONGBOW_RUN_TEST_CASE(Local, _fwdMetisState_Release); + LONGBOW_RUN_TEST_CASE(Local, _readInEnvironmentConnectionSpecification); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +// ==================================================================== + +LONGBOW_TEST_CASE(Local, connector_Fwd_Metis_Init_Release) +{ + // nothing to do, just checking that memory is in balance in teardown +} + +/** + * Call the opener with the right port. We should see a connection attempt on + * the server socket and be able to accept it. + */ +LONGBOW_TEST_CASE(Local, connector_Fwd_Metis_Opener_GoodPort) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + RtaConnection *conn = _openConnection(data, 1, fds); + assertNotNull(conn, "Got null connection opening on stack 1"); + + rtaFramework_NonThreadedStepCount(data->framework, 2); + + // we should now see a connection request + _waitForSelect(data->server_socket); + + close(fds[1]); +} + +/** + * Make sure everything is released and file descriptor is closed + */ +LONGBOW_TEST_CASE(Local, _fwdMetisState_Release) +{ + const int REMOTE = 0; + const int STACK = 1; + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + + // this replaces "_openSocket" + fwd_state->fd = fds[STACK]; + + _setupSocket(fwd_state); + + + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); + + // ensure that fds[STACK] is closed by _fwdMetisState_Release + uint8_t buffer[16]; + ssize_t nread = recv(fds[STACK], buffer, 16, 0); + assertTrue(nread == -1 && errno == EBADF, + "read from closed socket %d should be EBADF, got return %zd and errno (%d) %s", + fds[STACK], nread, errno, strerror(errno)); + + close(fds[REMOTE]); +} + +LONGBOW_TEST_CASE(Local, _readInEnvironmentConnectionSpecification) +{ + char *oldEnv = getenv(FORWARDER_CONNECTION_ENV); + setenv(FORWARDER_CONNECTION_ENV, "tcp://127.0.0.1:9999", 1); + struct sockaddr_in addr_in; + _readInEnvironmentConnectionSpecification(&addr_in); + assertTrue(addr_in.sin_port == htons(9999), "Port specification incorrectly parsed"); + assertTrue(addr_in.sin_addr.s_addr == inet_addr("127.0.0.1"), "Address specification incorrectly parsed");; + if (oldEnv) { + setenv(FORWARDER_CONNECTION_ENV, oldEnv, 1); + } else { + unsetenv(FORWARDER_CONNECTION_ENV); + } +} + + + +// ==================================================================== + + +LONGBOW_TEST_FIXTURE(UpDirectionV1) +{ + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketHeader_ExactFit); + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketHeader_TwoReads); + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _setupNextPacket); + + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacket_PartialMessage); + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacket_ExactlyOneMessage); + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacket_MoreThanOneMessage); + + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readFromMetis_ThreeMessages); + + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readFromMetis_InterestV1); + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readFromMetis_ContentObjectV1); + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readFromMetis_ControlV1); + + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketHeader_Error); + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketBody_Error); + + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketHeader_Closed); + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readPacketBody_Closed); + LONGBOW_RUN_TEST_CASE(UpDirectionV1, _readFromMetis_Closed); +} + +LONGBOW_TEST_FIXTURE_SETUP(UpDirectionV1) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(UpDirectionV1) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * Put in exactly 8 bytes. + * This should return NULL, but will set nextMessageLength to be the right thing. + * Does not drain the buffer + */ +LONGBOW_TEST_CASE(UpDirectionV1, _readPacketHeader_ExactFit) +{ + const int REMOTE = 0; + const int STACK = 1; + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + + // this replaces "_openSocket" + fwd_state->fd = fds[STACK]; + + _setupSocket(fwd_state); + + // Setup the header + uint16_t packetLength = 24; + uint8_t headerLength = 13; + uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest; + + CCNxCodecSchemaV1FixedHeader hdr = { .version = 1, .packetType = packetType, .packetLength = htons(packetLength), .headerLength = headerLength }; + + // put header in packet and write the packet + uint8_t packet[1024]; + size_t bufferReadLength = sizeof(hdr); + memcpy(packet, &hdr, bufferReadLength); + + // write out exactly the number of bytes we need + ssize_t nwritten = write(fds[REMOTE], packet, sizeof(CCNxCodecSchemaV1FixedHeader)); + assertTrue(nwritten == sizeof(CCNxCodecSchemaV1FixedHeader), "Wrong write size, expected %zu got %zd", + sizeof(CCNxCodecSchemaV1FixedHeader), nwritten); + + // test the function + ReadReturnCode readCode = _readPacketHeader(fwd_state); + assertTrue(readCode == ReadReturnCode_Finished, "readCode should be %d got %d", ReadReturnCode_Finished, readCode); + assertTrue(fwd_state->nextMessage.remainingReadLength == 0, "Remaining length should be 0 got %zu", fwd_state->nextMessage.remainingReadLength); + + // other properties are tested as part of _setupNextPacket + + // cleanup + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); + close(fds[REMOTE]); +} + +/* + * Write the fixed header in two 4 byte writes + */ +LONGBOW_TEST_CASE(UpDirectionV1, _readPacketHeader_TwoReads) +{ + const int REMOTE = 0; + const int STACK = 1; + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + + // this replaces "_openSocket" + fwd_state->fd = fds[STACK]; + + _setupSocket(fwd_state); + + // Setup the header + uint16_t packetLength = 24; + uint8_t headerLength = 13; + uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest; + + CCNxCodecSchemaV1FixedHeader hdr = { + .version = 1, + .packetType = packetType, + .packetLength = htons(packetLength), + .headerLength = headerLength + }; + + // put header in packet and write the packet + uint8_t packet[1024]; + size_t bufferReadLength = sizeof(hdr); + memcpy(packet, &hdr, bufferReadLength); + + // write out exactly the number of bytes we need + size_t firstWrite = 4; + size_t secondWrite = sizeof(CCNxCodecSchemaV1FixedHeader) - firstWrite; + + ssize_t nwritten = write(fds[REMOTE], packet, firstWrite); + assertTrue(nwritten == firstWrite, "Wrong write size, expected %zu got %zd", firstWrite, nwritten); + + ReadReturnCode readCode = _readPacketHeader(fwd_state); + assertTrue(readCode == ReadReturnCode_PartialRead, "readCode should be %d got %d", ReadReturnCode_PartialRead, readCode); + + nwritten = write(fds[REMOTE], packet + firstWrite, secondWrite); + assertTrue(nwritten == secondWrite, "Wrong write size, expected %zu got %zd", secondWrite, nwritten); + + readCode = _readPacketHeader(fwd_state); + assertTrue(readCode == ReadReturnCode_Finished, "readCode should be %d got %d", ReadReturnCode_Finished, readCode); + + assertTrue(fwd_state->nextMessage.remainingReadLength == 0, "Remaining length should be 0 got %zu", fwd_state->nextMessage.remainingReadLength); + + // other properties are tested as part of _setupNextPacket + + // cleanup + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); + close(fds[REMOTE]); +} + +LONGBOW_TEST_CASE(UpDirectionV1, _setupNextPacket) +{ + uint16_t packetLength = 24; + uint8_t headerLength = 13; + uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest; + uint8_t version = 1; + CCNxCodecSchemaV1FixedHeader hdr = { .version = version, .packetType = packetType, .packetLength = htons(packetLength), .headerLength = headerLength }; + + // setup fwd_state->nextMessage like we just read a header + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + fwd_state->nextMessage.remainingReadLength = 0; + memcpy(&fwd_state->nextMessage.fixedHeader, &hdr, sizeof(hdr)); + + // this is the truth we will test against + size_t nextMessageLength = packetLength; + + _setupNextPacket(fwd_state); + + size_t allocatedLength = parcBuffer_Capacity(fwd_state->nextMessage.packet); + size_t position = parcBuffer_Position(fwd_state->nextMessage.packet); + parcBuffer_Flip(fwd_state->nextMessage.packet); + void *buffer = parcBuffer_Overlay(fwd_state->nextMessage.packet, 0); + + assertTrue(fwd_state->nextMessage.length == nextMessageLength, "Wrong packet length, expected %zu got %zu", nextMessageLength, fwd_state->nextMessage.length); + assertTrue(fwd_state->nextMessage.packetType == packetType, "Wrong packetType, expected %u got %u", packetType, fwd_state->nextMessage.packetType); + assertTrue(fwd_state->nextMessage.version == version, "Wrong version, expected %u got %u", version, fwd_state->nextMessage.version); + assertTrue(allocatedLength == nextMessageLength, "Wrong packet buffer length, expected %zu got %zu", nextMessageLength, allocatedLength); + + // and make sure the beginning of the buffer is the fixed header + assertTrue(position == sizeof(hdr), "Wrong write position, expected %zu got %zu", sizeof(hdr), position); + assertTrue(memcmp(buffer, &hdr, sizeof(hdr)) == 0, "Beginning of buffer not the fixed header"); + + // TODO: Finish me + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); +} + +/** + * Write the fixed header plus part of the message body. + */ +LONGBOW_TEST_CASE(UpDirectionV1, _readPacket_PartialMessage) +{ + const int REMOTE = 0; + const int STACK = 1; + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + + // this replaces "_openSocket" + fwd_state->fd = fds[STACK]; + + _setupSocket(fwd_state); + + // Setup the header + uint16_t packetLength = 160; + uint8_t headerLength = 13; + uint8_t packetType = CCNxCodecSchemaV1Types_PacketType_Interest; + uint8_t version = 1; + CCNxCodecSchemaV1FixedHeader hdr = { .version = version, .packetType = packetType, .packetLength = htons(packetLength), .headerLength = headerLength }; + + // put header in packet and write the packet + uint8_t packet[1024]; + memcpy(packet, &hdr, sizeof(hdr)); + + // write out exactly the number of bytes we need + size_t firstWrite = 100; + + ssize_t nwritten = write(fds[REMOTE], packet, firstWrite); + assertTrue(nwritten == firstWrite, "Wrong write size, expected %zu got %zd", firstWrite, nwritten); + + ReadReturnCode readCode = _readPacket(fwd_state); + + assertTrue(readCode == ReadReturnCode_PartialRead, "return value should be %d got %d", ReadReturnCode_PartialRead, readCode); + + // should indicate there's nothing left to read of the header + assertTrue(fwd_state->nextMessage.remainingReadLength == 0, "Remaining length should be 0 got %zu", fwd_state->nextMessage.remainingReadLength); + + // we should be at position "firstWrite" in the packet buffer + assertNotNull(fwd_state->nextMessage.packet, "Packet buffer is null"); + assertTrue(parcBuffer_Position(fwd_state->nextMessage.packet) == firstWrite, + "Wrong position, expected %zu got %zu", firstWrite, parcBuffer_Position(fwd_state->nextMessage.packet)); + + // cleanup + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); + close(fds[REMOTE]); +} + +/** + * Write exactly one message + */ +LONGBOW_TEST_CASE(UpDirectionV1, _readPacket_ExactlyOneMessage) +{ + _testReadPacketV1(0); +} + +/** + * Write more than one message. + */ +LONGBOW_TEST_CASE(UpDirectionV1, _readPacket_MoreThanOneMessage) +{ + _testReadPacketV1(100); +} + +/** + * Make 3 messages pending on the read socket and make sure _readFromMetis delivers all + * 3 up the stack. _readFromMetis requires an RtaConnection, so we need a mock framework. + */ +LONGBOW_TEST_CASE(UpDirectionV1, _readFromMetis_ThreeMessages) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // create our connection. This will become part of the RTA framework, so will be + // cleaned up in the teardown + int api_fd; + int client_fd; + RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd); + + // Write three wire format packets up the bottom of the connector + const int loopCount = 3; + size_t writeSizes[loopCount]; + + for (int i = 0; i < loopCount; i++) { + writeSizes[i] = _sendPacketToConnectorV1(client_fd, (i + 1) * 100); + } + + FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);; + + _readFromMetis(fwd_state, conn); + + // now crank the handle to pop those messages up the stack + rtaFramework_NonThreadedStepCount(data->framework, 5); + + // now read the message out of the test component + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(conn), TESTING_UPPER, RTA_DOWN); + + // throw away the first control message + _throwAwayControlMessage(out); + + // Now read the actual messages we want to test + for (int i = 0; i < loopCount; i++) { + TransportMessage *test_tm = rtaComponent_GetMessage(out); + assertNotNull(test_tm, "Did not receive a transport message %d out of %d out of the top of the connector", i + 1, loopCount); + + assertTrue(transportMessage_IsInterest(test_tm), + "second transport message is not an interest") + { + ccnxTlvDictionary_Display(transportMessage_GetDictionary(test_tm), 0); + } + + // Make sure the transport message has the right properties + CCNxTlvDictionary *testDictionary = transportMessage_GetDictionary(test_tm); + PARCBuffer *writeFormat = ccnxWireFormatMessage_GetWireFormatBuffer(testDictionary); + assertNotNull(writeFormat, + "transport message does not have a wire format"); + + assertTrue(parcBuffer_Remaining(writeFormat) == writeSizes[i], + "Raw format message wrong length, expected %zu got %zu", + writeSizes[i], + parcBuffer_Remaining(writeFormat)); + + // cleanup + transportMessage_Destroy(&test_tm); + } + + // no extra cleanup, done in teardown +} + +LONGBOW_TEST_CASE(UpDirectionV1, _readFromMetis_InterestV1) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _testReadFromMetisFromArray(data, sizeof(v1_interest_nameA), v1_interest_nameA); +} + +LONGBOW_TEST_CASE(UpDirectionV1, _readFromMetis_ContentObjectV1) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _testReadFromMetisFromArray(data, sizeof(v1_content_nameA_crc32c), v1_content_nameA_crc32c); +} + +LONGBOW_TEST_CASE(UpDirectionV1, _readFromMetis_ControlV1) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _testReadFromMetisFromArray(data, sizeof(v1_cpi_add_route_crc32c), v1_cpi_add_route_crc32c); +} + +/* + * read from a closed socket + */ +LONGBOW_TEST_CASE(UpDirectionV1, _readPacketHeader_Closed) +{ + const int REMOTE = 0; + const int STACK = 1; + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + fwd_state->fd = fds[STACK]; + _setupSocket(fwd_state); + + // close remote side then try to write to it + close(fds[REMOTE]); + + ReadReturnCode readCode = _readPacketHeader(fwd_state); + + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); + + assertTrue(readCode == ReadReturnCode_Closed, "Wrong return code, expected %d got %d", ReadReturnCode_Closed, readCode); +} + +LONGBOW_TEST_CASE(UpDirectionV1, _readPacketBody_Closed) +{ + const int REMOTE = 0; + const int STACK = 1; + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + fwd_state->fd = fds[STACK]; + _setupSocket(fwd_state); + + ssize_t nwritten = write(fds[REMOTE], v1_interest_nameA, 8); + assertTrue(nwritten == 8, "Wrong write size, expected 8 got %zd", nwritten); + + // read the header to setup the read of the body + ReadReturnCode readCode; + + readCode = _readPacketHeader(fwd_state); + assertTrue(readCode == ReadReturnCode_Finished, "Did not read entire header"); + + // close remote side then try to write to it + close(fds[REMOTE]); + + // now try 2nd read + readCode = _readPacketBody(fwd_state); + + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); + + assertTrue(readCode == ReadReturnCode_Closed, "Wrong return code, expected %d got %d", ReadReturnCode_Closed, readCode); +} + +/* + * Set the socket to -1 to cause and error + */ +LONGBOW_TEST_CASE(UpDirectionV1, _readPacketHeader_Error) +{ + const int REMOTE = 0; + const int STACK = 1; + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + fwd_state->fd = fds[STACK]; + _setupSocket(fwd_state); + + fwd_state->fd = -1; + + // close remote side then try to write to it + + ReadReturnCode readCode = _readPacketHeader(fwd_state); + + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); + close(fds[STACK]); + close(fds[REMOTE]); + + assertTrue(readCode == ReadReturnCode_Error, "Wrong return code, expected %d got %d", ReadReturnCode_Error, readCode); +} + +/* + * Set the socket to -1 to cause and error + */ +LONGBOW_TEST_CASE(UpDirectionV1, _readPacketBody_Error) +{ + const int REMOTE = 0; + const int STACK = 1; + int fds[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, fds); + + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + fwd_state->fd = fds[STACK]; + _setupSocket(fwd_state); + + ssize_t nwritten = write(fds[REMOTE], v1_interest_nameA, 8); + assertTrue(nwritten == 8, "Wrong write size, expected 8 got %zd", nwritten); + + // read the header to setup the read of the body + ReadReturnCode readCode; + + readCode = _readPacketHeader(fwd_state); + assertTrue(readCode == ReadReturnCode_Finished, "Did not read entire header"); + + // invalidate to cause an error + fwd_state->fd = -1; + + // now try 2nd read + readCode = _readPacketBody(fwd_state); + + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); + close(fds[STACK]); + close(fds[REMOTE]); + + assertTrue(readCode == ReadReturnCode_Error, "Wrong return code, expected %d got %d", ReadReturnCode_Error, readCode); +} + +/* + * read from a closed socket. + * This should generate a Notify message that the connection is closed + */ +LONGBOW_TEST_CASE(UpDirectionV1, _readFromMetis_Closed) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // create our connection. This will become part of the RTA framework, so will be + // cleaned up in the teardown + int api_fd; + int client_fd; + RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd); + FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);; + + rtaFramework_NonThreadedStepCount(data->framework, 5); + + close(client_fd); + + _readFromMetis(fwd_state, conn); + + // now crank the handle to pop those messages up the stack + rtaFramework_NonThreadedStepCount(data->framework, 5); + + // now read the message out of the test component + PARCEventQueue *out = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(conn), TESTING_UPPER, RTA_DOWN); + + // throw away the first control message + _throwAwayControlMessage(out); + + TransportMessage *test_tm = rtaComponent_GetMessage(out); + assertNotNull(test_tm, "Did not receive a transport message out of the top of the connector"); + + assertTrue(transportMessage_IsControl(test_tm), + "second transport message is not a control") + { + ccnxTlvDictionary_Display(transportMessage_GetDictionary(test_tm), 0); + } + + // Make sure the transport message has the right properties + CCNxTlvDictionary *testDictionary = transportMessage_GetDictionary(test_tm); + assertTrue(ccnxControlFacade_IsNotification(testDictionary), "Control message is not Notification") + { + ccnxTlvDictionary_Display(testDictionary, 3); + } + + PARCJSON *json = ccnxControlFacade_GetJson(testDictionary); + NotifyStatus *notify = notifyStatus_ParseJSON(json); + assertTrue(notifyStatus_GetStatusCode(notify) == notifyStatusCode_CONNECTION_CLOSED, + "Wrong code, expected %d got %d", + notifyStatusCode_CONNECTION_CLOSED, + notifyStatus_GetStatusCode(notify)); + notifyStatus_Release(¬ify); + + // verify other properties + assertFalse(fwd_state->isConnected, "Forwarder state should show connection closed"); + + // cleanup + transportMessage_Destroy(&test_tm); + + // no extra cleanup, done in teardown +} + +LONGBOW_TEST_FIXTURE(DownDirectionV1) +{ + LONGBOW_RUN_TEST_CASE(DownDirectionV1, _queueMessageToMetis); + LONGBOW_RUN_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis); + LONGBOW_RUN_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis_TwoWrites); + LONGBOW_RUN_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis_Closed); + + LONGBOW_RUN_TEST_CASE(DownDirectionV1, connector_Fwd_Metis_Downcall_Read_Interst); + LONGBOW_RUN_TEST_CASE(DownDirectionV1, connector_Fwd_Metis_Downcall_Read_CPIRequest); +} + +LONGBOW_TEST_FIXTURE_SETUP(DownDirectionV1) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(DownDirectionV1) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/* + * _queueMessageToMetis postconditions: + * - increases the reference count to the wireFormat + * - adds the reference to fwd_output buffer + * - increments the debugging counter fwd_metis_references_queued + */ +LONGBOW_TEST_CASE(DownDirectionV1, _queueMessageToMetis) +{ + PARCEventScheduler *scheduler = parcEventScheduler_Create(); + FwdMetisState *fwd_state = connector_Fwd_Metis_CreateConnectionState(scheduler); + PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA)); + size_t expectedRefCount = parcObject_GetReferenceCount(wireFormat); + + _queueBufferMessageToMetis(wireFormat, fwd_state->metisOutputQueue); + + assertTrue(parcObject_GetReferenceCount(wireFormat) == expectedRefCount, + "Did not get right ref count for wire format, expected %zu got %" PRIu64, expectedRefCount, parcObject_GetReferenceCount(wireFormat)); + assertTrue(parcEventBuffer_GetLength(fwd_state->metisOutputQueue) == parcBuffer_Remaining(wireFormat), + "Wrong output buffer length, expected %zu got %zu", parcBuffer_Remaining(wireFormat), parcEventBuffer_GetLength(fwd_state->metisOutputQueue)); + + parcBuffer_Release(&wireFormat); + parcEventBuffer_Destroy(&fwd_state->metisOutputQueue); + _fwdMetisState_Release(&fwd_state); + parcEventScheduler_Destroy(&scheduler); +} + +/* + * Dequeue a small message to metis, should all be written out. + */ +LONGBOW_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + int api_fd; + int client_fd; + RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd); + + FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);; + + // Put data in the output queue + PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA)); + _queueBufferMessageToMetis(wireFormat, fwd_state->metisOutputQueue); + + // write it out + _dequeueMessagesToMetis(fwd_state); + rtaFramework_NonThreadedStepCount(data->framework, 5); + + // we should now be able to read it + bool readReady = _waitForSelect(client_fd); + assertTrue(readReady, "client socket %d not ready for read", client_fd); + + uint8_t testArray[sizeof(v1_interest_nameA) + 1]; + ssize_t nrecv = recv(client_fd, testArray, sizeof(testArray), 0); + + assertTrue(nrecv == sizeof(v1_interest_nameA), "Wrong read length, expected %zu got %zd", sizeof(v1_interest_nameA), nrecv); + assertTrue(memcmp(testArray, v1_interest_nameA, sizeof(v1_interest_nameA)) == 0, "Read memory does not compare"); + assertTrue(parcEventBuffer_GetLength(fwd_state->metisOutputQueue) == 0, "Metis output buffer not zero length, got %zu", parcEventBuffer_GetLength(fwd_state->metisOutputQueue)); + parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue)); + parcBuffer_Release(&wireFormat); +} + +/* + * Set the forwarder's send buffer small so it will take two writes to send the packet. + * This will test that when _dequeueMessagesToMetis cannot write the whole thing it will enable the + * write event and that metis will then trigger a second write when there's buffer space. + */ +LONGBOW_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis_TwoWrites) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + int api_fd; + int client_fd; + RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd); + + FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);; + + // set the send buffer + { + // make it slightly bigger than 1/2 + const int sendBufferSize = sizeof(v1_interest_nameA) / 2 + 1; + int res = setsockopt(fwd_state->fd, SOL_SOCKET, SO_SNDBUF, &sendBufferSize, sizeof(int)); + if (res < 0) { + if (DEBUG_OUTPUT) { + printf("%9c %s failed to set SO_SNDBUF to %d: (%d) %s\n", + ' ', __func__, sendBufferSize, errno, strerror(errno)); + } + // This is a non-fatal error + } + } + + // Put data in the output queue + PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA)); + _queueBufferMessageToMetis(wireFormat, fwd_state->metisOutputQueue); + + // write it out + _dequeueMessagesToMetis(fwd_state); + rtaFramework_NonThreadedStepCount(data->framework, 5); + + // we should now be able to read it + bool readReady = _waitForSelect(client_fd); + assertTrue(readReady, "client socket %d not ready for read", client_fd); + + uint8_t testArray[sizeof(v1_interest_nameA) + 1]; + ssize_t nrecv = recv(client_fd, testArray, sizeof(testArray), 0); + + assertTrue(nrecv == sizeof(v1_interest_nameA), "Wrong read length, expected %zu got %zd", sizeof(v1_interest_nameA), nrecv); + assertTrue(memcmp(testArray, v1_interest_nameA, sizeof(v1_interest_nameA)) == 0, "Read memory does not compare"); + assertTrue(parcEventBuffer_GetLength(fwd_state->metisOutputQueue) == 0, "Metis output buffer not zero length, got %zu", parcEventBuffer_GetLength(fwd_state->metisOutputQueue)); + parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue)); + parcBuffer_Release(&wireFormat); +} + +/* + * Dequeue a message to a closed socket + */ +LONGBOW_TEST_CASE(DownDirectionV1, _dequeueMessagesToMetis_Closed) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + int api_fd; + int client_fd; + RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd); + + FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS);; + PARCBuffer *wireFormat = parcBuffer_Wrap(v1_interest_nameA, sizeof(v1_interest_nameA), 0, sizeof(v1_interest_nameA)); + _queueBufferMessageToMetis(wireFormat, fwd_state->metisOutputQueue); + + // close remote side then try to write to it + close(client_fd); + + _dequeueMessagesToMetis(fwd_state); + rtaFramework_NonThreadedStepCount(data->framework, 5); + + parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue)); + parcBuffer_Release(&wireFormat); +} + +/** + * Sends an Interest down the stack. We need to create an Interest and encode its TLV wire format, + * then send it down the stack and make sure we receive it on a client socket. We don't actually + * run Metis in this test. + */ +LONGBOW_TEST_CASE(DownDirectionV1, connector_Fwd_Metis_Downcall_Read_Interst) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // create our connection. This will become part of the RTA framework, so will be + // cleaned up in the teardown + int api_fd; + int client_fd; + RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd); + FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS); + + // Create the interest with wire format and send it down the stack + TransportMessage *tm = trafficTools_CreateTransportMessageWithDictionaryInterest(conn, CCNxTlvDictionary_SchemaVersion_V1); + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecSchemaV1PacketEncoder_DictionaryEncode(transportMessage_GetDictionary(tm), NULL); + + ccnxWireFormatMessage_PutIoVec(transportMessage_GetDictionary(tm), vec); + ccnxCodecNetworkBufferIoVec_Release(&vec); + + // send it down the stack + PARCEventQueue *in = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(conn), TESTING_UPPER, RTA_DOWN); + rtaComponent_PutMessage(in, tm); + rtaFramework_NonThreadedStepCount(data->framework, 5); + + bool readReady = _waitForSelect(client_fd); + assertTrue(readReady, "select did not indicate read ready"); + + // now read it from out listener. It has a read timeout so if we dont get it in a reasonable amount + // of time, read will return an error about the timeout + + const size_t maxPacketLength = 1024; + uint8_t packet[maxPacketLength]; + + ssize_t readBytes = read(client_fd, packet, maxPacketLength); + assertFalse(readBytes < 0, "Got error on read: (%d) %s", errno, strerror(errno)); + + parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue)); + close(client_fd); +} + +/** + * Send an AddRoute command down the stack. It does not need a wire format in the transport message, its + * the job of the forwarder to create the Metis-specific message. + */ +LONGBOW_TEST_CASE(DownDirectionV1, connector_Fwd_Metis_Downcall_Read_CPIRequest) +{ + testUnimplemented("No way to create a v1 CPI message yet"); + +// TestData *data = longBowTestCase_GetClipBoardData(testCase); +// +// // create our connection. This will become part of the RTA framework, so will be +// // cleaned up in the teardown +// int api_fd; +// int client_fd; +// RtaConnection *conn = setupConnectionAndClientSocket(data, &api_fd, &client_fd); +// FwdMetisState *fwd_state = (FwdMetisState *) rtaConnection_GetPrivateData(conn, FWD_METIS); +// +// // now make the control message +// TransportMessage *tm = trafficTools_CreateTransportMessageWithDictionaryControl(conn, CCNxTlvDictionary_SchemaVersion_V1); +// CCNxCodecNetworkBufferIoVec *vec = ccnxCodecSchemaV1PacketEncoder_DictionaryEncode(transportMessage_GetDictionary(tm), NULL); +// ccnxWireFormatFacade_PutIoVec(transportMessage_GetDictionary(tm), vec); +// ccnxCodecNetworkBufferIoVec_Release(&vec); +// +// // send it down the stack +// PARCEventQueue *in = rtaProtocolStack_GetPutQueue(rtaConnection_GetStack(conn), TESTING_UPPER, RTA_DOWN); +// rtaComponent_PutMessage(in, tm); +// rtaFramework_NonThreadedStepCount(data->framework, 5); +// +// // now read it from out listener. It has a read timeout so if we dont get it in a reasonable amount +// // of time, read will return an error about the timeout +// +// const size_t maxPacketLength = 1024; +// uint8_t packet[maxPacketLength]; +// +// ssize_t readBytes = read(client_fd, packet, maxPacketLength); +// assertFalse(readBytes < 0, "Got error on read: (%d) %s", errno, strerror(errno)); +// parcEventBuffer_Destroy(&(fwd_state->metisOutputQueue)); +} + +// ==================================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(connector_Forwarder_Metis); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_rta_ApiConnection.c b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_rta_ApiConnection.c new file mode 100644 index 00000000..b0e937cd --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_rta_ApiConnection.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Create a non-threaded framework to test internal RTA functions + * + */ +#include "../rta_ApiConnection.c" + +#include <poll.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include <ccnx/transport/transport_rta/config/config_All.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_Commands.c> + +#include <ccnx/transport/test_tools/traffic_tools.h> + +typedef struct test_data { + PARCRingBuffer1x1 *commandRingBuffer; + PARCNotifier *commandNotifier; + RtaFramework *framework; + + int api_fds[2]; + int stackId; + + RtaProtocolStack *stack; + RtaConnection *connection; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + data->commandRingBuffer = parcRingBuffer1x1_Create(128, NULL); + data->commandNotifier = parcNotifier_Create(); + data->framework = rtaFramework_Create(data->commandRingBuffer, data->commandNotifier); + assertNotNull(data->framework, "rtaFramework_Create returned null"); + + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + + apiConnector_ProtocolStackConfig(stackConfig); + testingLower_ProtocolStackConfig(stackConfig); + protocolStack_ComponentsConfigArgs(stackConfig, apiConnector_GetName(), testingLower_GetName(), NULL); + + rtaFramework_NonThreadedStepCount(data->framework, 10); + + // Create the protocol stack + + data->stackId = 1; + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(data->stackId, stackConfig); + _rtaFramework_ExecuteCreateStack(data->framework, createStack); + rtaCommandCreateProtocolStack_Release(&createStack); + + rtaFramework_NonThreadedStepCount(data->framework, 10); + data->stack = (rtaFramework_GetProtocolStackByStackId(data->framework, data->stackId))->stack; + + // Create a connection in the stack + + int error = socketpair(AF_UNIX, SOCK_STREAM, 0, data->api_fds); + assertFalse(error, "Error creating socket pair: (%d) %s", errno, strerror(errno)); + + CCNxConnectionConfig *connConfig = ccnxConnectionConfig_Create(); + apiConnector_ConnectionConfig(connConfig); + + tlvCodec_ConnectionConfig(connConfig); + + testingLower_ConnectionConfig(connConfig); + + RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(data->stackId, data->api_fds[PAIR_OTHER], data->api_fds[PAIR_TRANSPORT], ccnxConnectionConfig_GetJson(connConfig)); + _rtaFramework_ExecuteOpenConnection(data->framework, openConnection); + rtaCommandOpenConnection_Release(&openConnection); + + rtaFramework_NonThreadedStepCount(data->framework, 10); + + data->connection = rtaConnectionTable_GetByApiFd(data->framework->connectionTable, data->api_fds[PAIR_OTHER]); + + // cleanup + + ccnxConnectionConfig_Destroy(&connConfig); + ccnxStackConfig_Release(&stackConfig); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + rtaFramework_Teardown(data->framework); + + parcRingBuffer1x1_Release(&data->commandRingBuffer); + parcNotifier_Release(&data->commandNotifier); + rtaFramework_Destroy(&data->framework); + + close(data->api_fds[0]); + close(data->api_fds[1]); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(rta_ApiConnection) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_ApiConnection) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_ApiConnection) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaApiConnection_BlockDown); + LONGBOW_RUN_TEST_CASE(Global, rtaApiConnection_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, rtaApiConnection_Create_Checks); + LONGBOW_RUN_TEST_CASE(Global, rtaApiConnection_Create_Check_ApiSocket); + + LONGBOW_RUN_TEST_CASE(Global, rtaApiConnection_UnblockDown); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + printf("Finishing testcase %s\n", longBowTestCase_GetName(testCase)); + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaApiConnection_SendToApi) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaApiConnection *apiConnection = rtaConnection_GetPrivateData(data->connection, API_CONNECTOR); + + TransportMessage *tm = trafficTools_CreateTransportMessageWithDictionaryInterest(data->connection, CCNxTlvDictionary_SchemaVersion_V1); + + RtaComponentStats *stats = rtaConnection_GetStats(data->connection, API_CONNECTOR); + rtaApiConnection_SendToApi(apiConnection, tm, stats); + rtaFramework_NonThreadedStepCount(data->framework, 10); + + // Let the dispatcher run + struct pollfd pfd = { .fd = data->api_fds[PAIR_OTHER], .events = POLLIN, .revents = 0 }; + int millisecondTimeout = 1000; + + int pollvalue = poll(&pfd, 1, millisecondTimeout); + assertTrue(pollvalue == 1, "Did not get an event from the API's side of the socket"); + + CCNxMetaMessage *testMessage; + ssize_t bytesRead = read(data->api_fds[PAIR_OTHER], &testMessage, sizeof(testMessage)); + assertTrue(bytesRead == sizeof(testMessage), "Wrong read size, got %zd expected %zd: (%d) %s", + bytesRead, sizeof(testMessage), + errno, strerror(errno)); + assertNotNull(testMessage, "Message read is NULL"); + + + assertTrue(testMessage == transportMessage_GetDictionary(tm), + "Got wrong raw message, got %p expected %p", + (void *) testMessage, (void *) transportMessage_GetDictionary(tm)); + + ccnxMetaMessage_Release(&testMessage); + transportMessage_Destroy(&tm); +} + +LONGBOW_TEST_CASE(Global, rtaApiConnection_BlockDown) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaApiConnection *apiConnection = rtaApiConnection_Create(data->connection); + + // make sure we're startined unblocked + short enabled = parcEventQueue_GetEnabled(apiConnection->bev_api); + assertTrue(enabled & PARCEventType_Read, "PARCEventType_Read is not enabled on a new Api Connector: enabled = %04X", enabled); + + rtaApiConnection_BlockDown(apiConnection); + enabled = parcEventQueue_GetEnabled(apiConnection->bev_api); + assertFalse(enabled & PARCEventType_Read, "PARCEventType_Read is still enabled after caling BlockDown: enabled = %04X", enabled); + + rtaApiConnection_Destroy(&apiConnection); +} + +LONGBOW_TEST_CASE(Global, rtaApiConnection_Create_Destroy) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + uint32_t beforeBalance = parcMemory_Outstanding(); + RtaApiConnection *apiConnection = rtaApiConnection_Create(data->connection); + assertNotNull(apiConnection, "Got null API connection"); + + rtaApiConnection_Destroy(&apiConnection); + assertNull(apiConnection, "Destroy did not null apiConnection"); + uint32_t afterBalance = parcMemory_Outstanding(); + assertTrue(beforeBalance == afterBalance, "Memory imbalance: %d", (int) (afterBalance - beforeBalance)); +} + +LONGBOW_TEST_CASE(Global, rtaApiConnection_Create_Checks) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + RtaApiConnection *apiConnection = rtaApiConnection_Create(data->connection); + assertTrue(apiConnection->api_fd == rtaConnection_GetApiFd(data->connection), + "Wrong api fd, got %d expected %d", + apiConnection->api_fd, rtaConnection_GetApiFd(data->connection)); + + assertTrue(apiConnection->transport_fd == rtaConnection_GetTransportFd(data->connection), + "Wrong api fd, got %d expected %d", + apiConnection->transport_fd, rtaConnection_GetTransportFd(data->connection)); + + assertTrue(apiConnection->connection == data->connection, + "Wrong connection, got %p expected %p", + (void *) apiConnection->connection, + (void *) data->connection); + + rtaApiConnection_Destroy(&apiConnection); +} + +LONGBOW_TEST_CASE(Global, rtaApiConnection_Create_Check_ApiSocket) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaApiConnection *apiConnection = rtaApiConnection_Create(data->connection); + + assertNotNull(apiConnection->bev_api, "API socket event null"); + + rtaApiConnection_Destroy(&apiConnection); +} + +LONGBOW_TEST_CASE(Global, rtaApiConnection_UnblockDown) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaApiConnection *apiConnection = rtaApiConnection_Create(data->connection); + + rtaApiConnection_BlockDown(apiConnection); + // we know from previous test that this puts the apiConnector in blocked state + + rtaApiConnection_UnblockDown(apiConnection); + short enabled = parcEventQueue_GetEnabled(apiConnection->bev_api); + assertTrue(enabled & PARCEventType_Read, "PARCEventType_Read is not enabled after caling UnlockDown: enabled = %04X", enabled); + + rtaApiConnection_Destroy(&apiConnection); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_ApiConnection); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/components.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/components.h new file mode 100644 index 00000000..1a07bcf3 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/components.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// components.h +// Libccnx +// + + +#ifndef Libccnx_components_h +#define Libccnx_components_h + +// Every component in the system must be defined here +// These must correspond to array indicies. +typedef enum { + API_CONNECTOR = 0, + FC_NONE = 1, + FC_VEGAS = 2, + FC_PIPELINE = 3, + // vacant = 4, + // vacant = 5, + // vacant = 6, + CODEC_NONE = 7, + CODEC_UNSPEC = 8, + CODEC_TLV = 9, + // vacant = 10, + // vacant = 11, + FWD_NONE = 12, + FWD_LOCAL = 13, + // vacant = 14, + // vacant = 15, + TESTING_UPPER = 16, + TESTING_LOWER = 17, + FWD_METIS = 19, + LAST_COMPONENT = 20, // MUST ALWAYS BE LAST + UNKNOWN_COMPONENT // MUST BE VERY LAST +} RtaComponents; + + +// This is defied in rta_ProtocolStack.c and should be kept +// in sync with RtaComponents +extern const char *RtaComponentNames[LAST_COMPONENT]; + +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta.h new file mode 100644 index 00000000..75b53950 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// rta.h +// Libccnx +// +// + +#ifndef Libccnx_rta_h +#define Libccnx_rta_h + +#include "rta_Transport.h" +#include "rta_ProtocolStack.h" +#include "rta_Connection.h" +#include "rta_Component.h" +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Component.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Component.c new file mode 100644 index 00000000..4be0c085 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Component.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_EventBuffer.h> + +#include <ccnx/transport/common/transport_Message.h> +#include <ccnx/transport/transport_rta/rta_Transport.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> + + +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +PARCEventQueue * +rtaComponent_GetOutputQueue(RtaConnection *conn, + RtaComponents component, + RtaDirection direction) +{ + RtaProtocolStack *stack; + + assertNotNull(conn, "called with null connection\n"); + + stack = rtaConnection_GetStack(conn); + assertNotNull(stack, "resolved null stack\n"); + + return rtaProtocolStack_GetPutQueue(stack, component, direction); +} + +int +rtaComponent_PutMessage(PARCEventQueue *queue, TransportMessage *tm) +{ + RtaConnection *conn = rtaConnection_GetFromTransport(tm); + assertNotNull(conn, "Got null connection from transport message\n"); + + if (rtaConnection_GetState(conn) != CONN_CLOSED) { + PARCEventBuffer *out = parcEventBuffer_GetQueueBufferOutput(queue); + int res; + + rtaConnection_IncrementMessagesInQueue(conn); + + if (DEBUG_OUTPUT) { + printf("%s queue %-12s tm %p\n", + __func__, + rtaProtocolStack_GetQueueName(rtaConnection_GetStack(conn), queue), + (void *) tm); + } + + res = parcEventBuffer_Append(out, (void *) &tm, sizeof(&tm)); + assertTrue(res == 0, "%s parcEventBuffer_Append returned error\n", __func__); + parcEventBuffer_Destroy(&out); + return 1; + } else { + // drop + transportMessage_Destroy(&tm); + + return 0; + } +} + +TransportMessage * +rtaComponent_GetMessage(PARCEventQueue *queue) +{ + PARCEventBuffer *in = parcEventBuffer_GetQueueBufferInput(queue); + + while (parcEventBuffer_GetLength(in) >= sizeof(TransportMessage *)) { + ssize_t len; + TransportMessage *tm; + RtaConnection *conn; + + len = parcEventBuffer_Read(in, (void *) &tm, sizeof(&tm)); + + assertTrue(len == sizeof(TransportMessage *), + "parcEventBuffer_Read returned error"); + + // Is the transport message for an open connection? + conn = rtaConnection_GetFromTransport(tm); + assertNotNull(conn, "%s GetInfo returnd null connection\n", __func__); + + if (DEBUG_OUTPUT) { + printf("%s queue %-12s tm %p\n", + __func__, + rtaProtocolStack_GetQueueName(rtaConnection_GetStack(conn), queue), + (void *) tm); + } + + (void) rtaConnection_DecrementMessagesInQueue(conn); + + if (rtaConnection_GetState(conn) != CONN_CLOSED) { + parcEventBuffer_Destroy(&in); + return tm; + } + + // it's a closed connection + + if (DEBUG_OUTPUT) { + printf("%s clearing connection %p reference in transport\n", + __func__, (void *) conn); + } + //drop + transportMessage_Destroy(&tm); + } + + parcEventBuffer_Destroy(&in); + return NULL; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Component.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Component.h new file mode 100644 index 00000000..24efa6aa --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Component.h @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_Component.h + * @brief <#Brief Description#> + * + * A Component is a functional block within a protocol stack. It exists + * between the API Connector (at the top) and the Forwarder Connector + * (at the bottom). All components have a similar interface. The only + * slight variation is that components betwen the Forwarder Connector + * and the Codec deal in "wire" message formats, while components above + * the connector deal with "parsed" (CCNxMessage) formats. + * + * To write a component, follow these procedures: + * 1) add your component's name to components.h enum. This is the + * symbolic name you will use for it in the code. We'll call + * it PROTO_WIZ. + * 2) Copy a skeleton, such as component_Verifier.h for your header. + * Let's call it component_Wizard.h. Inside the header, you'll + * define the "operations" structure that's exported to the system. + * @code{.c} + **#ifndef Libccnx_component_wizard_h + **#define Libccnx_component_wizard_h + * + * // Function structs for component variations + * extern ComponentOperations proto_wizard_ops; + * + **#endif + * @endcode + * + * 3) Copy a skeleton, like component_Verifier_Null.c, for your + * implementation. Let's call it component_Wizard.c. Inside + * you must: + * a) instantiate proto_wizard_ops: + * @code{.c} + * static int component_Wizard_Init(ProtocolStack *stack); + * static int component_Wizard_Opener(RtaConnection *conn); + * static void component_Wizard_Upcall_Read(PARCEventQueue *, void *conn); + * static void component_Wizard_Downcall_Read(PARCEventQueue *, void *conn); + * static int component_Wizard_Closer(RtaConnection *conn); + * static int component_Wizard_Release(ProtocolStack *stack); + * + * ComponentOperations verify_null_ops = { + * component_Wizard_Init, + * component_Wizard_Opener, + * component_Wizard_Upcall_Read, + * NULL, + * component_Wizard_Downcall_Read, + * NULL, + * component_Wizard_Closer, + * component_Wizard_Release + * }; + * @endcode + * + * These define the interface your component exposes to the stack + * Init: called once on stack creation + * Open: called once per connection Open + * UpcallRead: Called when the "upward" buffer has something to read + * DowncallRead: Called when the "downward" buffer has something to read + * Closer: called once per connection Close + * Release: called on protocol stack destruction. + * + * Optionally, you may include UpcallEvents and DowncallEvents, but + * in general those are not useful. + * + * Any of the function pointers in the "ops" may be NULL. + * + * b) Implement your Init. If you need to create a stack-wide data structure + * to track state, you would do something like this, which allocates + * memory and sticks it away in component-specific storage in the stack. + * Notice that protocolStack_SetPrivateData takes our protocol's name + * PROTO_WIZ as a parameter. + * + * @code{.c} + * static int + * component_Wizard_Init(ProtocolStack *stack) + * { + * struct mydata *data = mydata_Create(); + * protocolStack_SetPrivateData(stack, PROTO_WIZ, data); + * return 0; + * } + * @endcode + * + * c) Implement your Opener. You will very likely want to keep per-connection + * state. This follows a similar method to the Init, but in a connection. + * We squirl away the connection-specific data similarly to the stack-wide + * data. In addition, it's good practice to fetch your component's Stats + * for the connection and increment the OPENS counter for a successful open. + * + * @code{.c} + * static int + * component_Wizard_Opener(RtaConnection *connection) + * { + * ComponentStats *stats; + * struct myState *mystate; + * + * parcMemory_AlocateAndClear(&mystate, sizeof(void *), sizeof(struct api_conn_state)); + * rtaConnection_SetPrivateData(connection, PROTO_WIZ, mystate); + * + * stats = rtaConnection_GetStats(connection, PROTO_WIZ); + * stats_Increment(stats, STATS_OPENS); + * return 0; + * } + * @endcode + * + * d) Implement your Close and Release. These perform the inverse + * of the Open and Init. They should fetch your private data, if + * any, and free it: + * @code{.c} + * static int + * component_Wizard_Closer(RtaConnection *conn) + * { + * ComponentStats *stats = rtaConnection_GetStats(conn, PROTO_WIZ); + * struct myState *mystate = rtaConnection_GetPrivateData(conn, PROTO_WIZ); + * + * stats_Increment(stats, STATS_CLOSES); + * myState_Destroy(&mystate); + * return 0; + * } + * + * static int + * component_Wizard_Release(ProtocolStack *stack) + * { + * ComponentStats *stats = protocoLStack_GetStats(stack, PROTO_WIZ); + * struct myData *mydata = protocolStack_GetPrivateData(stack, PROTO_WIZ); + * + * stats_Increment(stats, STATS_CLOSES); + * myData_Destroy(&mydata); + * return 0; + * } + * @endcode + * + * d) Implement your Read handlers. They are similar for the upcall + * and downcall handlers. The main issue to be aware of is that + * you must *drain* the queue on each call. The callback is edge + * triggered. + * + * Below we show an example of the Upcall read callback, which means + * there is data from below travelling up the stack. Therefore, we + * retrieve the RTA_UP output queue to pass messages up the stack. + * The while() loop is what drains the queue. + * + * Note also that "ptr" is a pointer to the ProtocolStack that owns + * connecition (what your Init was called with). The Connection information + * rides inside the transport message, and is retrieved with a call + * to transportMessage_GetInfo(). + * + * @code{.c} + * static void + * component_Wizard_Upcall_Read(PARCEventQueue *in, PARCEvent_EventType event, void *ptr) + * { + * ProtocolStack *stack = (ProtocolStack *) ptr; + * PARCEventQueue *out = protocoStack_GetPutQueue(stack, PROTO_WIZ, RTA_UP); + * TransportMessage *tm; + * + * while( (tm = rtaComponent_GetMessage(in)) != NULL ) + * { + * RtaConnection *conn = transportMessage_GetInfo(tm); + * ComponentStats *stats = rtaConnection_GetStats(conn, PROTO_WIZ); + * CCNxMessage *msg = TransportMessage_GetCcnxMessage(tm); + * + * stats_Increment(stats, STATS_UPCALL_IN); + * + * // do something with the CCNxMessage + * + * if( rtaComponent_PutMessage(out, tm) ) + * stats_Increment(stats, STATS_UPCALL_OUT); + * } + * } + * @endcode + * + * Example: + * @code + * <#example#> + * @endcode + * + */ +/** + */ + +#ifndef Libccnx_rta_component_h +#define Libccnx_rta_component_h + +#include "components.h" +#include "rta_ComponentQueue.h" +#include "rta_ComponentStats.h" + +/** + * Init: one time initialization on first instantiation (0 success, -1 failure) + * Open: Per connection open, returns valid descriptor or -1 on failure + * upcallRead: Callback when one or more messages are available + * downcallRead: Callback when one or more messages are available. + * xEvent: Called for events on the queue + * Close: Per connection close + * Release: One time release of state when whole stack taken down + * stateChagne: Called when there is a state change related to the connection + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef struct { + int (*init)(RtaProtocolStack *stack); + int (*open)(RtaConnection *conn); + void (*upcallRead)(PARCEventQueue *queue, PARCEventType events, void *stack); + void (*upcallEvent)(PARCEventQueue *queue, PARCEventQueueEventType events, void *stack); + void (*downcallRead)(PARCEventQueue *queue, PARCEventType events, void *stack); + void (*downcallEvent)(PARCEventQueue *queue, PARCEventQueueEventType events, void *stack); + int (*close)(RtaConnection *conn); + int (*release)(RtaProtocolStack *stack); + void (*stateChange)(RtaConnection *conn); +} RtaComponentOperations; + +extern PARCEventQueue *rtaComponent_GetOutputQueue(RtaConnection *conn, + RtaComponents component, + RtaDirection direction); + +/** + * Send a message between components. The API connector and Forwarder connector + * must set the connection information in the transport message with + * rtaConnection_SetInTransport(). + * + * returns 1 on success, 0 on failure + * + * Example: + * @code + * <#example#> + * @endcode + */ +extern int rtaComponent_PutMessage(PARCEventQueue *queue, TransportMessage *tm); + +/** + * Fetch a message from the queue. Will return NULL if no message + * is available. + * + * As a side effect, it will drain message on a closed connection. + * + * Example: + * @code + * <#example#> + * @endcode + */ +extern TransportMessage *rtaComponent_GetMessage(PARCEventQueue *queue); +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentQueue.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentQueue.h new file mode 100644 index 00000000..33294cbf --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentQueue.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_ComponentQueue.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef Libccnx_rta_ComponentQueue_h +#define Libccnx_rta_ComponentQueue_h + +typedef enum { + RTA_UP = 0, + RTA_DOWN = 1 +} RtaDirection; +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentStats.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentStats.c new file mode 100644 index 00000000..3ae60a9c --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentStats.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> +#include <ccnx/transport/transport_rta/core/rta_ComponentStats.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> + +struct rta_component_stats { + RtaProtocolStack *stack; + RtaComponents type; + uint64_t stats[STATS_LAST]; +}; + +char * +rtaComponentStatType_ToString(RtaComponentStatType statsType) +{ + switch (statsType) { + case STATS_OPENS: + return "opens"; + + case STATS_CLOSES: + return "closes"; + + case STATS_UPCALL_IN: + return "upcall_in"; + + case STATS_UPCALL_OUT: + return "upcall_out"; + + case STATS_DOWNCALL_IN: + return "downcall_in"; + + case STATS_DOWNCALL_OUT: + return "downcall_out"; + + default: + trapIllegalValue(statsType, "Unknown RtaComponentStatType %d", statsType); + } +} + +/** + * Its ok to call with null stack. that just means when we increment, we won't + * also increment stack-wide stats + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaComponentStats * +rtaComponentStats_Create(RtaProtocolStack *stack, RtaComponents componentType) +{ + RtaComponentStats *stats = parcMemory_AllocateAndClear(sizeof(RtaComponentStats)); + assertNotNull(stats, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(RtaComponentStats)); + assertTrue(componentType < LAST_COMPONENT, "invalid type %d\n", componentType); + + stats->stack = stack; + stats->type = componentType; + return stats; +} + +/* Increment and return incremented value */ +uint64_t +rtaComponentStats_Increment(RtaComponentStats *stats, RtaComponentStatType statsType) +{ + assertNotNull(stats, "%s dereferenced a null stats pointer\n", __func__); + assertFalse(statsType >= STATS_LAST, "%s incorrect stat type %d\n", __func__, statsType); + stats->stats[statsType]++; + + if (stats->stack != NULL) { + RtaComponentStats *stack_stats = rtaProtocolStack_GetStats(stats->stack, stats->type); + // if stack is not null, then we must get stats from it + assertNotNull(stack_stats, "%s got null stack stats\n", __func__); + stack_stats->stats[statsType]++; + } + + return stats->stats[statsType]; +} + +/* Return value */ +uint64_t +rtaComponentStats_Get(RtaComponentStats *stats, RtaComponentStatType statsType) +{ + assertNotNull(stats, "dereferenced a null stats pointer\n"); + assertFalse(statsType >= STATS_LAST, "incorrect stat statsType %d\n", statsType); + return stats->stats[statsType]; +} + +/* dump the stats to the given output */ +void +rtaComponentStats_Dump(RtaComponentStats *stats, FILE *output) +{ +} + +void +rtaComponentStats_Destroy(RtaComponentStats **statsPtr) +{ + RtaComponentStats *stats; + assertNotNull(statsPtr, "%s got null stats pointer\n", __func__); + + stats = *statsPtr; + assertNotNull(stats, "%s dereferenced a null stats pointer\n", __func__); + + memset(stats, 0, sizeof(RtaComponentStats)); + parcMemory_Deallocate((void **) &stats); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentStats.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentStats.h new file mode 100644 index 00000000..6db22a70 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentStats.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_ComponentStats.h + * @brief <#Brief Description#> + * + * Statistics are PER CONNECTION PER COMPONENT. Therefore, a component would call + * rtaConnection_GetStats(conn, component) to access its stats. Each component must + * create its stats counter in _Open and free it in _Close. + * + * Each ProtocolStack has a PER STACK PER COMPONENT set of statistics too. When a + * component creates its stats in _Open, it passes a pointer to its stack, so when + * _Increment is called, it will increment both the component's stats and the stack's + * stats. + * + * For example: + * + * protocolStack_Init() creates stack-wide stats for each component type. + * componentX_Open(stack) creates per-connection stats for that component with + * a reference to stack using stats_Create(stack, component_type) + * componentX_Y(conn) performs some per-connection activity. It would call + * stats_Increment(rtaConnection_GetStats(conn), component_type, stat_type). + * That would increment the per-connection per-component stat and if the stack + * was not null, would increment the identical component_type, stat_type + * stat in the per-stack per-component counters. + * + * + * + */ +#ifndef Libccnx_rta_ComponentStats +#define Libccnx_rta_ComponentStats + +#include <ccnx/transport/transport_rta/core/components.h> + +struct protocol_stack; + +struct rta_component_stats; +/** + * + * @see stats_Create + */ +typedef struct rta_component_stats RtaComponentStats; + +typedef enum { + STATS_OPENS, + STATS_CLOSES, + STATS_UPCALL_IN, + STATS_UPCALL_OUT, + STATS_DOWNCALL_IN, + STATS_DOWNCALL_OUT, + STATS_LAST // must be last +} RtaComponentStatType; + +/** + * Create a stats component + * + * If the optional stack is specified, its statistics will be incremented whenever this + * stats object is incremented. Otherwise, it may be NULL. + * + * @param [in] stack Optional protocol stack + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +RtaComponentStats *rtaComponentStats_Create(struct protocol_stack *stack, RtaComponents componentType); + +/** + * <#OneLineDescription#> + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +char *rtaComponentStatType_ToString(RtaComponentStatType statType); + +/** + * Increment and return incremented value + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +uint64_t rtaComponentStats_Increment(RtaComponentStats *stats, RtaComponentStatType statType); + +/** + * Return value + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +uint64_t rtaComponentStats_Get(RtaComponentStats *stats, RtaComponentStatType statType); + +/** + * dump the stats to the given output + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void rtaComponentStats_Dump(RtaComponentStats *stats, FILE *output); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void rtaComponentStats_Destroy(RtaComponentStats **statsPtr); +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Connection.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Connection.c new file mode 100644 index 00000000..455fed8d --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Connection.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <ccnx/transport/common/transport_Message.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework_Commands.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> + +#include <ccnx/api/notify/notify_Status.h> +#include <ccnx/api/control/cpi_ControlFacade.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#ifdef DEBUG_OUTPUT +#undef DEBUG_OUTPUT +#endif + +#define DEBUG_OUTPUT 0 + +// SPEW will dump stack traces on reference count events +#define SPEW 0 + +struct rta_connection { + RtaProtocolStack *stack; + RtaFramework *framework; + + // unique id for this connection + unsigned connid; + + // opaque component-specific data and their closers + void *component_data[LAST_COMPONENT]; + RtaComponentStats *component_stats[LAST_COMPONENT]; + + RtaConnectionStateType connState; + + unsigned messages_in_queue; + unsigned refcount; + + PARCJSON *params; + + // api_fd is used in status messages up to the user + // transport_fd is used by the API connector to talk w/ API. + int api_fd; + int transport_fd; + + // is the connection blocked in the given direction? + bool blocked_down; + bool blocked_up; +}; + +RtaComponentStats * +rtaConnection_GetStats(RtaConnection *conn, RtaComponents component) +{ + assertNotNull(conn, "called with null connection\n"); + return conn->component_stats[component]; +} + +RtaConnection * +rtaConnection_Create(RtaProtocolStack *stack, const RtaCommandOpenConnection *cmdOpen) +{ + int i; + RtaConnection *conn = parcMemory_AllocateAndClear(sizeof(RtaConnection)); + assertNotNull(conn, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(RtaConnection)); + + conn->stack = stack; + conn->framework = rtaProtocolStack_GetFramework(stack); + conn->connid = rtaProtocolStack_GetNextConnectionId(stack); + conn->connState = CONN_OPEN; + conn->api_fd = rtaCommandOpenConnection_GetApiNotifierFd(cmdOpen); + conn->transport_fd = rtaCommandOpenConnection_GetTransportNotifierFd(cmdOpen); + + conn->params = parcJSON_Copy(rtaCommandOpenConnection_GetConfig(cmdOpen)); + conn->refcount = 1; + + conn->blocked_down = false; + conn->blocked_up = false; + + for (i = 0; i < LAST_COMPONENT; i++) { + conn->component_stats[i] = rtaComponentStats_Create(stack, i); + } + + if (DEBUG_OUTPUT) { + fprintf(stderr, "%9" PRIu64 " %s connection %p refcount %d\n", + rtaFramework_GetTicks(conn->framework), __func__, (void *) conn, conn->refcount); + if (SPEW) { + longBowRuntime_StackTrace(STDERR_FILENO); + } + + char *p = parcJSON_ToString(conn->params); + printf("Connection configuration: %s\n", p); + parcMemory_Deallocate((void **) &p); + } + + return conn; +} + +RtaConnection * +rtaConnection_Copy(RtaConnection *original) +{ + assertNotNull(original, "Called with null parameter"); + original->refcount++; + if (DEBUG_OUTPUT) { + fprintf(stderr, "%9" PRIu64 " %s connection %p refcount %d\n", + rtaFramework_GetTicks(original->framework), __func__, (void *) original, original->refcount); + if (SPEW) { + longBowRuntime_StackTrace(STDERR_FILENO); + } + } + + return original; +} + +void +rtaConnection_FreeFunc(void **voidPtr) +{ + rtaConnection_Destroy((RtaConnection **) voidPtr); +} + +void +rtaConnection_Destroy(RtaConnection **connPtr) +{ + int i; + RtaConnection *conn; + assertNotNull(connPtr, "called with null connection pointer\n"); + conn = *connPtr; + assertNotNull(conn, "called with null connection\n"); + assertTrue(conn->refcount > 0, "Called with 0 refcount, invalid state"); + + conn->refcount--; + if (conn->refcount > 0) { + if (DEBUG_OUTPUT) { + fprintf(stderr, "%9" PRIu64 " %s connection %p skipped, refcount %u\n", + rtaFramework_GetTicks(conn->framework), __func__, (void *) conn, conn->refcount); + if (SPEW) { + longBowRuntime_StackTrace(STDERR_FILENO); + } + } + return; + } + + assertTrue(conn->messages_in_queue == 0, "called when messages are still queued\n"); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %p\n", rtaFramework_GetTicks(conn->framework), __func__, (void *) conn); + if (SPEW) { + longBowRuntime_StackTrace(STDERR_FILENO); + } + } + + // Ok, at this point there's nothing left in queue, so we can + // get rid of the container now + + for (i = 0; i < LAST_COMPONENT; i++) { + rtaComponentStats_Destroy(&conn->component_stats[i]); + } + + rtaFramework_RemoveConnection(conn->framework, conn); + parcJSON_Release(&conn->params); + parcMemory_Deallocate((void **) &conn); + *connPtr = NULL; +} + +RtaProtocolStack * +rtaConnection_GetStack(RtaConnection *conn) +{ + assertNotNull(conn, "called with null connection\n"); + return conn->stack; +} + +/* + * Used to store per-connection state from Open. + * Should be freed in Close, but you don't need to set it NULL. + */ +void +rtaConnection_SetPrivateData(RtaConnection *conn, + RtaComponents component, + void *private) +{ + assertNotNull(conn, "called with null connection\n"); + conn->component_data[component] = private; +} + +/* + * Used to store per-connection state from Open + */ +void * +rtaConnection_GetPrivateData(RtaConnection *conn, + RtaComponents component) +{ + assertNotNull(conn, "called with null connection\n"); + return conn->component_data[component]; +} + +RtaConnectionStateType +rtaConnection_GetState(RtaConnection *conn) +{ + assertNotNull(conn, "called with null connection\n"); + return conn->connState; +} + +void +rtaConnection_SetState(RtaConnection *conn, RtaConnectionStateType connState) +{ + assertNotNull(conn, "called with null connection\n"); + conn->connState = connState; + rtaProtocolStack_ConnectionStateChange(conn->stack, conn); +} + +/* + * returns number in queue, including this one + */ +unsigned +rtaConnection_IncrementMessagesInQueue(RtaConnection *conn) +{ + assertNotNull(conn, "called with null connection\n"); + assertTrue(conn->connState != CONN_CLOSED, "%s called when connection closed\n", __func__); + conn->messages_in_queue++; + return conn->messages_in_queue; +} + +unsigned +rtaConnection_DecrementMessagesInQueue(RtaConnection *conn) +{ + assertNotNull(conn, "called with null connection\n"); + assertTrue(conn->messages_in_queue > 0, "Trying to decrement a queue with 0 messages already"); + + conn->messages_in_queue--; + return conn->messages_in_queue; +} + +int +rtaConnection_GetApiFd(RtaConnection *conn) +{ + assertNotNull(conn, "called with null connection\n"); + return conn->api_fd; +} + + +int +rtaConnection_GetTransportFd(RtaConnection *conn) +{ + assertNotNull(conn, "called with null connection\n"); + return conn->transport_fd; +} + +int +rtaConnection_GetStackId(RtaConnection *conn) +{ + return rtaProtocolStack_GetStackId(conn->stack); +} + +unsigned +rtaConnection_MessagesInQueue(RtaConnection *conn) +{ + assertNotNull(conn, "called with null connection\n"); + return conn->messages_in_queue; +} + +unsigned +rtaConnection_GetConnectionId(const RtaConnection *conn) +{ + assertNotNull(conn, "called with null connection\n"); + return conn->connid; +} + +void +rtaConnection_SendNotifyStatus(RtaConnection *conn, RtaComponents component, RtaDirection direction, const NotifyStatus *status) +{ + PARCJSON *json = notifyStatus_ToJSON(status); + + CCNxTlvDictionary *notification = ccnxControlFacade_CreateNotification(json); + parcJSON_Release(&json); + + TransportMessage *tm = transportMessage_CreateFromDictionary(notification); + ccnxTlvDictionary_Release(¬ification); + + PARCEventQueue *out = rtaComponent_GetOutputQueue(conn, component, direction); + + transportMessage_SetInfo(tm, rtaConnection_Copy(conn), rtaConnection_FreeFunc); + rtaComponent_PutMessage(out, tm); +} + +void +rtaConnection_SendStatus(RtaConnection *conn, + RtaComponents component, + RtaDirection direction, + NotifyStatusCode code, + CCNxName *optionalName, + const char *optionalMessage) +{ + NotifyStatus *status = notifyStatus_Create(conn->api_fd, code, optionalName, optionalMessage); + rtaConnection_SendNotifyStatus(conn, component, direction, status); + notifyStatus_Release(&status); +} + +RtaConnection * +rtaConnection_GetFromTransport(TransportMessage *tm) +{ + return (RtaConnection *) transportMessage_GetInfo(tm); +} + +RtaFramework * +rtaConnection_GetFramework(const RtaConnection *connection) +{ + assertNotNull(connection, "called with null connection"); + return connection->framework; +} + +PARCJSON * +rtaConnection_GetParameters(RtaConnection *conn) +{ + assertNotNull(conn, "called with null connection"); + return conn->params; +} + +bool +rtaConnection_BlockedDown(const RtaConnection *connection) +{ + assertNotNull(connection, "Parameter connection must be non-null"); + return (connection->connState != CONN_OPEN) || connection->blocked_down; +} + +bool +rtaConnection_BlockedUp(const RtaConnection *connection) +{ + assertNotNull(connection, "Parameter connection must be non-null"); + return (connection->connState != CONN_OPEN) || connection->blocked_up; +} + +void +rtaConnection_SetBlockedDown(RtaConnection *connection) +{ + assertNotNull(connection, "Parameter connection must be non-null"); + connection->blocked_down = true; + rtaProtocolStack_ConnectionStateChange(connection->stack, connection); +} + +void +rtaConnection_ClearBlockedDown(RtaConnection *connection) +{ + assertNotNull(connection, "Parameter connection must be non-null"); + connection->blocked_down = false; + rtaProtocolStack_ConnectionStateChange(connection->stack, connection); +} + +void +rtaConnection_SetBlockedUp(RtaConnection *connection) +{ + assertNotNull(connection, "Parameter connection must be non-null"); + connection->blocked_up = true; + rtaProtocolStack_ConnectionStateChange(connection->stack, connection); +} + +void +rtaConnection_ClearBlockedUp(RtaConnection *connection) +{ + assertNotNull(connection, "Parameter connection must be non-null"); + connection->blocked_up = false; + rtaProtocolStack_ConnectionStateChange(connection->stack, connection); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Connection.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Connection.h new file mode 100644 index 00000000..8619ef96 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Connection.h @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_Connection.h + * @brief <#Brief Description#> + * + * A connection embodies an API connection to the forwarder. Multiple + * connections are multiplexed over one stack. A connection, however, + * is largely independent of a particular stack. All the RTA connections + * are stored in RtaConnectionTable, which is managed by the Framework. + * + * A problem arises using queues between components, because there may + * be messages in queue that cannot be free'd without slogging through + * all the queues. + * + * Therefore, a connection tracks the number of messages in queue and + * will not be freed until all messages in queue are flushed. + * + * A connection carries an "isopen" flag. If it is false, no new + * messages can go in to the connection. Any message dequeued that + * references a closed connection discarded. + * + * Once the connection reaches 0 messages in queue, if it is closed, + * it is elegible for garbage collection. componentServq will call + * the _Destroy() method. Destroy() only works if the refcount for + * the connection is 0. If the ProtocolStack still has a reference + * to the connection, the connection will not be destroyed until + * the protocol stack calls Destroy. + * + * A Connection may live longer than its protocol stack. In the _Destroy, + * it should not make reference to the protocol stack. + * + */ +#ifndef Libccnx_Rta_Connection_h +#define Libccnx_Rta_Connection_h + +#include <sys/queue.h> +#include <ccnx/transport/common/transport.h> +#include <ccnx/transport/transport_rta/core/components.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_ComponentStats.h> +#include <ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.h> + +#include <ccnx/api/notify/notify_Status.h> + +struct rta_connection; +/** + * + * @see rtaConnection_Create + */ +typedef struct rta_connection RtaConnection; + +typedef enum { + CONN_OPEN, + CONN_CLOSED, + CONN_PAUSED +} RtaConnectionStateType; + +/** + * Create a connection and set the refcount to 1. If the connection + * pointer is stored by multiple entities, they should call + * IncrementRefcount. Calling _Destroy() decrements the refcount. + * + * transport_fd is our side of the data socketpair provided by rtaTransport. + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaConnection *rtaConnection_Create(RtaProtocolStack *stack, const RtaCommandOpenConnection *cmdOpen); + +/** + * Get a reference counted copy + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaConnection *rtaConnection_Copy(RtaConnection *original); + +/** + * Destroys the object if this call decrements the refcount to 0. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaConnection_Destroy(RtaConnection **connPtr); + +/** + * Same as _Destroy, but for using in a TransportMessage Info. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaConnection_FreeFunc(void **voidPtr); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +RtaProtocolStack *rtaConnection_GetStack(RtaConnection *connection); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +RtaFramework *rtaConnection_GetFramework(const RtaConnection *connection); + +/** + * + * Used to store per-connection state from Open. + * Should be freed in Close, but you don't need to set it NULL. + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void rtaConnection_SetPrivateData(RtaConnection *connection, RtaComponents component, void *private); + +/** + * Used to store per-connection state from Open + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void *rtaConnection_GetPrivateData(RtaConnection *connection, RtaComponents component); + +/** + * Returns the connection state (open, paused, closed) + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaConnectionStateType rtaConnection_GetState(RtaConnection *connection); + +/** + * Sets the connection state + * + * The API connector manages the connection state. open means all messages + * may flow. Paused means no new messages flow. closed means all existing + * messages will be destroyed. + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaConnection_SetState(RtaConnection *connection, RtaConnectionStateType state); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +RtaComponentStats *rtaConnection_GetStats(RtaConnection *connection, RtaComponents component); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +unsigned rtaConnection_IncrementMessagesInQueue(RtaConnection *connection); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +unsigned rtaConnection_DecrementMessagesInQueue(RtaConnection *connection); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +unsigned rtaConnection_MessagesInQueue(RtaConnection *connection); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +unsigned rtaConnection_GetConnectionId(const RtaConnection *connection); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +int rtaConnection_GetStackId(RtaConnection *connection); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +int rtaConnection_GetApiFd(RtaConnection *connection); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +int rtaConnection_GetTransportFd(RtaConnection *connection); + +/** + * Creates a status message (see ccnx/api/notify) and sends it up or down the stack. + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +extern void rtaConnection_SendStatus(RtaConnection *connection, + RtaComponents component, + RtaDirection direction, + NotifyStatusCode code, + CCNxName *optionalName, + const char *optionalMessage); + +/** + * Creates a status message (see ccnx/api/notify) and sends it up or down the stack. + * + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaConnection *rtaConnection_GetFromTransport(TransportMessage *tm); + +/** + * Creates a status message (see ccnx/api/notify) and sends it up or down the stack. + * + * <#Discussion#> + * + * @param connection + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCJSON *rtaConnection_GetParameters(RtaConnection *connection); + +/** + * Is the connection blocked in the down direction? + * + * Will return true if the connection is not open (DOWN or PAUSED state) or if the + * given direction is blocked. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return true Connection blocked, will not accept any more packets in down direction + * @return false Connection not blocked in down direction + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool rtaConnection_BlockedDown(const RtaConnection *connection); + +/** + * Is the connection blocked in the up direction? + * + * Will return true if the connection is not open (DOWN or PAUSED state) or if the + * given direction is blocked. + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return true Connection blocked, will not accept any more packets in up direction + * @return false Connection not blocked in up direction + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool rtaConnection_BlockedUp(const RtaConnection *connection); + +void rtaConnection_SetBlockedDown(RtaConnection *connection); +void rtaConnection_ClearBlockedDown(RtaConnection *connection); + +void rtaConnection_SetBlockedUp(RtaConnection *connection); +void rtaConnection_ClearBlockedUp(RtaConnection *connection); +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ConnectionTable.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ConnectionTable.c new file mode 100644 index 00000000..2b1e8d7e --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ConnectionTable.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Uses a linked list right now, but should be hash tables on the keys we use. + */ +#include <config.h> +#include <stdio.h> +#include <sys/queue.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework_Services.h> +#include <ccnx/transport/transport_rta/core/rta_ConnectionTable.h> + +#define DEBUG_OUTPUT 0 + +typedef struct rta_connection_entry { + RtaConnection *connection; + + TAILQ_ENTRY(rta_connection_entry) list; +} RtaConnectionEntry; + +struct rta_connection_table { + size_t max_elements; + size_t count_elements; + TableFreeFunc *freefunc; + TAILQ_HEAD(, rta_connection_entry) head; +}; + + +/** + * Create a connection table of the given size + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaConnectionTable * +rtaConnectionTable_Create(size_t elements, TableFreeFunc *freefunc) +{ + RtaConnectionTable *table = parcMemory_AllocateAndClear(sizeof(RtaConnectionTable)); + assertNotNull(table, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(RtaConnectionTable)); + TAILQ_INIT(&table->head); + table->max_elements = elements; + table->count_elements = 0; + table->freefunc = freefunc; + return table; +} + +/** + * Destroy the connection table, and it will call rtaConnection_Destroy() + * on each connection in the table. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void +rtaConnectionTable_Destroy(RtaConnectionTable **tablePtr) +{ + RtaConnectionTable *table; + + assertNotNull(tablePtr, "Called with null parameter"); + table = *tablePtr; + assertNotNull(table, "Called with parameter that dereferences to null"); + + while (!TAILQ_EMPTY(&table->head)) { + RtaConnectionEntry *entry = TAILQ_FIRST(&table->head); + TAILQ_REMOVE(&table->head, entry, list); + if (table->freefunc) { + table->freefunc(&entry->connection); + } + parcMemory_Deallocate((void **) &entry); + } + + parcMemory_Deallocate((void **) &table); + *tablePtr = NULL; +} + +/** + * Add a connetion to the table. Stores the reference provided (does not copy). + * Returns 0 on success, -1 on error + * + * Example: + * @code + * <#example#> + * @endcode + */ +int +rtaConnectionTable_AddConnection(RtaConnectionTable *table, RtaConnection *connection) +{ + assertNotNull(table, "Called with null parameter RtaConnectionTable"); + assertNotNull(connection, "Called with null parameter RtaConnection"); + + if (table->count_elements < table->max_elements) { + table->count_elements++; + RtaConnectionEntry *entry = parcMemory_AllocateAndClear(sizeof(RtaConnectionEntry)); + assertNotNull(entry, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(RtaConnectionEntry)); + entry->connection = connection; + TAILQ_INSERT_TAIL(&table->head, entry, list); + return 0; + } + return -1; +} + +/** + * Lookup a connection. + * Returns NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaConnection * +rtaConnectionTable_GetByApiFd(RtaConnectionTable *table, int api_fd) +{ + assertNotNull(table, "Called with null parameter RtaConnectionTable"); + + RtaConnectionEntry *entry; + TAILQ_FOREACH(entry, &table->head, list) + { + if (rtaConnection_GetApiFd(entry->connection) == api_fd) { + return entry->connection; + } + } + return NULL; +} + +/** + * Lookup a connection. + * Returns NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaConnection * +rtaConnectionTable_GetByTransportFd(RtaConnectionTable *table, int transport_fd) +{ + assertNotNull(table, "Called with null parameter RtaConnectionTable"); + + RtaConnectionEntry *entry; + TAILQ_FOREACH(entry, &table->head, list) + { + if (rtaConnection_GetTransportFd(entry->connection) == transport_fd) { + return entry->connection; + } + } + return NULL; +} + + +/** + * Remove a connection from the table, calling rtaConnection_Destroy() on it. + * Returns 0 on success, -1 if not found (or error) + * + * Example: + * @code + * <#example#> + * @endcode + */ +int +rtaConnectionTable_Remove(RtaConnectionTable *table, RtaConnection *connection) +{ + assertNotNull(table, "Called with null parameter RtaConnectionTable"); + assertNotNull(connection, "Called with null parameter RtaConnection"); + + RtaConnectionEntry *entry; + TAILQ_FOREACH(entry, &table->head, list) + { + if (entry->connection == connection) { + assertTrue(table->count_elements > 0, "Invalid state, found an entry, but count_elements is zero"); + table->count_elements--; + TAILQ_REMOVE(&table->head, entry, list); + if (table->freefunc) { + table->freefunc(&entry->connection); + } + parcMemory_Deallocate((void **) &entry); + return 0; + } + } + return -1; +} + +/** + * Remove all connections in a given stack_id, calling rtaConnection_Destroy() on it. + * Returns 0 on success, -1 if not found (or error) + * + * Example: + * @code + * <#example#> + * @endcode + */ +int +rtaConnectionTable_RemoveByStack(RtaConnectionTable *table, int stack_id) +{ + assertNotNull(table, "Called with null parameter RtaConnectionTable"); + + RtaConnectionEntry *entry = TAILQ_FIRST(&table->head); + while (entry != NULL) { + RtaConnectionEntry *temp = TAILQ_NEXT(entry, list); + if (rtaConnection_GetStackId(entry->connection) == stack_id) { + assertTrue(table->count_elements > 0, "Invalid state, found an entry, but count_elements is zero"); + table->count_elements--; + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 "%s stack_id %d conn %p\n", + rtaFramework_GetTicks(rtaConnection_GetFramework(entry->connection)), + __func__, + stack_id, + (void *) entry->connection); + } + + TAILQ_REMOVE(&table->head, entry, list); + if (table->freefunc) { + table->freefunc(&entry->connection); + } + + if (DEBUG_OUTPUT) { + printf("%9s %s FREEFUNC RETURNS\n", + " ", __func__); + } + + parcMemory_Deallocate((void **) &entry); + } + entry = temp; + } + return 0; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ConnectionTable.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ConnectionTable.h new file mode 100644 index 00000000..5cb1cb3e --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ConnectionTable.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_ConnectionTable.h + * @brief Data structure of connections. It is managed by rtaFramework. + * + */ + +#ifndef Libccnx_rta_ConnectionTable_h +#define Libccnx_rta_ConnectionTable_h + +#include "rta_Connection.h" + +struct rta_connection_table; +typedef struct rta_connection_table RtaConnectionTable; + +typedef void (TableFreeFunc)(RtaConnection **connection); + +/** + * Create a connection table of the given size. Whenever a + * connection is removed, the freefunc is called. Be sure that + * does not in turn call back in to the connection table. + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaConnectionTable *rtaConnectionTable_Create(size_t elements, TableFreeFunc *freefunc); + +/** + * Destroy the connection table, and it will call freefunc() + * on each connection in the table. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaConnectionTable_Destroy(RtaConnectionTable **tablePtr); + +/** + * Add a connetion to the table. Stores the reference provided (does not copy). + * Returns 0 on success, -1 on error + * + * Example: + * @code + * <#example#> + * @endcode + */ +int rtaConnectionTable_AddConnection(RtaConnectionTable *table, RtaConnection *connection); + +/** + * Lookup a connection. + * Returns NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaConnection *rtaConnectionTable_GetByApiFd(RtaConnectionTable *table, int api_fd); + +/** + * Lookup a connection. + * Returns NULL if not found + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaConnection *rtaConnectionTable_GetByTransportFd(RtaConnectionTable *table, int transport_fd); + +/** + * Remove a connection from the table, calling freefunc() on it. + * Returns 0 on success, -1 if not found (or error) + * + * Example: + * @code + * <#example#> + * @endcode + */ +int rtaConnectionTable_Remove(RtaConnectionTable *table, RtaConnection *connection); + +/** + * Remove all connections in a given stack_id, calling freefunc() on it. + * Returns 0 on success, -1 if not found (or error) + * + * Example: + * @code + * <#example#> + * @endcode + */ +int rtaConnectionTable_RemoveByStack(RtaConnectionTable *table, int stack_id); +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework.c new file mode 100644 index 00000000..ada629e0 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework.c @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This module implements the _Create(), _Start(), and _Destroy() methods. + * It also has various utilities for timers and events. + * + * The command channel is processed in rta_Framework_Commands.c. + * + * Example: + * @code + * <#example#> + * @endcode + */ + +#include <config.h> +#include <stdio.h> +#include <unistd.h> + +#include <errno.h> + +#include <string.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <parc/algol/parc_EventSignal.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_ArrayList.h> +#include <parc/logging/parc_LogReporterTextStdout.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> +#include <ccnx/transport/transport_rta/core/rta_ConnectionTable.h> +#include <ccnx/transport/common/transport_Message.h> +#include <ccnx/transport/common/transport_private.h> + +#include <ccnx/transport/transport_rta/connectors/connector_Api.h> +#include <ccnx/transport/transport_rta/connectors/connector_Forwarder.h> +#include <ccnx/transport/transport_rta/components/component_Codec.h> +#include <ccnx/transport/transport_rta/components/component_Flowcontrol.h> + +#include <ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +#include "rta_Framework_Commands.h" + +// =================================================== + +// event callbacks +static void _signal_cb(int signalNumber, PARCEventType event, void *arg); +static void _tick_cb(int, PARCEventType, void *); +static void transmitStatisticsCallback(int fd, PARCEventType what, void *user_data); + + +// =========================================== +// Public API (create, start, destroy) +// stop are done via the command channel +// start cannot be done via the command channel, as its not running until after start. + +void +rta_Framework_LockStatus(RtaFramework *framework) +{ + int res = pthread_mutex_lock(&framework->status_mutex); + assertTrue(res == 0, "error from pthread_mutex_lock: %d", res); +} + +void +rta_Framework_UnlockStatus(RtaFramework *framework) +{ + int res = pthread_mutex_unlock(&framework->status_mutex); + assertTrue(res == 0, "error from pthread_mutex_unlock: %d", res); +} + +void +rta_Framework_WaitStatus(RtaFramework *framework) +{ + int res = pthread_cond_wait(&framework->status_cv, &framework->status_mutex); + assertTrue(res == 0, "error from pthread_mutex_unlock: %d", res); +} + +void +rta_Framework_BroadcastStatus(RtaFramework *framework) +{ + int res = pthread_cond_broadcast(&framework->status_cv); + assertTrue(res == 0, "error from pthread_mutex_unlock: %d", res); +} + + +/** + * This is called whenever the connection table wants to free a connection. + * It should call the protocol stack's closers on the connection, then + * destroy the connection. It is called either (a) inside the worker thread, + * or (b) after the worker thread has stopped, so no locking needed. + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +rtaFramework_ConnectionTableFreeFunc(RtaConnection **connectionPtr) +{ + RtaConnection *connection; + assertNotNull(connectionPtr, "Called with null double pointer"); + connection = *connectionPtr; + assertNotNull(connection, "Parameter must not dereference to null"); + + if (rtaConnection_GetState(connection) != CONN_CLOSED) { + rtaFramework_CloseConnection(rtaConnection_GetFramework(connection), connection); + } + + rtaConnection_Destroy(connectionPtr); +} + +static void +_signal_cb(int signalNumber, PARCEventType event, void *arg) +{ +} + +static void +rtaFramework_InitializeEventScheduler(RtaFramework *framework) +{ + framework->base = parcEventScheduler_Create(); + assertNotNull(framework->base, "Could not initialize event scheduler!"); + + framework->signal_pipe = parcEventSignal_Create(framework->base, SIGPIPE, PARCEventType_Signal | PARCEventType_Persist, _signal_cb, framework); + parcEventSignal_Start(framework->signal_pipe); + + if (gettimeofday(&framework->starttime, NULL) != 0) { + perror("Error getting time of day"); + trapUnexpectedState("Could not read gettimeofday"); + } +} + +static void +rtaFramework_SetupMillisecondTimer(RtaFramework *framework) +{ + struct timeval wtnow_timeout; + + // setup a milli-second timer + wtnow_timeout.tv_sec = 0; + wtnow_timeout.tv_usec = 1000000 / WTHZ; + + framework->tick_event = parcEventTimer_Create( + framework->base, + PARCEventType_Persist, + _tick_cb, + (void *) framework); + + parcEventTimer_Start(framework->tick_event, &wtnow_timeout); +} + +static void +rtaFramework_CreateCommandChannel(RtaFramework *framework) +{ + int fd = parcNotifier_Socket(framework->commandNotifier); + + // setup a PARCEventQueue for command_fd + + // Set non-blocking flag + int flags = fcntl(fd, F_GETFL, NULL); + assertFalse(flags == -1, "fcntl failed to obtain file descriptor flags (%d)", errno); + int res = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + assertTrue(res == 0, "rtaFramework_Create failed to set socket non-blocking: %s", strerror(errno)); + + framework->commandEvent = parcEvent_Create(framework->base, fd, PARCEventType_Read | PARCEventType_Persist, rtaFramework_CommandCallback, (void *) framework); + + // The command port is the highest priority + parcEvent_SetPriority(framework->commandEvent, PARCEventPriority_Maximum); + + parcEvent_Start(framework->commandEvent); + + // the notifier socket is now ready to fire +} + +/* + * Until things get plumbed from above via control messages, we will use + * environment variables in the form "RtaFacility_facility=level" with a special facility "RtaFacility_All". + * The "All" is processed first, then more specific facilities, so one could set all to a default + * level then set specific ones to over-ride. + * + * Default log level is Error + * + * Strings: + * RtaFacility_Framework + * RtaFacility_Api + * RtaFacility_Flowcontrol + * RtaFacility_Codec + * RtaFacility_Forwarder + */ +static void +_setLogLevels(RtaFramework *framework) +{ + for (int i = 0; i < RtaLoggerFacility_END; i++) { + rtaLogger_SetLogLevel(framework->logger, i, PARCLogLevel_Error); + } + + char *levelString = getenv("RtaFacility_All"); + if (levelString) { + PARCLogLevel level = parcLogLevel_FromString(levelString); + if (level != PARCLogLevel_All) { + for (int i = 0; i < RtaLoggerFacility_END; i++) { + rtaLogger_SetLogLevel(framework->logger, i, level); + } + } + } + + // no do specific facilities + char buffer[1024]; + for (int i = 0; i < RtaLoggerFacility_END; i++) { + snprintf(buffer, 1024, "RtaFacility_%s", rtaLogger_FacilityString(i)); + levelString = getenv(buffer); + if (levelString) { + PARCLogLevel level = parcLogLevel_FromString(levelString); + if (level != PARCLogLevel_All) { + rtaLogger_SetLogLevel(framework->logger, i, level); + } + } + } +} + +/** + * Create a framework. This is a thread-safe function. + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaFramework * +rtaFramework_Create(PARCRingBuffer1x1 *commandRingBuffer, PARCNotifier *commandNotifier) +{ + RtaFramework *framework = parcMemory_AllocateAndClear(sizeof(RtaFramework)); + assertNotNull(framework, "RtaFramework parcMemory_AllocateAndClear returned null"); + + PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + framework->logger = rtaLogger_Create(reporter, parcClock_Monotonic()); + parcLogReporter_Release(&reporter); + + _setLogLevels(framework); + + // setup the event scheduler + + // mutes, condition variable, and protected state for starting + // and stopping the event thread from an outside thread. + pthread_mutex_init(&framework->status_mutex, NULL); + pthread_cond_init(&framework->status_cv, NULL); + framework->status = FRAMEWORK_INIT; + + framework->commandRingBuffer = parcRingBuffer1x1_Acquire(commandRingBuffer); + framework->commandNotifier = parcNotifier_Acquire(commandNotifier); + + framework->connid_next = 1; + TAILQ_INIT(&framework->protocols_head); + + //TODO: make 16384 configurable. + framework->connectionTable = rtaConnectionTable_Create(16384, rtaFramework_ConnectionTableFreeFunc); + assertNotNull(framework->connectionTable, "Could not allocate conneciton table"); + + rtaFramework_InitializeEventScheduler(framework); + + rtaFramework_SetupMillisecondTimer(framework); + + framework->transmit_statistics_event = parcEventTimer_Create(framework->base, + PARCEventType_Persist, + transmitStatisticsCallback, + (void *) framework); + + + rtaFramework_CreateCommandChannel(framework); + + if (rtaLogger_IsLoggable(framework->logger, RtaLoggerFacility_Framework, PARCLogLevel_Info)) { + rtaLogger_Log(framework->logger, RtaLoggerFacility_Framework, PARCLogLevel_Info, __func__, + "framework %p created", (void *) framework); + } + + return framework; +} + +static void +rtaFramework_DestroyEventScheduler(RtaFramework *framework) +{ + parcEventTimer_Destroy(&(framework->tick_event)); + parcEventTimer_Destroy(&(framework->transmit_statistics_event)); + + if (framework->signal_int != NULL) { + parcEventSignal_Destroy(&(framework->signal_int)); + } + if (framework->signal_usr1 != NULL) { + parcEventSignal_Destroy(&(framework->signal_usr1)); + } + + parcEvent_Destroy(&(framework->commandEvent)); + parcNotifier_Release(&framework->commandNotifier); + parcRingBuffer1x1_Release(&framework->commandRingBuffer); + + parcEventSignal_Destroy(&(framework->signal_pipe)); + parcEventScheduler_Destroy(&(framework->base)); +} + +void +rtaFramework_Destroy(RtaFramework **frameworkPtr) +{ + RtaFramework *framework; + + assertNotNull(frameworkPtr, "Parameter must be non-null RtaFramework double pointer"); + framework = *frameworkPtr; + assertNotNull(framework, "Parameter must dereference to non-Null RtaFramework pointer"); + + rtaLogger_Log(framework->logger, RtaLoggerFacility_Framework, PARCLogLevel_Info, __func__, + "framework %p destroy", (void *) framework); + + // status can be STOPPED or INIT. It's ok to destroy one that's never been started. + + // %%%% LOCK + rta_Framework_LockStatus(framework); + assertTrue(framework->status == FRAMEWORK_SHUTDOWN || + framework->status == FRAMEWORK_INIT || + framework->status == FRAMEWORK_TEARDOWN, + "Framework invalid state, got %d", + framework->status); + rta_Framework_UnlockStatus(framework); + // %%%% UNLOCK + + rtaConnectionTable_Destroy(&framework->connectionTable); + + rtaFramework_DestroyEventScheduler(framework); + + rtaLogger_Release(&framework->logger); + + parcMemory_Deallocate((void **) &framework); + + *frameworkPtr = NULL; +} + +RtaLogger * +rtaFramework_GetLogger(RtaFramework *framework) +{ + return framework->logger; +} + +/** + * May block briefly, returns the current status of the framework. + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaFrameworkStatus +rtaFramework_GetStatus(RtaFramework *framework) +{ + RtaFrameworkStatus status; + // %%%% LOCK + rta_Framework_LockStatus(framework); + status = framework->status; + rta_Framework_UnlockStatus(framework); + // %%%% UNLOCK + return status; +} + +/** + * Blocks until the framework status equals or exeeds the desired status + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaFrameworkStatus +rtaFramework_WaitForStatus(RtaFramework *framework, + RtaFrameworkStatus status) +{ + // %%%% LOCK + rta_Framework_LockStatus(framework); + while (framework->status < status) { + rta_Framework_WaitStatus(framework); + } + rta_Framework_UnlockStatus(framework); + // %%%% UNLOCK + + return status; +} + +// ================================================================= + +// Transport Operations + +PARCEventScheduler * +rtaFramework_GetEventScheduler(RtaFramework *framework) +{ + assertNotNull(framework, "Parameter must be non-NULL RtaFramework"); + return framework->base; +} + +unsigned +rtaFramework_GetNextConnectionId(RtaFramework *framework) +{ + assertNotNull(framework, "Parameter must be non-NULL RtaFramework"); + + return framework->connid_next++; +} + +// ============================ +// Internal functions + +/* + * This is dispatched from the event loop, so its a loosely accurate time + */ +static void +_tick_cb(int fd, PARCEventType what, void *user_data) +{ + RtaFramework *framework = (RtaFramework *) user_data; + assertTrue(what & PARCEventType_Timeout, "%s got unknown signal %d", __func__, what); + framework->clock_ticks++; + + if (framework->killme) { + int res; + + if (rtaLogger_IsLoggable(framework->logger, RtaLoggerFacility_Framework, PARCLogLevel_Debug)) { + rtaLogger_Log(framework->logger, RtaLoggerFacility_Framework, PARCLogLevel_Debug, __func__, + "framework %p exiting base loop", (void *) framework); + } + + res = parcEventScheduler_Abort(framework->base); + assertTrue(res == 0, "error on parcEventScheduler_Abort: %d", res); + } +} + +FILE *GlobalStatisticsFile = NULL; + +static void +transmitStatisticsCallback(int fd, PARCEventType what, void *user_data) +{ + RtaFramework *framework = (RtaFramework *) user_data; + assertTrue(what & PARCEventType_Timeout, "unknown signal %d", what); + + FrameworkProtocolHolder *holder; + TAILQ_FOREACH(holder, &framework->protocols_head, list) + { + RtaProtocolStack *stack = holder->stack; + PARCArrayList *list = rtaProtocolStack_GetStatistics(stack, GlobalStatisticsFile); + parcArrayList_Destroy(&list); + } +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework.h new file mode 100644 index 00000000..fbd698d1 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_Framework.h + * @brief <#Brief Description#> + * + * rtaFramework executes inside the worker thread in callback from the event scheduler. + * + * It provides service functions to components and connectors so they do not need + * to be event aware. + * + * It also manages the command channel to communicate with rtaTransport in the API's thread. + * + * _Create(), _Start(), and _Destroy() are called from the API's thread. You should not + * call _Destroy until rtaFramework_GetStatus() is FRAMEWORK_SHUTDOWN. + * + * The framework can run in threaded mode or non-threaded mode. Including this one + * header gives you both sets of operations, but they are not compatible. + * + * THREADED MODE: + * call _Create + * call _Start + * ... do work ... + * call _Shutdown + * call _Destroy + * + * NON-THREADED MODE + * call _Create + * ... do work ... + * call _Step or _StepCount or _StepTimed + * ... do work ... + * call _Step or _StepCount or _StepTimed + * ... do work ... + * call _Teardown + * call _Destroy + * + */ +#ifndef Libccnx_rta_Framework_h +#define Libccnx_rta_Framework_h + +#include <parc/concurrent/parc_RingBuffer_1x1.h> +#include <parc/concurrent/parc_Notifier.h> +#include <ccnx/transport/transport_rta/core/rta_Logger.h> + +// =================================== +// External API, used by rtaTransport + +struct rta_framework; +typedef struct rta_framework RtaFramework; + +#define RTA_MAX_PRIORITY 0 +#define RTA_NORMAL_PRIORITY 1 +#define RTA_MIN_PRIORITY 2 + +/** + * Transient states: STARTING, STOPPING. You don't want to block waiting for those + * as you could easily miss them + * + * Example: + * @code + * <#example#> + * @endcode + */ +typedef enum { + FRAMEWORK_INIT = 0, /** Initial status after Create() * + * Example: + * @code + * <#example#> + * @endcode + */ + FRAMEWORK_SETUP = 1, /** Configured in non-threaded mode * + * Example: + * @code + * <#example#> + * @endcode + */ + + FRAMEWORK_STARTING = 2, /** Between calling _Start() and the thread running * + * Example: + * @code + * <#example#> + * @endcode + */ + FRAMEWORK_RUNNING = 3, /** After event scheduler thread starts * + * Example: + * @code + * <#example#> + * @endcode + */ + FRAMEWORK_STOPPING = 4, /** When shutdown is finished, but before event scheduler exists * + * Example: + * @code + * <#example#> + * @endcode + */ + + FRAMEWORK_TEARDOWN = 5, /** After cleanup from SETUP * + * Example: + * @code + * <#example#> + * @endcode + */ + FRAMEWORK_SHUTDOWN = 6, /** After event scheduler exits * + * Example: + * @code + * <#example#> + * @endcode + */ +} RtaFrameworkStatus; + +/** + * Creates the framework context, but does not start the worker thread. + * <code>command_fd</code> is the socketpair or pipe (one-way is ok) over which + * RTATransport will send commands. + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaFramework *rtaFramework_Create(PARCRingBuffer1x1 *commandRingBuffer, PARCNotifier *commandNotifier); + + +void rtaFramework_Destroy(RtaFramework **frameworkPtr); + +/** + * Returns the Logging system used by the framework + * + * <#Paragraphs Of Explanation#> + * + * @param [in] framework An allocated RtaFramework + * + * @retval non-null The Logging system + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaLogger *rtaFramework_GetLogger(RtaFramework *framework); + +/** + * May block briefly, returns the current status of the framework. + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaFrameworkStatus rtaFramework_GetStatus(RtaFramework *framework); + +/** + * Blocks until the framework status equals or exeeds the desired status + * Transient states: STARTING, STOPPING. You don't want to block waiting for those + * as you could easily miss them + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaFrameworkStatus rtaFramework_WaitForStatus(RtaFramework *framework, + RtaFrameworkStatus status); + + +#include "rta_Framework_Threaded.h" +#include "rta_Framework_NonThreaded.h" +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Commands.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Commands.c new file mode 100644 index 00000000..a02efefa --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Commands.c @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <errno.h> +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework_private.h> + +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/commands/rta_Command.h> + +#include <parc/algol/parc_Event.h> + +#ifdef DEBUG_OUTPUT +#undef DEBUG_OUTPUT +#endif + +#define DEBUG_OUTPUT 0 + +extern FILE *GlobalStatisticsFile; + +static bool _rtaFramework_ExecuteCreateStack(RtaFramework *framework, const RtaCommandCreateProtocolStack *createStack); +static bool _rtaFramework_ExecuteDestroyStack(RtaFramework *framework, const RtaCommandDestroyProtocolStack *destroyStack); +static bool _rtaFramework_ExecuteOpenConnection(RtaFramework *framework, const RtaCommandOpenConnection *openConnection); +static bool _rtaFramework_ExecuteCloseConnection(RtaFramework *framework, const RtaCommandCloseConnection *closeConnection); +static bool _rtaFramework_ExecuteTransmitStatistics(RtaFramework *framework, const RtaCommandTransmitStatistics *transmitStats); +static bool _rtaFramework_ExecuteShutdownFramework(RtaFramework *framework); + +static void rtaFramework_DrainApiDescriptor(int fd); + +void +rtaFramework_CommandCallback(int fd, PARCEventType what, void *user_framework) +{ + RtaFramework *framework = (RtaFramework *) user_framework; + + // flag the notifier that we are starting a batch of reads + parcNotifier_PauseEvents(framework->commandNotifier); + + RtaCommand *command = NULL; + while ((command = rtaCommand_Read(framework->commandRingBuffer)) != NULL) { + // The shutdown command can broadcast a change of state before the function + // returns, so we need to free the RtaCommand before executing the shutdown. + // Therefore, we include the rtaCommand_Destroy() as part of the switch. + + if (rtaCommand_IsOpenConnection(command)) { + _rtaFramework_ExecuteOpenConnection(framework, rtaCommand_GetOpenConnection(command)); + rtaCommand_Release(&command); + } else if (rtaCommand_IsCloseConnection(command)) { + _rtaFramework_ExecuteCloseConnection(framework, rtaCommand_GetCloseConnection(command)); + rtaCommand_Release(&command); + } else if (rtaCommand_IsCreateProtocolStack(command)) { + _rtaFramework_ExecuteCreateStack(framework, rtaCommand_GetCreateProtocolStack(command)); + rtaCommand_Release(&command); + } else if (rtaCommand_IsDestroyProtocolStack(command)) { + _rtaFramework_ExecuteDestroyStack(framework, rtaCommand_GetDestroyProtocolStack(command)); + rtaCommand_Release(&command); + } else if (rtaCommand_IsTransmitStatistics(command)) { + _rtaFramework_ExecuteTransmitStatistics(framework, rtaCommand_GetTransmitStatistics(command)); + rtaCommand_Release(&command); + } else if (rtaCommand_IsShutdownFramework(command)) { + // release the command before executing shutdown + rtaCommand_Release(&command); + _rtaFramework_ExecuteShutdownFramework(framework); + } else { + rtaCommand_Display(command, 3); + rtaCommand_Release(&command); + trapUnexpectedState("Got unknown command type"); + } + } + + // resume notifications + parcNotifier_StartEvents(framework->commandNotifier); +} + +// ========================================= +// Internal command processing + +/** + * Create a protocol holder and insert it in the framework's + * protocols_head list. + * + * Example: + * @code + * <#example#> + * @endcode + */ +static FrameworkProtocolHolder * +rtaFramework_CreateProtocolHolder(RtaFramework *framework, PARCJSON *params, uint64_t kv_hash, int stack_id) +{ + // request for a new protocol stack, create it + FrameworkProtocolHolder *holder = parcMemory_AllocateAndClear(sizeof(FrameworkProtocolHolder)); + assertNotNull(holder, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(FrameworkProtocolHolder)); + + TAILQ_INSERT_TAIL(&framework->protocols_head, holder, list); + + holder->kv_hash = kv_hash; + holder->stack_id = stack_id; + + if (DEBUG_OUTPUT) { + printf("%s created protocol holder %p hash %" PRIu64 "\n", + __func__, + (void *) holder, + kv_hash); + } + + return holder; +} + +/** + * Lookup the existing protocol holder for stackid. + * Returns NULL if not found. + * + * Example: + * @code + * <#example#> + * @endcode + */ +static FrameworkProtocolHolder * +rtaFramework_GetProtocolStackByStackId(RtaFramework *framework, int stack_id) +{ + FrameworkProtocolHolder *holder; + TAILQ_FOREACH(holder, &framework->protocols_head, list) + { + if (holder->stack_id == stack_id) { + return holder; + } + } + return NULL; +} + +static bool +_rtaFramework_ExecuteCreateStack(RtaFramework *framework, const RtaCommandCreateProtocolStack *createStack) +{ + // if we're in INIT mode, we need to bump + // wait for notificaiton from event thread + if (framework->status == FRAMEWORK_INIT) { + rta_Framework_LockStatus(framework); + if (framework->status == FRAMEWORK_INIT) { + framework->status = FRAMEWORK_SETUP; + } + rta_Framework_BroadcastStatus(framework); + rta_Framework_UnlockStatus(framework); + } + + FrameworkProtocolHolder *holder = + rtaFramework_GetProtocolStackByStackId(framework, rtaCommandCreateProtocolStack_GetStackId(createStack)); + assertNull(holder, "Found a holder with stack_id %d, but we're asked to create it!", + rtaCommandCreateProtocolStack_GetStackId(createStack)); + + uint64_t kv_hash = ccnxStackConfig_HashCode(rtaCommandCreateProtocolStack_GetStackConfig(createStack)); + + // this creates it and inserts in framework->protocols_head + holder = rtaFramework_CreateProtocolHolder(framework, NULL, kv_hash, rtaCommandCreateProtocolStack_GetStackId(createStack)); + + holder->stack = + rtaProtocolStack_Create(framework, rtaCommandCreateProtocolStack_GetConfig(createStack), rtaCommandCreateProtocolStack_GetStackId(createStack)); + rtaProtocolStack_Configure(holder->stack); + + if (DEBUG_OUTPUT) { + printf("%s created protocol %p kv_hash %016" PRIX64 " stack_id %d\n", + __func__, (void *) holder->stack, kv_hash, rtaCommandCreateProtocolStack_GetStackId(createStack)); + } + return 0; +} + +static bool +_rtaFramework_ExecuteOpenConnection(RtaFramework *framework, const RtaCommandOpenConnection *openConnection) +{ + int res; + FrameworkProtocolHolder *holder; + RtaConnection *rtaConnection; + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s framework %p\n", + rtaFramework_GetTicks(framework), __func__, (void *) framework); + } + + holder = rtaFramework_GetProtocolStackByStackId(framework, rtaCommandOpenConnection_GetStackId(openConnection)); + assertNotNull(holder, "Could not find stack_id %d", rtaCommandOpenConnection_GetStackId(openConnection)); + + rtaConnection = rtaConnectionTable_GetByApiFd(framework->connectionTable, rtaCommandOpenConnection_GetApiNotifierFd(openConnection)); + assertNull(rtaConnection, "Found api_fd %d, but it should not exist!", rtaCommandOpenConnection_GetApiNotifierFd(openConnection)); + + rtaConnection = rtaConnection_Create(holder->stack, openConnection); + res = rtaConnectionTable_AddConnection(framework->connectionTable, rtaConnection); + assertTrue(res == 0, "Got error from rtaConnectionTable_AddConnection: %d", res); + + res = rtaProtocolStack_Open(holder->stack, rtaConnection); + assertTrue(res == 0, "Got error from rtaProtocolStack_Open: %d", res); + + rtaConnection_SetState(rtaConnection, CONN_OPEN); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s created connection %p stack_id %d api_fd %d transport_fd %d\n", + rtaFramework_GetTicks(framework), + __func__, + (void *) rtaConnection, + rtaCommandOpenConnection_GetStackId(openConnection), + rtaCommandOpenConnection_GetApiNotifierFd(openConnection), + rtaCommandOpenConnection_GetTransportNotifierFd(openConnection)); + } + + return true; +} + + +/** + * Mark a connection as closed. If there are no pending + * packets in queues, destroy it too. + * It's non-static because we call from rta_Framework.c + * + * Example: + * @code + * <#example#> + * @endcode + */ +int +rtaFramework_CloseConnection(RtaFramework *framework, RtaConnection *connection) +{ + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %p api_fd %d\n", + rtaFramework_GetTicks(framework), + __func__, (void *) connection, rtaConnection_GetApiFd(connection)); + } + + assertFalse(rtaConnection_GetState(connection) == CONN_CLOSED, + "connection api_fd %d is already closed", rtaConnection_GetApiFd(connection)); + + rtaConnection_SetState(connection, CONN_CLOSED); + rtaProtocolStack_Close(rtaConnection_GetStack(connection), connection); + + rtaFramework_DrainApiDescriptor(rtaConnection_GetApiFd(connection)); + + + // Remove it from the connection table, which will free our reference to it. + + rtaConnectionTable_Remove(framework->connectionTable, connection); + + // Done. The rtaConnection will be removed when the last queued + // messages for it are gone. We keep the connection holder, so if we do + // a Destroy we'll know about it. RtaConnection will call + // rtaFramework_RemoveConnection(...) when RtaConnection_Destroy() refcount + // is zero and it's going to fully remove the connection. + + return 0; +} + + +static bool +_rtaFramework_ExecuteCloseConnection(RtaFramework *framework, const RtaCommandCloseConnection *closeConnection) +{ + RtaConnection *connection = rtaConnectionTable_GetByApiFd(framework->connectionTable, rtaCommandCloseConnection_GetApiNotifierFd(closeConnection)); + assertNotNull(connection, "Could not find api_fd %d", rtaCommandCloseConnection_GetApiNotifierFd(closeConnection)); + + return (rtaFramework_CloseConnection(framework, connection) == 0); +} + +/** + * When the transport is closing the descriptor + * to the API, it should call this to drain any pending but unretrieved + * messages out of the API's side of the socket + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +rtaFramework_DrainApiDescriptor(int fd) +{ + unsigned count = 0; + + if (DEBUG_OUTPUT) { + printf("%s fd %d\n", __func__, fd); + } + + // Set non-blocking flag + int flags = fcntl(fd, F_GETFL, NULL); + assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno); + int failure = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno); + + // Now drain the user side of stuff they have not read + CCNxMetaMessage *msg; + while (read(fd, &msg, sizeof(CCNxMetaMessage *)) == sizeof(CCNxMetaMessage *)) { + count++; + ccnxMetaMessage_Release(&msg); + } + + if (DEBUG_OUTPUT) { + printf("%s destroyed %u messages\n", __func__, count); + } +} + +/** + * This is a deferred callback from the RtaConnection when its last TransportMessage + * has been purged from the queues. + * + * Don't call anything inside here that ends up back in the RtaConnection. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void +rtaFramework_RemoveConnection(RtaFramework *framework, RtaConnection *rtaConnection) +{ + rtaFramework_DrainApiDescriptor(rtaConnection_GetApiFd(rtaConnection)); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s connection %p closing api_fd %d\n", + rtaFramework_GetTicks(framework), + __func__, (void *) rtaConnection, rtaConnection_GetApiFd(rtaConnection)); + } + + close(rtaConnection_GetApiFd(rtaConnection)); + close(rtaConnection_GetTransportFd(rtaConnection)); +} + +void +rtaFramework_DestroyProtocolHolder(RtaFramework *framework, FrameworkProtocolHolder *holder) +{ + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s proto_holder %p\n", + rtaFramework_GetTicks(framework), + __func__, (void *) holder); + } + + // remove any and all connections associated with this protocol stack. + // If the connections still have packets floating around in queues, the connection + // will stay around until they all get flushed then will destroy on + // the last packet destruction + rtaConnectionTable_RemoveByStack(framework->connectionTable, holder->stack_id); + + rtaProtocolStack_Destroy(&holder->stack); + + TAILQ_REMOVE(&framework->protocols_head, holder, list); + + parcMemory_Deallocate((void **) &holder); +} + + +static bool +_rtaFramework_ExecuteDestroyStack(RtaFramework *framework, const RtaCommandDestroyProtocolStack *destroyStack) +{ + FrameworkProtocolHolder *holder = rtaFramework_GetProtocolStackByStackId(framework, rtaCommandDestroyProtocolStack_GetStackId(destroyStack)); + assertNotNull(holder, "Could not find stack_id %d", rtaCommandDestroyProtocolStack_GetStackId(destroyStack)); + + rtaConnectionTable_RemoveByStack(framework->connectionTable, rtaCommandDestroyProtocolStack_GetStackId(destroyStack)); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s proto_holder %p\n", + rtaFramework_GetTicks(framework), + __func__, (void *) holder); + } + + rtaFramework_DestroyProtocolHolder(framework, holder); + + return true; +} + +/** + * This will update the shared framework->status, so needs a lock around + * the work it does. + * + * Example: + * @code + * <#example#> + * @endcode + */ +static bool +_rtaFramework_ExecuteShutdownFramework(RtaFramework *framework) +{ + FrameworkProtocolHolder *holder; + + // %%% LOCK + rta_Framework_LockStatus(framework); + if (framework->status != FRAMEWORK_RUNNING) { + RtaFrameworkStatus status = framework->status; + rta_Framework_UnlockStatus(framework); + // %%% UNLOCK + assertTrue(0, "Invalid state, expected FRAMEWORK_RUNNING or later, got %d", status); + return -1; + } + + holder = TAILQ_FIRST(&framework->protocols_head); + while (holder != NULL) { + FrameworkProtocolHolder *temp = TAILQ_NEXT(holder, list); + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s stack_id %d\n", + framework->clock_ticks, __func__, holder->stack_id); + } + + rtaFramework_DestroyProtocolHolder(framework, holder); + holder = temp; + } + + parcEventScheduler_Stop(framework->base, &(struct timeval) { .tv_sec = 0, .tv_usec = 1000 }); + framework->status = FRAMEWORK_STOPPING; + rta_Framework_BroadcastStatus(framework); + rta_Framework_UnlockStatus(framework); + // %%% UNLOCK + + return 0; +} + +// Goes into rta_Framework_Commands.c +static bool +_rtaFramework_ExecuteTransmitStatistics(RtaFramework *framework, const RtaCommandTransmitStatistics *transmitStats) +{ + if (GlobalStatisticsFile != NULL) { + fclose(GlobalStatisticsFile); + } + + GlobalStatisticsFile = fopen(rtaCommandTransmitStatistics_GetFilename(transmitStats), "a"); + assertNotNull(GlobalStatisticsFile, "Failed to open %s", rtaCommandTransmitStatistics_GetFilename(transmitStats)); + + if (GlobalStatisticsFile != NULL) { + struct timeval period = rtaCommandTransmitStatistics_GetPeriod(transmitStats); + parcEventTimer_Start(framework->transmit_statistics_event, &period); + } else { + fprintf(stderr, "Will not report statistics: Failed to open %s for output.", rtaCommandTransmitStatistics_GetFilename(transmitStats)); + } + + return 0; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Commands.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Commands.h new file mode 100644 index 00000000..52f6c2d4 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Commands.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file <#filename#> + * @brief Process the commands from RTATransport + * + * <#Detailed Description#> + * + */ +#ifndef Libccnx_rta_Framework_Commands_h +#define Libccnx_rta_Framework_Commands_h + +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_private.h> + +#include <parc/algol/parc_Event.h> + +/** + * RtaConnection will call this when RtaConnection_Destroy() refcount reaches + * zero and it's actually going to destroy a connection. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void +rtaFramework_RemoveConnection(RtaFramework *framework, RtaConnection *rtaConneciton); + +/** + * called by event scheduler for activity on the Command channel + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaFramework_CommandCallback(int fd, PARCEventType what, void *user_framework); +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.c new file mode 100644 index 00000000..158f3ec0 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * * + * Example: + * @code + * <#example#> + * @endcode + */ + +#include <config.h> +#include <stdio.h> +#include <unistd.h> + +#include <errno.h> + +#include <string.h> +#include <fcntl.h> +#include <sys/socket.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> + +#include "rta_Framework.h" +#include "rta_ConnectionTable.h" +#include "rta_Framework_Commands.h" + +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +// This is implemented in rta_Framework_Commands +void +rtaFramework_DestroyProtocolHolder(RtaFramework *framework, FrameworkProtocolHolder *holder); + +/** + * If running in non-threaded mode (you don't call _Start), you must manually + * turn the crank. This turns it for a single cycle. + * Return 0 on success, -1 on error (likely you're running in threaded mode) + * + * Example: + * @code + * <#example#> + * @endcode + */ +int +rtaFramework_NonThreadedStep(RtaFramework *framework) +{ + if (framework->status == FRAMEWORK_INIT) { + framework->status = FRAMEWORK_SETUP; + } + + assertTrue(framework->status == FRAMEWORK_SETUP, + "Framework invalid state for non-threaded, expected %d got %d", + FRAMEWORK_SETUP, + framework->status + ); + + if (framework->status != FRAMEWORK_SETUP) { + return -1; + } + + if (parcEventScheduler_Start(framework->base, PARCEventSchedulerDispatchType_LoopOnce) < 0) { + return -1; + } + + return 0; +} + +/** + * If running in non-threaded mode (you don't call _Start), you must manually + * turn the crank. This turns it for a number of cycles. + * Return 0 on success, -1 on error (likely you're running in threaded mode) + * + * Example: + * @code + * <#example#> + * @endcode + */ +int +rtaFramework_NonThreadedStepCount(RtaFramework *framework, unsigned count) +{ + if (framework->status == FRAMEWORK_INIT) { + framework->status = FRAMEWORK_SETUP; + } + + assertTrue(framework->status == FRAMEWORK_SETUP, + "Framework invalid state for non-threaded, expected %d got %d", + FRAMEWORK_SETUP, + framework->status + ); + + if (framework->status != FRAMEWORK_SETUP) { + return -1; + } + + while (count-- > 0) { + if (parcEventScheduler_Start(framework->base, PARCEventSchedulerDispatchType_LoopOnce) < 0) { + return -1; + } + } + return 0; +} + +/** + * If running in non-threaded mode (you don't call _Start), you must manually + * turn the crank. This turns it for a given amount of time. + * Return 0 on success, -1 on error (likely you're running in threaded mode) + * + * Example: + * @code + * <#example#> + * @endcode + */ +int +rtaFramework_NonThreadedStepTimed(RtaFramework *framework, struct timeval *duration) +{ + if (framework->status == FRAMEWORK_INIT) { + framework->status = FRAMEWORK_SETUP; + } + + assertTrue(framework->status == FRAMEWORK_SETUP, + "Framework invalid state for non-threaded, expected %d got %d", + FRAMEWORK_SETUP, + framework->status + ); + + if (framework->status != FRAMEWORK_SETUP) { + return -1; + } + + parcEventScheduler_Stop(framework->base, duration); + + if (parcEventScheduler_Start(framework->base, 0) < 0) { + return -1; + } + return 0; +} + + +/** + * After a protocol stack is created, you need to Teardown. If you + * are running in threaded mode (did a _Start), you should send an asynchronous + * SHUTDOWN command instead. This function only works if in the SETUP state + * + * Example: + * @code + * <#example#> + * @endcode + */ +int +rtaFramework_Teardown(RtaFramework *framework) +{ + FrameworkProtocolHolder *holder; + + assertNotNull(framework, "called with null framework"); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s framework %p\n", + rtaFramework_GetTicks(framework), + __func__, (void *) framework); + } + + // %%% LOCK + rta_Framework_LockStatus(framework); + if (framework->status != FRAMEWORK_SETUP) { + RtaFrameworkStatus status = framework->status; + rta_Framework_UnlockStatus(framework); + // %%% UNLOCK + assertTrue(0, "Invalid state, expected FRAMEWORK_SETUP, got %d", status); + return -1; + } + + holder = TAILQ_FIRST(&framework->protocols_head); + while (holder != NULL) { + FrameworkProtocolHolder *temp = TAILQ_NEXT(holder, list); + rtaFramework_DestroyProtocolHolder(framework, holder); + holder = temp; + } + + framework->status = FRAMEWORK_TEARDOWN; + rta_Framework_BroadcastStatus(framework); + rta_Framework_UnlockStatus(framework); + // %%% UNLOCK + + return 0; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.h new file mode 100644 index 00000000..ca193c83 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_Framework_NonThreaded.h + * @brief Implementation of the non-threaded api. + * + * Unless you call one of the _Step methods frequently, the tick clock will be off. + * + */ +#ifndef Libccnx_rta_Framework_NonThreaded_h +#define Libccnx_rta_Framework_NonThreaded_h + +#include <sys/time.h> + +// ============================== +// NON-THREADED API + +/** + * If running in non-threaded mode (you don't call _Start), you must manually + * turn the crank. This turns it for a single cycle. + * Return 0 on success, -1 on error (likely you're running in threaded mode) + * + * Example: + * @code + * <#example#> + * @endcode + */ +int rtaFramework_NonThreadedStep(RtaFramework *framework); + +/** + * If running in non-threaded mode (you don't call _Start), you must manually + * turn the crank. This turns it for a number of cycles. + * Return 0 on success, -1 on error (likely you're running in threaded mode) + * + * Example: + * @code + * <#example#> + * @endcode + */ +int rtaFramework_NonThreadedStepCount(RtaFramework *framework, unsigned count); + +/** + * If running in non-threaded mode (you don't call _Start), you must manually + * turn the crank. This turns it for a given amount of time. + * Return 0 on success, -1 on error (likely you're running in threaded mode) + * + * Example: + * @code + * <#example#> + * @endcode + */ +int rtaFramework_NonThreadedStepTimed(RtaFramework *framework, struct timeval *duration); + + +/** + * After a protocol stack is created, you need to Teardown. If you + * are running in threaded mode (did a _Start), you should send an asynchronous + * SHUTDOWN command instead. This function only works if in the SETUP state + * + * Example: + * @code + * <#example#> + * @endcode + */ +int rtaFramework_Teardown(RtaFramework *framework); +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Services.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Services.c new file mode 100644 index 00000000..cf2c8cd3 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Services.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> + +#include <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> +#include <sys/queue.h> + +#include "rta_Framework.h" +#include "rta_Framework_private.h" +#include "rta_Framework_Services.h" + +ticks +rtaFramework_GetTicks(RtaFramework *framework) +{ + assertNotNull(framework, "Parameter framework cannot be null"); + return framework->clock_ticks; +} + +uint64_t +rtaFramework_TicksToUsec(ticks tick) +{ + return FC_USEC_PER_TICK * tick; +} + +ticks +rtaFramework_UsecToTicks(unsigned usec) +{ + return MSEC_TO_TICKS(usec / 1000); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Services.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Services.h new file mode 100644 index 00000000..f9adf194 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Services.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_Framework_Services.h + * @brief Miscellaneous services offered by the Framework for components and connectors + * + * <#Detailed Description#> + * + */ +#ifndef Libccnx_rta_Framework_Services_h +#define Libccnx_rta_Framework_Services_h + +#include "rta_Framework.h" + +#include <parc/algol/parc_EventScheduler.h> + +// =================================== + +typedef uint64_t ticks; +#define TICK_CMP(a, b) ((int64_t) a - (int64_t) b) + +/** + * <#One Line Description#> + * + * If a component wants to use the event scheduler to manage sockets, it + * can get a reference to the event base to manage those things + * + * @param [in] framework <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +PARCEventScheduler *rtaFramework_GetEventScheduler(RtaFramework *framework); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] framework <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +unsigned rtaFramework_GetNextConnectionId(RtaFramework *framework); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] framework <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +ticks rtaFramework_GetTicks(RtaFramework *framework); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] tick <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +extern uint64_t rtaFramework_TicksToUsec(ticks tick); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] usec <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +extern ticks rtaFramework_UsecToTicks(unsigned usec); +#endif // Libccnx_rta_Framework_Services_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Threaded.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Threaded.c new file mode 100644 index 00000000..ade2c0a1 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Threaded.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include <stdio.h> +#include <unistd.h> +#include <pthread.h> + +#include <errno.h> + +#include <string.h> +#include <fcntl.h> +#include <sys/socket.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> + +#include "rta_Framework.h" +#include "rta_ConnectionTable.h" +#include "rta_Framework_Commands.h" + +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +// the thread function +static void *_rtaFramework_Run(void *ctx); + +/** + * Starts the worker thread. Blocks until started + * + * Example: + * @code + * <#example#> + * @endcode + */ +void +rtaFramework_Start(RtaFramework *framework) +{ + pthread_attr_t attr; + + // ensure we're in the INIT state, then bump to STARTING + // %%% LOCK + rta_Framework_LockStatus(framework); + if (framework->status == FRAMEWORK_INIT) { + framework->status = FRAMEWORK_STARTING; + rta_Framework_BroadcastStatus(framework); + rta_Framework_UnlockStatus(framework); + // %%% UNLOCK + } else { + RtaFrameworkStatus status = framework->status; + rta_Framework_UnlockStatus(framework); + // %%% UNLOCK + assertTrue(0, "Invalid state, not FRAMEWORK_INIT, got %d", status); + return; + } + + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + if (pthread_create(&framework->thread, &attr, _rtaFramework_Run, framework) != 0) { + perror("pthread_create"); + exit(EXIT_FAILURE); + } + + if (DEBUG_OUTPUT) { + printf("%s framework started %p\n", __func__, (void *) framework); + } + + // wait for notificaiton from event thread + rta_Framework_LockStatus(framework); + while (framework->status == FRAMEWORK_INIT) { + rta_Framework_WaitStatus(framework); + } + rta_Framework_UnlockStatus(framework); + + if (DEBUG_OUTPUT) { + printf("%s framework running %p\n", __func__, (void *) framework); + } +} + +static void * +_rtaFramework_Run(void *ctx) +{ + RtaFramework *framework = (RtaFramework *) ctx; + + // %%% LOCK + rta_Framework_LockStatus(framework); + if (framework->status != FRAMEWORK_STARTING) { + assertTrue(0, "Invalid state, expected before %d, got %d", FRAMEWORK_STARTING, framework->status); + rta_Framework_UnlockStatus(framework); + // %%% UNLOCK + pthread_exit(NULL); + } + framework->status = FRAMEWORK_RUNNING; + + // Set our thread name, only used to diagnose a crash or in debugging +#if __APPLE__ + pthread_setname_np("RTA Framework"); +#else + pthread_setname_np(framework->thread, "RTA Framework"); +#endif + + rta_Framework_BroadcastStatus(framework); + rta_Framework_UnlockStatus(framework); + // %%% UNLOCK + + if (DEBUG_OUTPUT) { + const int bufferLength = 1024; + char frameworkName[bufferLength]; + pthread_getname_np(framework->thread, frameworkName, bufferLength); + printf("Framework thread running: '%s'\n", frameworkName); + } + + // blocks + parcEventScheduler_Start(framework->base, PARCEventSchedulerDispatchType_Blocking); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s existed parcEventScheduler_Start\n", framework->clock_ticks, __func__); + } + + // %%% LOCK + rta_Framework_LockStatus(framework); + framework->status = FRAMEWORK_SHUTDOWN; + rta_Framework_BroadcastStatus(framework); + rta_Framework_UnlockStatus(framework); + // %%% UNLOCK + + pthread_exit(NULL); +} + +/** + * Stops the worker thread by sending a CommandShutdown. + * Blocks until shutdown complete. + * + * CALLED FROM API's THREAD + * + * Example: + * @code + * <#example#> + * @endcode + */ +void +rtaFramework_Shutdown(RtaFramework *framework) +{ + RtaCommand *shutdown = rtaCommand_CreateShutdownFramework(); + rtaCommand_Write(shutdown, framework->commandRingBuffer); + parcNotifier_Notify(framework->commandNotifier); + rtaCommand_Release(&shutdown); + + // now block on reading status + rtaFramework_WaitForStatus(framework, FRAMEWORK_SHUTDOWN); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Threaded.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Threaded.h new file mode 100644 index 00000000..ba6e9cb3 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Threaded.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_Framework_Threaded.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef Libccnx_rta_Framework_Threaded_h +#define Libccnx_rta_Framework_Threaded_h + +// ============================= +// THREADED + +/** + * Starts the worker thread. Blocks until started. + * + * CALLED FROM API's THREAD + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaFramework_Start(RtaFramework *framework); + +/** + * Stops the worker thread by sending a CommandShutdown. + * Blocks until shutdown complete. + * + * The caller must provider their side of the command channel + * + * CALLED FROM API's THREAD + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaFramework_Shutdown(RtaFramework *framework); + +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_private.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_private.h new file mode 100644 index 00000000..ffc386c5 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_private.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_Framework_private.h + * @brief <#Brief Description#> + * + * <#Detailed Description#> + * + */ +#ifndef Libccnx_rta_Framework_private_h +#define Libccnx_rta_Framework_private_h + +#include <stdlib.h> +#include <sys/queue.h> +#include <pthread.h> + +#include "rta_ProtocolStack.h" +#include "rta_Connection.h" +#include "rta_Framework_Services.h" + +#include "rta_ConnectionTable.h" + +#include <parc/algol/parc_EventScheduler.h> +#include <parc/algol/parc_Event.h> +#include <parc/algol/parc_EventTimer.h> +#include <parc/algol/parc_EventSignal.h> + +// the router's wrapped time frquency is 1 msec +#define WTHZ 1000 +#define FC_MSEC_PER_TICK (1000 / WTHZ) +#define FC_USEC_PER_TICK (1000000 / WTHZ) +#define MSEC_TO_TICKS(msec) ((msec < FC_MSEC_PER_TICK) ? 1 : msec / FC_MSEC_PER_TICK) + +// =================================================== + +typedef struct framework_protocol_holder { + RtaProtocolStack *stack; + uint64_t kv_hash; + int stack_id; + + TAILQ_ENTRY(framework_protocol_holder) list; +} FrameworkProtocolHolder; + + +struct rta_framework { + PARCRingBuffer1x1 *commandRingBuffer; + PARCNotifier *commandNotifier; + PARCEvent *commandEvent; + + //struct event_config *cfg; + int udp_socket; + + PARCEventScheduler *base; + + PARCEventSignal *signal_int; + PARCEventSignal *signal_usr1; + PARCEventTimer *tick_event; + PARCEvent *udp_event; + PARCEventTimer *transmit_statistics_event; + PARCEventSignal *signal_pipe; + + struct timeval starttime; + ticks clock_ticks; // at WTHZ + + // used by seed48 and nrand48 + unsigned short seed[3]; + + pthread_t thread; + + unsigned connid_next; + + // operations that modify global state need + // to be locked. + pthread_mutex_t status_mutex; + pthread_cond_t status_cv; + RtaFrameworkStatus status; + + // signals from outside control thread to event scheduler + // that it should exit its event loop. This does + // not need to be protected in mutex (its not + // a condition variable). We check for this + // inside the HZ timer callback. + bool killme; + + // A list of all our in-use protocol stacks + TAILQ_HEAD(, framework_protocol_holder) protocols_head; + + RtaConnectionTable *connectionTable; + + RtaLogger *logger; +}; + +int rtaFramework_CloseConnection(RtaFramework *framework, RtaConnection *connection); + +/** + * Lock the frameworks state machine status + * + * Will block until the state machine status is locked + * + * @param [in] framework An allocated framework + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rta_Framework_LockStatus(RtaFramework *framework); + +/** + * Unlock the state mahcines status + * + * Will assert if we do not currently hold the lock + * + * @param [in] framework An allocated framework + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rta_Framework_UnlockStatus(RtaFramework *framework); + +/** + * Wait on the state machine's condition variable to be signaled + * + * <#Paragraphs Of Explanation#> + * + * @param [in] framework An allocated framework + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rta_Framework_WaitStatus(RtaFramework *framework); + +/** + * Broadcast on the state machine's condition variable (signal it) + * + * <#Paragraphs Of Explanation#> + * + * @param [in] framework An allocated framework + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rta_Framework_BroadcastStatus(RtaFramework *framework); +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Logger.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Logger.c new file mode 100644 index 00000000..b8cc8d12 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Logger.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <config.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> + +#include <LongBow/runtime.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_Object.h> + +#include <parc/logging/parc_Log.h> +#include <ccnx/transport/transport_rta/core/rta_Logger.h> + +struct rta_logger { + PARCClock *clock; + + PARCLogReporter *reporter; + PARCLog *loggerArray[RtaLoggerFacility_END]; +}; + +static const struct facility_to_string { + RtaLoggerFacility facility; + const char *string; +} _facilityToString[] = { + { .facility = RtaLoggerFacility_Framework, .string = "Framework" }, + { .facility = RtaLoggerFacility_ApiConnector, .string = "Api" }, + { .facility = RtaLoggerFacility_Flowcontrol, .string = "Flowcontrol" }, + { .facility = RtaLoggerFacility_Codec, .string = "Codec" }, + { .facility = RtaLoggerFacility_ForwarderConnector, .string = "Forwarder" }, + { .facility = 0, .string = NULL } +}; + +const char * +rtaLogger_FacilityString(RtaLoggerFacility facility) +{ + for (int i = 0; _facilityToString[i].string != NULL; i++) { + if (_facilityToString[i].facility == facility) { + return _facilityToString[i].string; + } + } + return "Unknown"; +} + +static void +_allocateLoggers(RtaLogger *logger, PARCLogReporter *reporter) +{ + trapUnexpectedStateIf(logger->reporter != NULL, "Trying to allocate a reporter when the previous one is not null"); + logger->reporter = parcLogReporter_Acquire(reporter); + + char hostname[255]; + int gotHostName = gethostname(hostname, 255); + if (gotHostName < 0) { + snprintf(hostname, 255, "unknown"); + } + + for (int i = 0; i < RtaLoggerFacility_END; i++) { + logger->loggerArray[i] = parcLog_Create(hostname, rtaLogger_FacilityString(i), "rta", logger->reporter); + parcLog_SetLevel(logger->loggerArray[i], PARCLogLevel_Error); + } +} + +static void +_releaseLoggers(RtaLogger *logger) +{ + for (int i = 0; i < RtaLoggerFacility_END; i++) { + parcLog_Release(&logger->loggerArray[i]); + } + parcLogReporter_Release(&logger->reporter); +} + +static void +_destroyer(RtaLogger **loggerPtr) +{ + RtaLogger *logger = *loggerPtr; + _releaseLoggers(logger); + parcClock_Release(&(*loggerPtr)->clock); +} + +parcObject_ExtendPARCObject(RtaLogger, _destroyer, NULL, NULL, NULL, NULL, NULL, NULL); + +parcObject_ImplementAcquire(rtaLogger, RtaLogger); + +parcObject_ImplementRelease(rtaLogger, RtaLogger); + +RtaLogger * +rtaLogger_Create(PARCLogReporter *reporter, const PARCClock *clock) +{ + assertNotNull(reporter, "Parameter reporter must be non-null"); + assertNotNull(clock, "Parameter clock must be non-null"); + + RtaLogger *logger = parcObject_CreateAndClearInstance(RtaLogger); + if (logger) { + logger->clock = parcClock_Acquire(clock); + _allocateLoggers(logger, reporter); + } + + return logger; +} + +void +rtaLogger_SetReporter(RtaLogger *logger, PARCLogReporter *reporter) +{ + assertNotNull(logger, "Parameter logger must be non-null"); + + // save the log level state + PARCLogLevel savedLevels[RtaLoggerFacility_END]; + for (int i = 0; i < RtaLoggerFacility_END; i++) { + savedLevels[i] = parcLog_GetLevel(logger->loggerArray[i]); + } + + _releaseLoggers(logger); + + _allocateLoggers(logger, reporter); + + // restore log level state + for (int i = 0; i < RtaLoggerFacility_END; i++) { + parcLog_SetLevel(logger->loggerArray[i], savedLevels[i]); + } +} + +void +rtaLogger_SetClock(RtaLogger *logger, PARCClock *clock) +{ + assertNotNull(logger, "Parameter logger must be non-null"); + parcClock_Release(&logger->clock); + logger->clock = parcClock_Acquire(clock); +} + +static void +_assertInvariants(const RtaLogger *logger, RtaLoggerFacility facility) +{ + assertNotNull(logger, "Parameter logger must be non-null"); + trapOutOfBoundsIf(facility >= RtaLoggerFacility_END, "Invalid facility %d", facility); +} + +void +rtaLogger_SetLogLevel(RtaLogger *logger, RtaLoggerFacility facility, PARCLogLevel minimumLevel) +{ + _assertInvariants(logger, facility); + PARCLog *log = logger->loggerArray[facility]; + parcLog_SetLevel(log, minimumLevel); +} + +bool +rtaLogger_IsLoggable(const RtaLogger *logger, RtaLoggerFacility facility, PARCLogLevel level) +{ + _assertInvariants(logger, facility); + PARCLog *log = logger->loggerArray[facility]; + return parcLog_IsLoggable(log, level); +} + +void +rtaLogger_Log(RtaLogger *logger, RtaLoggerFacility facility, PARCLogLevel level, const char *module, const char *format, ...) +{ + if (rtaLogger_IsLoggable(logger, facility, level)) { + // this is logged as the messageid + uint64_t logtime = parcClock_GetTime(logger->clock); + + // rtaLogger_IsLoggable asserted invariants so we know facility is in bounds + PARCLog *log = logger->loggerArray[facility]; + + va_list va; + va_start(va, format); + + parcLog_MessageVaList(log, level, logtime, format, va); + + va_end(va); + } +} + diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Logger.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Logger.h new file mode 100644 index 00000000..067ad4f1 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Logger.h @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file rta_Logger.h + * @brief Logger for the Rta transport + * + * A facility based logger to allow selective logging from different parts of Rta + * + */ + +#ifndef Rta_rta_Logger_h +#define Rta_rta_Logger_h + +#include <sys/time.h> +#include <stdarg.h> +#include <parc/algol/parc_Buffer.h> +#include <parc/logging/parc_LogLevel.h> +#include <parc/logging/parc_LogReporter.h> +#include <parc/algol/parc_Clock.h> + +struct rta_logger; +typedef struct rta_logger RtaLogger; + +/** + * Framework - Overall framework + * ApiConnector - API Connector + * Flowcontrol - Flow controller + * Codec - Codec and verification/signing + * ForwarderConnector - Forwarder connector + */ +typedef enum { + RtaLoggerFacility_Framework, + RtaLoggerFacility_ApiConnector, + RtaLoggerFacility_Flowcontrol, + RtaLoggerFacility_Codec, + RtaLoggerFacility_ForwarderConnector, + RtaLoggerFacility_END // sentinel value +} RtaLoggerFacility; + +/** + * Returns a string representation of a facility + * + * Do not free the returned value. + * + * @param [in] facility The facility to change to a string + * + * @retval string A string representation of the facility + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char *rtaLogger_FacilityString(RtaLoggerFacility facility); + +/** + * Returns a string representation of a log level + * + * Do not free the returned value. + * + * @param [in] level The level to change to a string + * + * @retval string A string representation of the level + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char *rtaLogger_LevelString(PARCLogLevel level); + +/** + * Create a logger that uses a given writer and clock + * + * <#Paragraphs Of Explanation#> + * + * @param [in] writer The output writer + * @param [in] clock The clock to use for log messages + * + * @retval non-null An allocated logger + * @retval null An error + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaLogger *rtaLogger_Create(PARCLogReporter *reporter, const PARCClock *clock); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaLogger_Release(RtaLogger **loggerPtr); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +RtaLogger *rtaLogger_Acquire(const RtaLogger *logger); + +/** + * Sets the minimum log level for a facility + * + * The default log level is ERROR. For a message to be logged, it must be of equal + * or higher log level. + * + * @param [in] logger An allocated logger + * @param [in] facility The facility to set the log level for + * @param [in] The minimum level to log + * + * @retval <#value#> <#explanation#> + * + * Example: + * @code + * { + * PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); + * RtaLogger *logger = rtaLogger_Create(reporter, parcClock_Wallclock()); + * parcLogReporter_Release(&reporter); + * rtaLogger_SetLogLevel(logger, RtaLoggerFacility_IO, PARCLogLevel_Warning); + * } + * @endcode + */ +void rtaLogger_SetLogLevel(RtaLogger *logger, RtaLoggerFacility facility, PARCLogLevel minimumLevel); + +/** + * Tests if the log level would be logged + * + * If the facility would log the given level, returns true. May be used as a + * guard around expensive logging functions. + * + * @param [in] logger An allocated logger + * @param [in] facility The facility to test + * @param [in] The level to test + * + * @retval true The given facility would log the given level + * @retval false A message of the given level would not be logged + * + * Example: + * @code + * <#example#> + * @endcode + */ +bool rtaLogger_IsLoggable(const RtaLogger *logger, RtaLoggerFacility facility, PARCLogLevel level); + +/** + * Log a message + * + * The message will only be logged if it is loggable (rtaLogger_IsLoggable returns true). + * + * @param [in] logger An allocated RtaLogger + * @param [in] facility The facility to log under + * @param [in] level The log level of the message + * @param [in] module The specific module logging the message + * @param [in] format The message with varargs + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaLogger_Log(RtaLogger *logger, RtaLoggerFacility facility, PARCLogLevel level, const char *module, const char *format, ...); + +/** + * Switch the logger to a new reporter + * + * Will close the old reporter and re-setup the internal loggers to use the new reporter. + * All current log level settings are preserved. + * + * @param [in] logger An allocated RtaLogger + * @param [in] reporter An allocated PARCLogReporter + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaLogger_SetReporter(RtaLogger *logger, PARCLogReporter *reporter); + +/** + * Set a new clock to use with the logger + * + * The logger will start getting the time (logged as the messageid) from the specified clock + * + * @param [in] logger An allocated RtaLogger + * @param [in] clock An allocated PARCClock + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaLogger_SetClock(RtaLogger *logger, PARCClock *clock); +#endif // Rta_rta_Logger_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ProtocolStack.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ProtocolStack.c new file mode 100644 index 00000000..7bdafbf7 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ProtocolStack.c @@ -0,0 +1,786 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/queue.h> +#include <string.h> +#include <strings.h> +#include <sys/time.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/algol/parc_EventQueue.h> + +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_Services.h> + +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/core/rta_Connection.h> +#include <ccnx/transport/transport_rta/core/rta_Component.h> +#include <ccnx/transport/transport_rta/core/rta_ConnectionTable.h> +#include <ccnx/transport/transport_rta/core/rta_ComponentStats.h> +#include <ccnx/transport/common/transport_Message.h> +#include <ccnx/transport/common/transport_private.h> + +#include <ccnx/transport/transport_rta/connectors/connector_Api.h> +#include <ccnx/transport/transport_rta/connectors/connector_Forwarder.h> +#include <ccnx/transport/transport_rta/components/component_Codec.h> +#include <ccnx/transport/transport_rta/components/component_Flowcontrol.h> +#include <ccnx/transport/transport_rta/components/component_Testing.h> + +#include <ccnx/transport/transport_rta/config/config_ProtocolStack.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#define MAX_STACK_DEPTH 10 + +#ifndef DEBUG_OUTPUT +#define DEBUG_OUTPUT 0 +#endif + +const char *RtaComponentNames[LAST_COMPONENT] = +{ + "API", // 0 + "FC_NONE", + "FC_VEGAS", + "FC_PIPELINE", + "VERIFY_NONE", // 4 + "VERIFY_ENUMERATED", + "VERIFY_LOCATOR", + "CODEC_NONE", + NULL, // 8 + "CODEC_TLV", + "CODEC_CCNB", + "CODE_FLAN", + NULL, // 12 + "FWD_LOCAL", + "FWD_FLAN", + "FWD_CCND", // 15 + "TESTING_UPPER", + "TESTING_LOWER", // 17 + "CCND_REGISTRAR", + "FWD_METIS" +}; + +struct protocol_stack { + int stack_id; + + // used during configuration to indicate if configured + int config_codec; + + RtaFramework *framework; + + // They key value pairs passed to open. The api must + // keep this memory valid for as long as the connection is open + PARCJSON *params; + + // the inter-component queues + unsigned component_count; + PARCEventQueuePair *queue_pairs[MAX_STACK_DEPTH]; + RtaComponents components[MAX_STACK_DEPTH]; + + // queues assigned to components + struct component_queues { + PARCEventQueue *up; + PARCEventQueue *down; + } *component_queues[LAST_COMPONENT]; + RtaComponentOperations component_ops[LAST_COMPONENT]; + void *component_state[LAST_COMPONENT]; + + // stack-wide stats + RtaComponentStats *stack_stats[LAST_COMPONENT]; + + + // state change events are disabled during initial setup and teardown + bool stateChangeEventsEnabled; +}; + +static void set_queue_pairs(RtaProtocolStack *stack, RtaComponents comp_type); +static int configure_ApiConnector(RtaProtocolStack *stack, RtaComponents comp_type, RtaComponentOperations ops); +static int configure_Component(RtaProtocolStack *stack, RtaComponents comp_type, RtaComponentOperations ops); +static int configure_FwdConnector(RtaProtocolStack *stack, RtaComponents comp_type, RtaComponentOperations ops); + +// ======================================== + +RtaFramework * +rtaProtocolStack_GetFramework(RtaProtocolStack *stack) +{ + assertNotNull(stack, "called with null stack"); + return stack->framework; +} + +RtaProtocolStack * +rtaProtocolStack_Create(RtaFramework *framework, PARCJSON *params, int stack_id) +{ + RtaProtocolStack *stack = parcMemory_AllocateAndClear(sizeof(RtaProtocolStack)); + assertNotNull(stack, "%9" PRIu64 " parcMemory_AllocateAndClear returned NULL\n", + rtaFramework_GetTicks(stack->framework)); + + stack->stateChangeEventsEnabled = false; + + stack->params = parcJSON_Copy(params); + + assertNotNull(stack->params, "SYSTEM key is NULL in params"); + + assertNotNull(framework, "Parameter framework may not be null"); + + stack->framework = framework; + stack->stack_id = stack_id; + + // create all the buffer pairs + for (int i = 0; i < MAX_STACK_DEPTH; i++) { + stack->queue_pairs[i] = parcEventQueue_CreateConnectedPair(rtaFramework_GetEventScheduler(stack->framework)); + + assertNotNull(stack->queue_pairs[i], "parcEventQueue_CreateConnectedPair returned NULL index %d", i); + if (stack->queue_pairs[i] == NULL) { + for (int j = 0; j < i; j++) { + parcEventQueue_DestroyConnectedPair(&(stack->queue_pairs[j])); + } + + parcMemory_Deallocate((void **) &stack); + return NULL; + } + + // set them all to normal priority. The command port is high priority. External buffes are low priority. + parcEventQueue_SetPriority(parcEventQueue_GetConnectedUpQueue(stack->queue_pairs[i]), PARCEventPriority_Normal); + parcEventQueue_SetPriority(parcEventQueue_GetConnectedDownQueue(stack->queue_pairs[i]), PARCEventPriority_Normal); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s create buffer pair %p <-> %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(stack)), + __func__, + (void *) parcEventQueue_GetConnectedUpQueue(stack->queue_pairs[i]), + (void *) parcEventQueue_GetConnectedDownQueue(stack->queue_pairs[i])); + } + } + + for (int i = 0; i < LAST_COMPONENT; i++) { + stack->stack_stats[i] = rtaComponentStats_Create(NULL, i); + } + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s created stack %d at %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(stack)), + __func__, + stack_id, + (void *) stack); + } + + stack->stateChangeEventsEnabled = true; + + return stack; +} + +/** + * Opens a connection inside the protocol stack: it calls open() on each component. + * + * Returns 0 on success, -1 on error + * + * Example: + * @code + * <#example#> + * @endcode + */ +int +rtaProtocolStack_Open(RtaProtocolStack *stack, RtaConnection *connection) +{ + assertNotNull(stack, "called with null stack\n"); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s stack_id %d opening conn %p api_fd %d\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(stack)), + __func__, + stack->stack_id, + (void *) connection, rtaConnection_GetApiFd(connection)); + } + + // call all the opens, except the api + + // need to disable events during creation to avoid calling the event notifier + // of a component before the component sees the "open" call for this connection + stack->stateChangeEventsEnabled = false; + for (int i = 0; i < stack->component_count; i++) { + RtaComponents comp = stack->components[i]; + if (stack->component_ops[comp].open != NULL && + stack->component_ops[comp].open(connection) != 0) { + fprintf(stderr, "%s component %d failed open\n", __func__, i); + abort(); + return -1; + } + } + stack->stateChangeEventsEnabled = true; + + return 0; +} + +/* + * Closes a connection but does not touch stack->connection_head + */ +static int +internal_Stack_Close(RtaProtocolStack *stack, RtaConnection *conn) +{ + int i; + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s stack_id %d closing stack %p conn %p\n", + rtaFramework_GetTicks(rtaConnection_GetFramework(conn)), + __func__, + stack->stack_id, + (void *) stack, + (void *) conn); + } + + rtaConnection_SetState(conn, CONN_CLOSED); + + // call all the opens + for (i = 0; i < stack->component_count; i++) { + RtaComponents comp = stack->components[i]; + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s calling close for %s\n", + rtaFramework_GetTicks(rtaConnection_GetFramework(conn)), __func__, RtaComponentNames[comp]); + } + + if (stack->component_ops[comp].close != NULL && + stack->component_ops[comp].close(conn) != 0) { + fprintf(stderr, "%s component %d failed open\n", __func__, i); + abort(); + return -1; + } + } + + return 0; +} + +/** + * Calls the close() function of each component in the protocol stack. + * + * This is typically called from inside the API connector when it processes + * a CLOSE json message. + * Returns 0 success, -1 error. + * + * Example: + * @code + * <#example#> + * @endcode + */ +int +rtaProtocolStack_Close(RtaProtocolStack *stack, RtaConnection *conn) +{ + assertNotNull(stack, "called with null stack\n"); + assertNotNull(conn, "called with null connection\n"); + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s stack_id %d stack %p conn %p\n", + rtaFramework_GetTicks(rtaConnection_GetFramework(conn)), + __func__, + stack->stack_id, + (void *) stack, + (void *) conn); + } + + + internal_Stack_Close(stack, conn); + + return 0; +} + +/** + * Calls the release() function of all components. + * Drains all the component queues. + * + * This is called from rtaFramework_DestroyStack, who is responsible for closing + * all the connections in it before calling this. + * + * Example: + * @code + * <#example#> + * @endcode + */ +void +rtaProtocolStack_Destroy(RtaProtocolStack **stackPtr) +{ + RtaProtocolStack *stack; + + assertNotNull(stackPtr, "%s called with null pointer to stack\n", __func__); + + stack = *stackPtr; + assertNotNull(stack, "%s called with null stack dereference\n", __func__); + + if (DEBUG_OUTPUT) { + printf("%s stack_id %d destroying stack %p\n", + __func__, + stack->stack_id, + (void *) stack); + } + + stack->stateChangeEventsEnabled = false; + + // call all the release functions + for (int i = 0; i < stack->component_count; i++) { + RtaComponents comp = stack->components[i]; + if (stack->component_ops[comp].release != NULL && + stack->component_ops[comp].release(stack) != 0) { + fprintf(stderr, "%s component %d failed release\n", __func__, i); + abort(); + } + } + + for (int i = 0; i < MAX_STACK_DEPTH; i++) { + TransportMessage *tm; + while ((tm = rtaComponent_GetMessage(parcEventQueue_GetConnectedUpQueue(stack->queue_pairs[i]))) != NULL) { + assertFalse(1, "%s we should never execute the body, it should just drain\n", __func__); + } + + while ((tm = rtaComponent_GetMessage(parcEventQueue_GetConnectedDownQueue(stack->queue_pairs[i]))) != NULL) { + assertFalse(1, "%s we should never execute the body, it should just drain\n", __func__); + } + + if (DEBUG_OUTPUT) { + printf("%9" PRIu64 " %s destroy buffer pair %p <-> %p\n", + rtaFramework_GetTicks(rtaProtocolStack_GetFramework(stack)), + __func__, + (void *) parcEventQueue_GetConnectedUpQueue(stack->queue_pairs[i]), + (void *) parcEventQueue_GetConnectedDownQueue(stack->queue_pairs[i])); + } + + parcEventQueue_DestroyConnectedPair(&(stack->queue_pairs[i])); + } + + for (int i = 0; i < LAST_COMPONENT; i++) { + if (stack->component_queues[i]) { + parcMemory_Deallocate((void **) &(stack->component_queues[i])); + } + } + + for (int i = 0; i < LAST_COMPONENT; i++) { + rtaComponentStats_Destroy(&stack->stack_stats[i]); + } + + + parcJSON_Release(&stack->params); + memset(stack, 0, sizeof(RtaProtocolStack)); + + parcMemory_Deallocate((void **) &stack); + *stackPtr = NULL; +} + + +PARCEventQueue * +rtaProtocolStack_GetPutQueue(RtaProtocolStack *stack, RtaComponents component, RtaDirection direction) +{ + assertNotNull(stack, "%s called with null stack\n", __func__); + + if (direction == RTA_UP) { + return stack->component_queues[component]->up; + } else { + return stack->component_queues[component]->down; + } +} + + +/** + * Look up the symbolic name of the queue. Do not free the return. + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char * +rtaProtocolStack_GetQueueName(RtaProtocolStack *stack, PARCEventQueue *queue) +{ + int component; + for (component = 0; component <= LAST_COMPONENT; component++) { + if (stack->component_queues[component]) { + if (stack->component_queues[component]->up == queue) { + return RtaComponentNames[component]; + } + if (stack->component_queues[component]->down == queue) { + return RtaComponentNames[component]; + } + } + } + trapUnexpectedState("Could not find queue %p in stack %p", (void *) queue, (void *) stack); +} + +// ================================================= +// ================================================= + +static RtaComponents +getComponentTypeFromName(const char *name) +{ + int i; + + if (name == NULL) { + return UNKNOWN_COMPONENT; + } + + for (i = 0; i < LAST_COMPONENT; i++) { + if (RtaComponentNames[i] != NULL) { + if (strncasecmp(RtaComponentNames[i], name, 16) == 0) { + return (RtaComponents) i; + } + } + } + return UNKNOWN_COMPONENT; +} + + +/** + * Calls the confguration routine for each component in the stack + * + * Builds an array list of everything in the JSON configuration, then + * calls its configuation routine. + * + * The connecting event queues are disabled at this point. + * + * @param [in,out] stack The Protocol Stack to operate on + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +rtaProtocolStack_ConfigureComponents(RtaProtocolStack *stack) +{ + PARCArrayList *componentNameList; + componentNameList = protocolStack_GetComponentNameArray(stack->params); + assertTrue(parcArrayList_Size(componentNameList) < MAX_STACK_DEPTH, + "Too many components in a stack size %zu\n", + parcArrayList_Size(componentNameList)); + + + for (int i = 0; i < parcArrayList_Size(componentNameList); i++) { + // match it to a component type + const char *comp_name = parcArrayList_Get(componentNameList, i); + RtaComponents comp_type = getComponentTypeFromName(comp_name); + + // this could be sped up slightly by putting the ops structures + // in an array + switch (comp_type) { + case API_CONNECTOR: + configure_ApiConnector(stack, comp_type, api_ops); + break; + + case FC_NONE: + trapIllegalValue(comp_type, "Null flowcontroller no longer supported"); + break; + case FC_VEGAS: + configure_Component(stack, comp_type, flow_vegas_ops); + break; + case FC_PIPELINE: + abort(); + break; + + case CODEC_NONE: + trapIllegalValue(comp_type, "Null codec no longer supported"); + break; + case CODEC_TLV: + configure_Component(stack, comp_type, codec_tlv_ops); + break; + + case FWD_NONE: + abort(); + break; + case FWD_LOCAL: + configure_FwdConnector(stack, comp_type, fwd_local_ops); + break; + + case FWD_METIS: + configure_FwdConnector(stack, comp_type, fwd_metis_ops); + break; + + case TESTING_UPPER: + // fallthrough + case TESTING_LOWER: + configure_Component(stack, comp_type, testing_null_ops); + break; + + + default: + fprintf(stderr, "%s unsupported component type %s\n", __func__, comp_name); + abort(); + } + } + parcArrayList_Destroy(&componentNameList); +} + +static bool +rtaProtocolStack_InitializeComponents(RtaProtocolStack *stack) +{ + // Call all the inits + for (int i = 0; i < LAST_COMPONENT; i++) { + int res = 0; + if (stack->component_ops[i].init != NULL) { + res = stack->component_ops[i].init(stack); + } + + if (res != 0) { + fprintf(stderr, "%s opener for layer %d failed\n", __func__, i); + trapUnrecoverableState("Error Initializing the components") + return false; + } + } + return true; +} + +/** + * Enables events on all the queues between components + * + * Enables events on each queue. + * + * @param [in,out] stack The PRotocol stack + * + * Example: + * @code + * <#example#> + * @endcode + */ +static void +rtaProtocolStack_EnableComponentQueues(RtaProtocolStack *stack) +{ + // enable all the events on intermediate queues + for (int i = 0; i < stack->component_count; i++) { + RtaComponents component = stack->components[i]; + PARCEventQueue *upQueue = stack->component_queues[component]->up; + if (upQueue != NULL) { + parcEventQueue_Enable(upQueue, PARCEventType_Read); + } + + PARCEventQueue *downQueue = stack->component_queues[component]->down; + if (downQueue != NULL) { + parcEventQueue_Enable(downQueue, PARCEventType_Read); + } + } +} + +/* + * Called from transportRta_Open() + * + * Returns 0 for success, -1 on error (connection not made) + */ +int +rtaProtocolStack_Configure(RtaProtocolStack *stack) +{ + assertNotNull(stack, "%s called with null stack\n", __func__); + + rtaProtocolStack_ConfigureComponents(stack); + + bool initSuccess = rtaProtocolStack_InitializeComponents(stack); + if (!initSuccess) { + return -1; + } + + rtaProtocolStack_EnableComponentQueues(stack); + + return 0; +} + +/* + * Domain is the top-level key, e.g. SYSTEM or USER + */ +PARCJSON * +rtaProtocolStack_GetParam(RtaProtocolStack *stack, const char *domain, const char *key) +{ + assertNotNull(stack, "%s called with null stack\n", __func__); + assertNotNull(domain, "%s called with null domain\n", __func__); + assertNotNull(key, "%s called with null key\n", __func__); + + PARCJSONValue *value = parcJSON_GetValueByName(stack->params, domain); + assertNotNull(value, "Did not find domain %s in protocol stack parameters", domain); + if (value == NULL) { + return NULL; + } + PARCJSON *domainJson = parcJSONValue_GetJSON(value); + + value = parcJSON_GetValueByName(domainJson, key); + assertNotNull(value, "Did not find key %s in protocol stack parameters", key); + return parcJSONValue_GetJSON(value); +} + +unsigned +rtaProtocolStack_GetNextConnectionId(RtaProtocolStack *stack) +{ + assertNotNull(stack, "Parameter stack must be a non-null RtaProtocolStack pointer."); + return rtaFramework_GetNextConnectionId(stack->framework); +} + +RtaComponentStats * +rtaProtocolStack_GetStats(const RtaProtocolStack *stack, RtaComponents type) +{ + assertTrue(type < LAST_COMPONENT, "invalid type %d\n", type); + return stack->stack_stats[type]; +} + +static void +printSingleTuple(FILE *file, const struct timeval *timeval, const RtaProtocolStack *stack, RtaComponents componentType, RtaComponentStatType stat) +{ + RtaComponentStats *stats = rtaProtocolStack_GetStats(stack, componentType); + + fprintf(file, "{ \"stackId\" : %d, \"component\" : \"%s\", \"name\" : \"%s\", \"value\" : %" PRIu64 ", \"timeval\" : %ld.%06u }\n", + stack->stack_id, + RtaComponentNames[componentType], + rtaComponentStatType_ToString(stat), + rtaComponentStats_Get(stats, stat), + timeval->tv_sec, + (unsigned) timeval->tv_usec + ); +} + +PARCArrayList * +rtaProtocolStack_GetStatistics(const RtaProtocolStack *stack, FILE *file) +{ + PARCArrayList *list = parcArrayList_Create(NULL); + + struct timeval timeval; + gettimeofday(&timeval, NULL); + + // This does not fill in the array list + for (int componentIndex = 0; componentIndex < stack->component_count; componentIndex++) { + RtaComponents componentType = stack->components[componentIndex]; + printSingleTuple(file, &timeval, stack, componentType, STATS_OPENS); + printSingleTuple(file, &timeval, stack, componentType, STATS_CLOSES); + printSingleTuple(file, &timeval, stack, componentType, STATS_UPCALL_IN); + printSingleTuple(file, &timeval, stack, componentType, STATS_UPCALL_OUT); + printSingleTuple(file, &timeval, stack, componentType, STATS_DOWNCALL_IN); + printSingleTuple(file, &timeval, stack, componentType, STATS_DOWNCALL_OUT); + } + + return list; +} + + +// ============================================= + +static void +set_queue_pairs(RtaProtocolStack *stack, RtaComponents comp_type) +{ + //PARCEventQueuePair *component_queues[LAST_COMPONENT]; + // Save references to the OUTPUT queues used by a specific component. + if (stack->component_queues[comp_type] == NULL) { + stack->component_queues[comp_type] = parcMemory_AllocateAndClear(sizeof(struct component_queues)); + } + + stack->component_queues[comp_type]->up = + parcEventQueue_GetConnectedUpQueue(stack->queue_pairs[stack->component_count - 1]); + + stack->component_queues[comp_type]->down = + parcEventQueue_GetConnectedDownQueue(stack->queue_pairs[stack->component_count]); + + // Set callbacks on the INPUT queues read by a specific component + parcEventQueue_SetCallbacks(stack->component_queues[comp_type]->up, + stack->component_ops[comp_type].downcallRead, + NULL, + stack->component_ops[comp_type].downcallEvent, + (void *) stack); + + parcEventQueue_SetCallbacks(stack->component_queues[comp_type]->down, + stack->component_ops[comp_type].upcallRead, + NULL, + stack->component_ops[comp_type].upcallEvent, + (void *) stack); +} + + +static int +configure_ApiConnector(RtaProtocolStack *stack, RtaComponents comp_type, RtaComponentOperations ops) +{ + if (stack->component_queues[comp_type] == NULL) { + stack->component_queues[comp_type] = parcMemory_AllocateAndClear(sizeof(struct component_queues)); + } + + assertNotNull(stack->component_queues[comp_type], "called with null component_queue"); + assertNotNull(stack->queue_pairs[stack->component_count], "called with null queue_pair"); + + // This wires the bottom half of the API Connector to the streams. + // It does not do the top half, which is in the connector's INIT + + stack->components[stack->component_count] = comp_type; + stack->component_ops[comp_type] = ops; + + stack->component_queues[comp_type]->down = + parcEventQueue_GetConnectedDownQueue(stack->queue_pairs[stack->component_count]); + + parcEventQueue_SetCallbacks(stack->component_queues[comp_type]->down, + stack->component_ops[comp_type].upcallRead, + NULL, + stack->component_ops[comp_type].upcallEvent, + (void *) stack); + + stack->component_count++; + return 0; +} + +static int +configure_Component(RtaProtocolStack *stack, RtaComponents comp_type, RtaComponentOperations ops) +{ + stack->component_ops[comp_type] = ops; + stack->components[stack->component_count] = comp_type; + set_queue_pairs(stack, comp_type); + stack->component_count++; + return 0; +} + + +static int +configure_FwdConnector(RtaProtocolStack *stack, RtaComponents comp_type, RtaComponentOperations ops) +{ + stack->component_ops[comp_type] = ops; + stack->components[stack->component_count] = comp_type; + + // We only set the upcall buffers. The down buffers + // are controlled by the forwarder connector + if (stack->component_queues[comp_type] == NULL) { + stack->component_queues[comp_type] = parcMemory_AllocateAndClear(sizeof(struct component_queues)); + } + + stack->component_queues[comp_type]->up = + parcEventQueue_GetConnectedUpQueue(stack->queue_pairs[stack->component_count - 1]); + + parcEventQueue_SetCallbacks(stack->component_queues[comp_type]->up, + stack->component_ops[comp_type].downcallRead, + NULL, + stack->component_ops[comp_type].downcallEvent, + (void *) stack); + + stack->component_count++; + return 0; +} + +int +rtaProtocolStack_GetStackId(RtaProtocolStack *stack) +{ + return stack->stack_id; +} + +void +rtaProtocolStack_ConnectionStateChange(RtaProtocolStack *stack, void *connection) +{ + if (stack->stateChangeEventsEnabled) { + for (int componentIndex = 0; componentIndex < stack->component_count; componentIndex++) { + RtaComponents componentType = stack->components[componentIndex]; + if (stack->component_ops[componentType].stateChange != NULL) { + stack->component_ops[componentType].stateChange(connection); + } + } + } +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ProtocolStack.h b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ProtocolStack.h new file mode 100644 index 00000000..df09c23f --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ProtocolStack.h @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** @file rta_ProtocolStack.h + * @brief A set of connectors and components + * + * In a Ready To Assemble transport, individual pieces are called connectors + * and components. A connector attaches to the API library at the top and + * to the forwarder at the bottom. In between the connectors are components. + * + * One set of connectors and components is called a protocol stack. + * + * A ProtocolStack defines a set of Components linked by bidirectional + * queues. A ProtocolStack is defined by the KeyValue set passed to + * the Transport. The hash of the KeyValue set selects the protocol stack. + * If the Transport sees a new hash, it creates a new protocol stack + * via ProtocolStack_Create(). + * + * Each API connection calls _Open, which will return a new RtaConnection + * pointer. The Transport gives the API an "api_fd", which the Transport + * translates to the RtaConnection. + * + * A protocol stack is implemented as a set of queue pairs between components. + * There is a fixed sized array called queue_pairs[MAX_STACK_DEPTH]. The + * queue_pairs[i].pair[RTA_DOWN] end attaches to the upper component. RTA_DOWN + * indicates the direction of travel for a write. queue_pairs[i].pair[RTA_UP] + * attaches to the lower component. + * + * A component only knows its identity (see components.h). For example, the + * TLV codec is called CODEC_TLV, and that is the only identity it know. It does + * not know the identity of the pieces above or below it. + * + * Therefore, when a component calls protocolStack_GetPutQ(stack, CODEC_TLV, RTA_DOWN), + * it is asking for the queue to write to in the DOWN direction. This means that + * we should keep an index by the component name, not by the queue_pairs[] array. + * Thus, we keep a component_queues[] array that is indexed by the component name. + * + * Let's say our stack is API_CONNECTOR, FC_NULL, VERIFY_NULL, CODEC_TLV, FWD_LOCAL. + * The picture is like this: + * + * @code + * | + * * <- api_connector managed queue + * API_CONNECTOR + * * <- queue_pair[0].pair[DOWN] <- component_queue[API_CONNECTOR].pair[DOWN] + * | + * * <- queue_pair[0].pair[UP] <- component_queue[FC_NULL].pair[UP] + * FC_NULL + * * <- queue_pair[1].pair[DOWN] <- component_queue[FC_NULL].pair[DOWN] + * | + * * <- queue_pair[1].pair[UP] <- component_queue[VERIFY_NULL].pair[UP] + * VERIFY_NULL + * * <- queue_pair[2].pair[DOWN] <- component_queue[VERIFY_NULL].pair[DOWN] + * | + * * <- queue_pair[2].pair[UP] <- component_queue[CODEC_TLV].pair[UP] + * CODEC_TLV + * * <- queue_pair[3].pair[DOWN] <- component_queue[CODEC_TLV].pair[DOWN] + * | + * * <- queue_pair[3].pair[UP] <- component_queue[FWD_LOCAL].pair[UP] + * FWD_LOCAL + * * <- fwd_local managed connection + * | + * @endcode + * + * Each component also has a pair of callbacks, one for reading messages flowing down + * the stack and one for reading messages flowing up the stack. These are called + * "downcall_read" for reading messages flowing down and "upcall_read" for messages + * flowing up. + * + * Recall that the direction attributes UP and DOWN in the queues are in terms + * of WRITES, therefore the directions are opposite for reads. A component's + * downcall_read will read from component_queue[X].pair[UP]. + * + * Example: + * @code + * <#example#> + * @endcode + */ +#ifndef Libccnx_rta_ProtocolStack_h +#define Libccnx_rta_ProtocolStack_h + +#include <parc/algol/parc_ArrayList.h> + +#include <parc/algol/parc_EventQueue.h> + +#include <ccnx/transport/transport_rta/core/rta_ComponentStats.h> +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/components.h> +#include <ccnx/transport/transport_rta/core/rta_ComponentQueue.h> +#include <ccnx/transport/transport_rta/commands/rta_Command.h> + +struct rta_connection; +struct component_queue; + +struct protocol_stack; +typedef struct protocol_stack RtaProtocolStack; + +/** + * Used to assign unique connection id to sockets. This is just + * for internal tracking, its not a descriptor. + * + * <#Paragraphs Of Explanation#> + * + * @param [in] stack <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +unsigned rtaProtocolStack_GetNextConnectionId(RtaProtocolStack *stack); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] framework <#description#> + * @param [in] params <#description#> + * @param [in] stack_id <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +RtaProtocolStack *rtaProtocolStack_Create(RtaFramework *framework, PARCJSON *params, int stack_id); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] stack <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +int rtaProtocolStack_Configure(RtaProtocolStack *stack); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] stack <#description#> + * @param [in] component <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void *rtaProtocolStack_GetPrivateData(RtaProtocolStack *stack, RtaComponents component); +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] stack <#description#> + * @param [in] component <#description#> + * @param [in] private <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void rtaProtocolStack_SetPrivateData(RtaProtocolStack *stack, RtaComponents component, void *private); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [in] stack <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +RtaFramework *rtaProtocolStack_GetFramework(RtaProtocolStack *stack); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +int rtaProtocolStack_GetStackId(RtaProtocolStack *stack); + +/** + * Opens a connection inside the protocol stack: it calls open() on each component. + * + * Returns 0 on success, -1 on error + * + * Example: + * @code + * <#example#> + * @endcode + */ +int rtaProtocolStack_Open(RtaProtocolStack *, struct rta_connection *connection); + +/** + * + * 0 success, -1 error + * + * Example: + * @code + * <#example#> + * @endcode + */ +int rtaProtocolStack_Close(RtaProtocolStack *, struct rta_connection *conn); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +void rtaProtocolStack_Destroy(RtaProtocolStack **stack); + +/** + * Return the queue used for output for a component in a given direction + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +PARCEventQueue *rtaProtocolStack_GetPutQueue(RtaProtocolStack *stack, + RtaComponents component, + RtaDirection direction); + +/** + * <#One Line Description#> + * + * Domain is the top-level key, e.g. SYSTEM or USER + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +PARCJSON *rtaProtocolStack_GetParam(RtaProtocolStack *stack, const char *domain, const char *key); + +/** + * <#One Line Description#> + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + * + * @see <#references#> + */ +RtaComponentStats *rtaProtocolStack_GetStats(const RtaProtocolStack *stack, RtaComponents type); + +/** + * <#OneLineDescription#> + * + * <#Discussion#> + * + * @param stack + * @param file + * @return <#return#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +PARCArrayList *rtaProtocolStack_GetStatistics(const RtaProtocolStack *stack, FILE *file); + +/** + * Look up the symbolic name of the queue. Do not free the return. + * + * Example: + * @code + * <#example#> + * @endcode + */ +const char *rtaProtocolStack_GetQueueName(RtaProtocolStack *stack, PARCEventQueue *queue); + +/** + * A state event occured on the given connection, let all the components know. + * + * A state changed occured (UP, DOWN, PAUSE, or flow control), notify all the components + * + * @param [in] connection The RtaConnection. + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +void rtaProtocolStack_ConnectionStateChange(RtaProtocolStack *stack, void *connection); +#endif diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/.gitignore b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/.gitignore new file mode 100644 index 00000000..8763938d --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/.gitignore @@ -0,0 +1,10 @@ +test_rta_Component +test_rta_Connection +test_rta_Framework_NonThreaded +test_rta_Framework_Services +test_rta_Framework_Threaded +test_rta_ProtocolStack +test_rta_Logger +test_rta_Stats +output.txt +core diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/CMakeLists.txt b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/CMakeLists.txt new file mode 100644 index 00000000..b57c2afd --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/CMakeLists.txt @@ -0,0 +1,23 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_rta_ConnectionTable + test_rta_Framework + test_rta_Framework_Commands + test_rta_Component + test_rta_Connection + test_rta_Framework_NonThreaded + test_rta_Framework_Services + test_rta_Framework_Threaded + test_rta_Logger + test_rta_ProtocolStack + test_rta_ComponentStats +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() + diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Component.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Component.c new file mode 100644 index 00000000..a1de01bf --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Component.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Create a non-threaded framework to test comopnent functions. + * + */ +#define DEBUG_OUTPUT 1 +#include "../rta_Component.c" + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/config/config_All.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_Commands.c> +#include <ccnx/transport/test_tools/traffic_tools.h> + +#include <sys/socket.h> +#include <errno.h> + +#define PAIR_OTHER 0 +#define PAIR_TRANSPORT 1 + +typedef struct test_data { + PARCRingBuffer1x1 *commandRingBuffer; + PARCNotifier *commandNotifier; + + int api_fds[2]; + RtaFramework *framework; + RtaProtocolStack *stack; + RtaConnection *connection; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + int error = socketpair(AF_UNIX, SOCK_STREAM, 0, data->api_fds); + assertFalse(error, "Error creating socket pair: (%d) %s", errno, strerror(errno)); + + data->commandRingBuffer = parcRingBuffer1x1_Create(128, NULL); + data->commandNotifier = parcNotifier_Create(); + data->framework = rtaFramework_Create(data->commandRingBuffer, data->commandNotifier); + + assertNotNull(data->framework, "rtaFramework_Create returned null"); + + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + apiConnector_ProtocolStackConfig(stackConfig); + testingLower_ProtocolStackConfig(stackConfig); + protocolStack_ComponentsConfigArgs(stackConfig, apiConnector_GetName(), testingLower_GetName(), NULL); + + rtaFramework_NonThreadedStepCount(data->framework, 10); + + int stackId = 1; + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stackId, stackConfig); + _rtaFramework_ExecuteCreateStack(data->framework, createStack); + rtaCommandCreateProtocolStack_Release(&createStack); + + rtaFramework_NonThreadedStepCount(data->framework, 10); + data->stack = (rtaFramework_GetProtocolStackByStackId(data->framework, stackId))->stack; + + CCNxConnectionConfig *connConfig = ccnxConnectionConfig_Create(); + apiConnector_ConnectionConfig(connConfig); + + tlvCodec_ConnectionConfig(connConfig); + + testingLower_ConnectionConfig(connConfig); + + RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(stackId, + data->api_fds[PAIR_OTHER], + data->api_fds[PAIR_TRANSPORT], + ccnxConnectionConfig_GetJson(connConfig)); + + rtaFramework_NonThreadedStepCount(data->framework, 10); + _rtaFramework_ExecuteOpenConnection(data->framework, openConnection); + rtaCommandOpenConnection_Release(&openConnection); + + rtaFramework_NonThreadedStepCount(data->framework, 10); + data->connection = rtaConnectionTable_GetByApiFd(data->framework->connectionTable, data->api_fds[PAIR_OTHER]); + + ccnxConnectionConfig_Destroy(&connConfig); + ccnxStackConfig_Release(&stackConfig); + + return data; +} + +static void +_commonTeardown(TestData *data) +{ + rtaFramework_Teardown(data->framework); + + parcRingBuffer1x1_Release(&data->commandRingBuffer); + parcNotifier_Release(&data->commandNotifier); + rtaFramework_Destroy(&data->framework); + + close(data->api_fds[0]); + close(data->api_fds[1]); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(rta_Component) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Component) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Component) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaComponent_GetOutputQueue); + + LONGBOW_RUN_TEST_CASE(Global, rtaComponent_PutMessage_ClosedConnection); + LONGBOW_RUN_TEST_CASE(Global, rtaComponent_PutMessage_OpenConnection); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaComponent_GetOutputQueue) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + PARCEventQueue *queue = rtaComponent_GetOutputQueue(data->connection, API_CONNECTOR, RTA_DOWN); + assertNotNull(queue, "Got null queue for API_CONNECTOR DOWN queue"); +} + +LONGBOW_TEST_CASE(Global, rtaComponent_PutMessage_ClosedConnection) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + rtaConnection_SetState(data->connection, CONN_CLOSED); + + // Create the TransportMessage to put on the queue + TransportMessage *tm = trafficTools_CreateTransportMessageWithDictionaryControl(data->connection, CCNxTlvDictionary_SchemaVersion_V1); + + // Send it down from the API connector to the Testing Lower component + PARCEventQueue *outputQueue = rtaComponent_GetOutputQueue(data->connection, API_CONNECTOR, RTA_DOWN); + + int success = rtaComponent_PutMessage(outputQueue, tm); + assertFalse(success, "Error putting message on API Connector's down queue"); + + // check that we got it + PARCEventQueue *inputQueue = rtaComponent_GetOutputQueue(data->connection, TESTING_LOWER, RTA_UP); + + TransportMessage *test_tm = rtaComponent_GetMessage(inputQueue); + assertNull(test_tm, "Should have returned NULL on a closed connection"); + + // The transport message was destroyed by PutMessage because the connection + // was closed. Don't need to destroy the transport message. + + // set state back to OPEN so the connection is properly disposed of + rtaConnection_SetState(data->connection, CONN_OPEN); +} + +LONGBOW_TEST_CASE(Global, rtaComponent_PutMessage_OpenConnection) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + // Create the TransportMessage to put on the queue + TransportMessage *tm = trafficTools_CreateTransportMessageWithDictionaryControl(data->connection, CCNxTlvDictionary_SchemaVersion_V1); + + // Send it down from the API connector to the Testing Lower component + PARCEventQueue *outputQueue = rtaComponent_GetOutputQueue(data->connection, API_CONNECTOR, RTA_DOWN); + + int success = rtaComponent_PutMessage(outputQueue, tm); + assertTrue(success, "Error putting message on API Connector's down queue"); + + // check that we got it + PARCEventQueue *inputQueue = rtaComponent_GetOutputQueue(data->connection, TESTING_LOWER, RTA_UP); + + TransportMessage *test_tm = rtaComponent_GetMessage(inputQueue); + assertTrue(test_tm == tm, "Got wrong message, got %p expected %p", (void *) test_tm, (void *) tm); + + transportMessage_Destroy(&tm); +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Component); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ComponentStats.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ComponentStats.c new file mode 100644 index 00000000..1da0b4fc --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ComponentStats.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "../rta_ComponentStats.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/config/config_All.h> + +#include <inttypes.h> +#include <sys/socket.h> +#include <errno.h> + +#define PAIR_OTHER 0 +#define PAIR_TRANSPORT 1 + +#include <LongBow/unit-test.h> + +typedef struct test_data { + PARCRingBuffer1x1 *commandRingBuffer; + PARCNotifier *commandNotifier; + + int api_fds[2]; + RtaFramework *framework; + RtaProtocolStack *stack; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + int error = socketpair(AF_UNIX, SOCK_STREAM, 0, data->api_fds); + assertTrue(error == 0, "Error creating socket pair: (%d) %s", errno, strerror(errno)); + + data->commandRingBuffer = parcRingBuffer1x1_Create(128, NULL); + data->commandNotifier = parcNotifier_Create(); + data->framework = rtaFramework_Create(data->commandRingBuffer, data->commandNotifier); + assertNotNull(data->framework, "rtaFramework_Create returned null"); + + rtaFramework_Start(data->framework); + + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + apiConnector_ProtocolStackConfig(stackConfig); + testingLower_ProtocolStackConfig(stackConfig); + protocolStack_ComponentsConfigArgs(stackConfig, apiConnector_GetName(), testingLower_GetName(), NULL); + data->stack = rtaProtocolStack_Create(data->framework, ccnxStackConfig_GetJson(stackConfig), 1); + + ccnxStackConfig_Release(&stackConfig); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + rtaProtocolStack_Destroy(&data->stack); + + // blocks until done + rtaFramework_Shutdown(data->framework); + + parcRingBuffer1x1_Release(&data->commandRingBuffer); + parcNotifier_Release(&data->commandNotifier); + rtaFramework_Destroy(&data->framework); + + close(data->api_fds[0]); + close(data->api_fds[1]); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(rta_Stats) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Stats) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Stats) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, stats_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, stats_Dump); + LONGBOW_RUN_TEST_CASE(Global, stats_Get); + LONGBOW_RUN_TEST_CASE(Global, stats_Increment); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, stats_Create_Destroy) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaComponentStats *stats = rtaComponentStats_Create(data->stack, API_CONNECTOR); + + assertNotNull(stats, "Got null stats from rtaComponentStats_Create"); + assertTrue(stats->stack == data->stack, + "Bad stack pointer, got %p expected %p", + (void *) stats->stack, (void *) data->stack); + + rtaComponentStats_Destroy(&stats); +} + +LONGBOW_TEST_CASE(Global, stats_Dump) +{ + for (int i = 0; i < STATS_LAST; i++) { + char *test = rtaComponentStatType_ToString(i); + assertNotNull(test, "Got null string for stat type %d", i); + } +} + +LONGBOW_TEST_CASE(Global, stats_Get) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaComponentStats *stats = rtaComponentStats_Create(data->stack, API_CONNECTOR); + + for (int i = 0; i < STATS_LAST; i++) { + // set each stat to a value + uint64_t value = i + 5; + stats->stats[i] = value; + + uint64_t counter = stats->stats[i]; + assertTrue(counter == value, "Counter %d wrong value, got %" PRIu64 " expected %" PRIu64, i, counter, value); + } + + rtaComponentStats_Destroy(&stats); +} + +LONGBOW_TEST_CASE(Global, stats_Increment) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaComponentStats *stats = rtaComponentStats_Create(data->stack, API_CONNECTOR); + + for (int i = 0; i < STATS_LAST; i++) { + rtaComponentStats_Increment(stats, (RtaComponentStatType) i); + } + + // now make sure they are all "1" + for (int i = 0; i < STATS_LAST; i++) { + uint64_t counter = stats->stats[i]; + assertTrue(counter == 1, "Counter %d wrong value, got %" PRIu64 "expected 1", i, counter); + } + + rtaComponentStats_Destroy(&stats); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Stats); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Connection.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Connection.c new file mode 100644 index 00000000..4b80d7e1 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Connection.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "../rta_Connection.c" +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(rta_Connection) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Connection) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Connection) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Connection); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ConnectionTable.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ConnectionTable.c new file mode 100644 index 00000000..d77f47c0 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ConnectionTable.c @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_ConnectionTable.c" +#include "../rta_ProtocolStack.c" +#include <LongBow/unit-test.h> + +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(rta_ConnectionTable) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_ConnectionTable) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_ConnectionTable) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaConnectionTable_AddConnection); + LONGBOW_RUN_TEST_CASE(Global, rtaConnectionTable_AddConnection_TooMany); + LONGBOW_RUN_TEST_CASE(Global, rtaConnectionTable_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, rtaConnectionTable_GetByApiFd); + LONGBOW_RUN_TEST_CASE(Global, rtaConnectionTable_GetByTransportFd); + LONGBOW_RUN_TEST_CASE(Global, rtaConnectionTable_Remove); + LONGBOW_RUN_TEST_CASE(Global, rtaConnectionTable_RemoveByStack); +} + +typedef struct test_data { + PARCRingBuffer1x1 *commandRingBuffer; + PARCNotifier *commandNotifier; + + RtaFramework *framework; + + // in some tests we use two protocol stacks + RtaProtocolStack *stack_a; + RtaProtocolStack *stack_b; +} TestData; + +static RtaConnection * +createConnection(RtaProtocolStack *stack, int api_fd, int transport_fd) +{ + // ------- + // Create a connection to use in the table + PARCJSON *params = parcJSON_ParseString("{}"); + RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(stack->stack_id, api_fd, transport_fd, params); + + // Create a connection that goes in the connection table + RtaConnection *conn = rtaConnection_Create(stack, openConnection); + assertNotNull(conn, "Got null connection from rtaConnection_Create"); + + rtaCommandOpenConnection_Release(&openConnection); + parcJSON_Release(¶ms); + return conn; +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + // --------------------------- + // To test a connection table, we need to create a Framework and a Protocol stack + + data->commandRingBuffer = parcRingBuffer1x1_Create(128, NULL); + data->commandNotifier = parcNotifier_Create(); + + data->framework = rtaFramework_Create(data->commandRingBuffer, data->commandNotifier); + + // fake out a protocol stack + data->stack_a = parcMemory_AllocateAndClear(sizeof(RtaProtocolStack)); + assertNotNull(data->stack_a, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(RtaProtocolStack)); + data->stack_a->stack_id = 1; + data->stack_a->framework = data->framework; + + // fake out a protocol stack + data->stack_b = parcMemory_AllocateAndClear(sizeof(RtaProtocolStack)); + assertNotNull(data->stack_b, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(RtaProtocolStack)); + data->stack_b->stack_id = 2; + data->stack_b->framework = data->framework; + + longBowTestCase_SetClipBoardData(testCase, data); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + // now cleanup everything + rtaFramework_Destroy(&data->framework); + parcNotifier_Release(&data->commandNotifier); + parcRingBuffer1x1_Release(&data->commandRingBuffer); + + parcMemory_Deallocate((void **) &(data->stack_a)); + parcMemory_Deallocate((void **) &(data->stack_b)); + parcMemory_Deallocate((void **) &data); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +/** + * Destroy the table before destroying the connection + */ +LONGBOW_TEST_CASE(Global, rtaConnectionTable_AddConnection) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaConnection *conn = createConnection(data->stack_a, 2, 3); + + // This is the part we want to test. + RtaConnectionTable *table = rtaConnectionTable_Create(1000, rtaConnection_Destroy); + rtaConnectionTable_AddConnection(table, conn); + + assertTrue(table->count_elements == 1, "Incorrect table size, expected %d got %zu", 1, table->count_elements); + rtaConnectionTable_Destroy(&table); +} + + +/** + * Create a connection table with just 1 connection and make sure table + * does the right thing on overflow + */ +LONGBOW_TEST_CASE(Global, rtaConnectionTable_AddConnection_TooMany) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaConnection *conn = createConnection(data->stack_a, 2, 3); + + int res; + + // create the table with size 1 + RtaConnectionTable *table = rtaConnectionTable_Create(1, rtaConnection_Destroy); + res = rtaConnectionTable_AddConnection(table, conn); + assertTrue(res == 0, "Got non-zero return %d", res); + assertTrue(table->count_elements == 1, "Incorrect table size, expected %d got %zu", 1, table->count_elements); + + // add the second connection, should return failure + res = rtaConnectionTable_AddConnection(table, conn); + assertTrue(res == -1, "Should have failed, expecting -1, got %d", res); + + rtaConnectionTable_Destroy(&table); +} + + +LONGBOW_TEST_CASE(Global, rtaConnectionTable_Create_Destroy) +{ + size_t beforeBalance = parcMemory_Outstanding(); + RtaConnectionTable *table = rtaConnectionTable_Create(1000, rtaConnection_Destroy); + assertTrue(table->max_elements == 1000, "Initialized with wrong number of elements"); + rtaConnectionTable_Destroy(&table); + size_t afterBalance = parcMemory_Outstanding(); + assertTrue(beforeBalance == afterBalance, "Memory imbalance after create/destroy"); +} + +LONGBOW_TEST_CASE(Global, rtaConnectionTable_GetByApiFd) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaConnection *conn = createConnection(data->stack_a, 2, 3); + + RtaConnectionTable *table = rtaConnectionTable_Create(1000, rtaConnection_Destroy); + rtaConnectionTable_AddConnection(table, conn); + + RtaConnection *test; + test = rtaConnectionTable_GetByApiFd(table, 2); + assertTrue(test == conn, "Got wrong connection, expecting %p got %p", (void *) conn, (void *) test); + + test = rtaConnectionTable_GetByApiFd(table, 3); + assertTrue(test == NULL, "Got wrong connection, expecting %p got %p", NULL, (void *) test); + + test = rtaConnectionTable_GetByApiFd(table, 4); + assertTrue(test == NULL, "Got wrong connection, expecting %p got %p", NULL, (void *) test); + + rtaConnectionTable_Destroy(&table); +} + +LONGBOW_TEST_CASE(Global, rtaConnectionTable_GetByTransportFd) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + RtaConnection *conn = createConnection(data->stack_a, 2, 3); + + RtaConnectionTable *table = rtaConnectionTable_Create(1000, rtaConnection_Destroy); + rtaConnectionTable_AddConnection(table, conn); + + RtaConnection *test; + test = rtaConnectionTable_GetByTransportFd(table, 2); + assertTrue(test == NULL, "Got wrong connection, expecting %p got %p", NULL, (void *) test); + + test = rtaConnectionTable_GetByTransportFd(table, 3); + assertTrue(test == conn, "Got wrong connection, expecting %p got %p", (void *) conn, (void *) test); + + test = rtaConnectionTable_GetByTransportFd(table, 4); + assertTrue(test == NULL, "Got wrong connection, expecting %p got %p", NULL, (void *) test); + + + rtaConnectionTable_Destroy(&table); +} + +/** + * We create two connections and make sure that when we remove one the other + * is still in the table + */ +LONGBOW_TEST_CASE(Global, rtaConnectionTable_Remove) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + int res; + int a_pair[2]; + int b_pair[2]; + + // we have to use actual socket pairs in this test because Remove will destroy + // the last copy of the connection and call close() on the sockets. + socketpair(PF_LOCAL, SOCK_STREAM, 0, a_pair); + socketpair(PF_LOCAL, SOCK_STREAM, 0, b_pair); + + RtaConnectionTable *table = rtaConnectionTable_Create(1000, rtaConnection_Destroy); + + RtaConnection *conn_a = createConnection(data->stack_a, a_pair[0], a_pair[1]); + rtaConnectionTable_AddConnection(table, conn_a); + + RtaConnection *conn_b = createConnection(data->stack_b, b_pair[0], b_pair[1]); + rtaConnectionTable_AddConnection(table, conn_b); + + assertTrue(table->count_elements == 2, "Wrong element count"); + + res = rtaConnectionTable_Remove(table, conn_b); + assertTrue(res == 0, "Got error from rtaConnectionTable_Remove: %d", res); + assertTrue(table->count_elements == 1, "Wrong element count"); + + RtaConnection *test = rtaConnectionTable_GetByApiFd(table, a_pair[0]); + assertNotNull(test, "Could not retrieve connection that was supposed to still be there"); + + rtaConnectionTable_Destroy(&table); +} + +/** + * Create two connections, they are in different protocol stacks. Remove one by + * stack id and make sure the other is still in the table + */ +LONGBOW_TEST_CASE(Global, rtaConnectionTable_RemoveByStack) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + int res; + int a_pair[2]; + int b_pair[2]; + + // we have to use actual socket pairs in this test because Remove will destroy + // the last copy of the connection and call close() on the sockets. + socketpair(PF_LOCAL, SOCK_STREAM, 0, a_pair); + socketpair(PF_LOCAL, SOCK_STREAM, 0, b_pair); + + RtaConnectionTable *table = rtaConnectionTable_Create(1000, rtaConnection_Destroy); + + RtaConnection *conn_a = createConnection(data->stack_a, a_pair[0], a_pair[1]); + rtaConnectionTable_AddConnection(table, conn_a); + + RtaConnection *conn_b = createConnection(data->stack_b, b_pair[0], b_pair[1]); + rtaConnectionTable_AddConnection(table, conn_b); + + // now remove a connection by stack id + + res = rtaConnectionTable_RemoveByStack(table, data->stack_a->stack_id); + assertTrue(res == 0, "Got error from rtaConnectionTable_RemoveByStack: %d", res); + assertTrue(table->count_elements == 1, "Wrong element count"); + + RtaConnection *test = rtaConnectionTable_GetByApiFd(table, b_pair[0]); + assertNotNull(test, "Could not retrieve connection that was supposed to still be there"); + + rtaConnectionTable_Destroy(&table); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_ConnectionTable); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework.c new file mode 100644 index 00000000..715908f3 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_Framework.c" +#include <ccnx/transport/transport_rta/commands/rta_Command.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include <math.h> + +typedef struct test_data { + PARCRingBuffer1x1 *commandRingBuffer; + PARCNotifier *commandNotifier; + RtaFramework *framework; +} TestData; + + +static TestData * +_createTestData(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->commandRingBuffer = parcRingBuffer1x1_Create(128, NULL); + data->commandNotifier = parcNotifier_Create(); + data->framework = rtaFramework_Create(data->commandRingBuffer, data->commandNotifier); + rtaLogger_SetLogLevel(data->framework->logger, RtaLoggerFacility_Framework, PARCLogLevel_Debug); + return data; +} + +static void +_destroyTestData(TestData *data) +{ + parcRingBuffer1x1_Release(&data->commandRingBuffer); + parcNotifier_Release(&data->commandNotifier); + rtaFramework_Destroy(&data->framework); + parcMemory_Deallocate((void **) &data); +} + +LONGBOW_TEST_RUNNER(rta_Framework) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Framework) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Framework) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// =================================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaFramework_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, rtaFramework_GetEventScheduler); + LONGBOW_RUN_TEST_CASE(Global, rtaFramework_GetNextConnectionId); + LONGBOW_RUN_TEST_CASE(Global, rtaFramework_GetStatus); + LONGBOW_RUN_TEST_CASE(Global, rtaFramework_Start_Shutdown); + LONGBOW_RUN_TEST_CASE(Global, tick_cb); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _createTestData()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _destroyTestData(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaFramework_Create_Destroy) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertNotNull(data->framework, "rtaFramework_Create returned null"); + assertTrue(data->framework->commandRingBuffer == data->commandRingBuffer, "framework commandRingBuffer incorrect"); + assertTrue(data->framework->commandNotifier == data->commandNotifier, "framework commandNotifier incorrect"); + assertNotNull(data->framework->commandEvent, "framework commandEvent is null"); +} + +LONGBOW_TEST_CASE(Global, rtaFramework_GetEventScheduler) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertTrue(rtaFramework_GetEventScheduler(data->framework) == data->framework->base, "getEventScheduler broken"); +} + +LONGBOW_TEST_CASE(Global, rtaFramework_GetNextConnectionId) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertTrue(rtaFramework_GetNextConnectionId(data->framework) == 1, "GetNextConnetionId not starting at 1"); + assertTrue(rtaFramework_GetNextConnectionId(data->framework) == 2, "GetNextConnetionId first increment not 2"); +} + +LONGBOW_TEST_CASE(Global, rtaFramework_GetStatus) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + assertTrue(rtaFramework_GetStatus(data->framework) == FRAMEWORK_INIT, "Wrong initial status"); +} + +LONGBOW_TEST_CASE(Global, rtaFramework_Start_Shutdown) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + rtaFramework_Start(data->framework); + assertTrue(rtaFramework_WaitForStatus(data->framework, FRAMEWORK_RUNNING) == FRAMEWORK_RUNNING, "Status not RUNNING"); + + // blocks until done + rtaFramework_Shutdown(data->framework); +} + +LONGBOW_TEST_CASE(Global, tick_cb) +{ + ticks tic0, tic1; + struct timeval t0, t1; + double delta_tic, delta_t, delta_abs; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + rtaFramework_Start(data->framework); + assertTrue(rtaFramework_WaitForStatus(data->framework, FRAMEWORK_RUNNING) == FRAMEWORK_RUNNING, "Status not RUNNING"); + + gettimeofday(&t0, NULL); + tic0 = data->framework->clock_ticks; + sleep(2); + gettimeofday(&t1, NULL); + tic1 = data->framework->clock_ticks; + + delta_t = (t1.tv_sec + t1.tv_usec * 1E-6) - (t0.tv_sec + t0.tv_usec * 1E-6); + delta_tic = ((tic1 - tic0) * FC_USEC_PER_TICK) * 1E-6; + delta_abs = fabs(delta_tic - delta_t); + + printf("over 2 seconds, absolute clock error is %.6f seconds\n", delta_abs); + + + // blocks until done + rtaFramework_Shutdown(data->framework); +} + +// =================================================== + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _setLogLevels_All); + LONGBOW_RUN_TEST_CASE(Local, _setLogLevels_All_Framework); + LONGBOW_RUN_TEST_CASE(Local, _setLogLevels_Framework); + LONGBOW_RUN_TEST_CASE(Local, _setLogLevels_ApiConnector); + LONGBOW_RUN_TEST_CASE(Local, _setLogLevels_FlowController); + LONGBOW_RUN_TEST_CASE(Local, _setLogLevels_Codec); + LONGBOW_RUN_TEST_CASE(Local, _setLogLevels_ForwarderConnector); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + longBowTestCase_SetClipBoardData(testCase, _createTestData()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + _destroyTestData(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _setLogLevels_All) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + setenv("RtaFacility_All", "Warning", 1); + _setLogLevels(data->framework); + + for (int i = 0; i < RtaLoggerFacility_END; i++) { + bool isLoggable = rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), i, PARCLogLevel_Warning); + assertTrue(isLoggable, "Facility %s not set to Warning", rtaLogger_FacilityString(i)); + } + + unsetenv("RtaFacility_All"); +} + +LONGBOW_TEST_CASE(Local, _setLogLevels_All_Framework) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + setenv("RtaFacility_All", "Info", 1); + setenv("RtaFacility_Framework", "Warning", 1); + _setLogLevels(data->framework); + + assertTrue(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_ApiConnector, PARCLogLevel_Info), "Api facility not Info"); + assertTrue(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_Framework, PARCLogLevel_Warning), "Framework not Warning"); + + unsetenv("RtaFacility_All"); + unsetenv("RtaFacility_Framework"); +} + +LONGBOW_TEST_CASE(Local, _setLogLevels_Framework) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + setenv("RtaFacility_Framework", "Warning", 1); + _setLogLevels(data->framework); + + assertFalse(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_Framework, PARCLogLevel_Info), "Info should not be loggable"); + assertTrue(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_Framework, PARCLogLevel_Warning), "Warning should be loggable"); + unsetenv("RtaFacility_Framework"); +} + +LONGBOW_TEST_CASE(Local, _setLogLevels_ApiConnector) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + setenv("RtaFacility_Api", "Warning", 1); + _setLogLevels(data->framework); + + assertFalse(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_ApiConnector, PARCLogLevel_Info), "Info should not be loggable"); + assertTrue(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_ApiConnector, PARCLogLevel_Warning), "Warning should be loggable"); + unsetenv("RtaFacility_Api"); +} + +LONGBOW_TEST_CASE(Local, _setLogLevels_FlowController) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + setenv("RtaFacility_Flowcontrol", "Warning", 1); + _setLogLevels(data->framework); + + assertFalse(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Info), "Info should not be loggable"); + assertTrue(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_Flowcontrol, PARCLogLevel_Warning), "Warning should be loggable"); + unsetenv("RtaFacility_Flowcontrol"); +} + +LONGBOW_TEST_CASE(Local, _setLogLevels_Codec) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + setenv("RtaFacility_Codec", "Warning", 1); + _setLogLevels(data->framework); + + assertFalse(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_Codec, PARCLogLevel_Info), "Info should not be loggable"); + assertTrue(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_Codec, PARCLogLevel_Warning), "Warning should be loggable"); + unsetenv("RtaFacility_Codec"); +} + +LONGBOW_TEST_CASE(Local, _setLogLevels_ForwarderConnector) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + setenv("RtaFacility_Forwarder", "Warning", 1); + _setLogLevels(data->framework); + + assertFalse(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_ForwarderConnector, PARCLogLevel_Info), "Info should not be loggable"); + assertTrue(rtaLogger_IsLoggable(rtaFramework_GetLogger(data->framework), RtaLoggerFacility_ForwarderConnector, PARCLogLevel_Warning), "Warning should be loggable"); + unsetenv("RtaFacility_Forwarder"); +} + +// =================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Framework); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Commands.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Commands.c new file mode 100644 index 00000000..d19d680b --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Commands.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Creates a bentpipe forwarder and then creates and runs in non-threaded Transport in + * the commonSetup() function. The function commonTeardown() undoes all that. + * + */ + +#include "../rta_Framework_Commands.c" +#include <sys/param.h> + +#include <LongBow/unit-test.h> + +#include <parc/security/parc_Pkcs12KeyStore.h> +#include <parc/security/parc_Security.h> + +#include <ccnx/api/control/cpi_ControlMessage.h> + +#include <parc/algol/parc_SafeMemory.h> + +#include <ccnx/transport/transport_rta/config/config_All.h> +#include <ccnx/transport/transport_rta/rta_Transport.h> +#include <ccnx/transport/common/transport_private.h> +#include <ccnx/transport/test_tools/traffic_tools.h> + +#include <ccnx/transport/test_tools/bent_pipe.h> + +// ============================================== +typedef struct test_data { + PARCRingBuffer1x1 *commandRingBuffer; + PARCNotifier *commandNotifier; + RtaFramework *framework; + + char bentpipe_Directory[MAXPATHLEN]; + char bentpipe_LocalName[MAXPATHLEN]; + BentPipeState *bentpipe; + char keystoreName[MAXPATHLEN]; + char keystorePassword[MAXPATHLEN]; +} TestData; + +static CCNxTransportConfig * +_createParams(const char *local_name, const char *keystore_name, const char *keystore_passwd) +{ + assertNotNull(local_name, "Got null local name\n"); + assertNotNull(keystore_name, "Got null keystore name\n"); + assertNotNull(keystore_passwd, "Got null keystore passwd\n"); + + CCNxStackConfig *stackConfig = apiConnector_ProtocolStackConfig( + tlvCodec_ProtocolStackConfig( + localForwarder_ProtocolStackConfig( + protocolStack_ComponentsConfigArgs(ccnxStackConfig_Create(), + apiConnector_GetName(), + tlvCodec_GetName(), + localForwarder_GetName(), + NULL)))); + + CCNxConnectionConfig *connConfig = apiConnector_ConnectionConfig( + localForwarder_ConnectionConfig(ccnxConnectionConfig_Create(), local_name)); + + connConfig = tlvCodec_ConnectionConfig(connConfig); + + publicKeySigner_ConnectionConfig(connConfig, keystore_name, keystore_passwd); + + CCNxTransportConfig *result = ccnxTransportConfig_Create(stackConfig, connConfig); + ccnxStackConfig_Release(&stackConfig); + return result; +} + +static void +_runNonThreaded(TestData *data) +{ + rtaFramework_NonThreadedStepTimed(data->framework, &((struct timeval) { 0, 100000 })); +} + +static void +_stopThreaded(TestData *data) +{ + printf("Beginning shutdown pid %d\n", getpid()); + // blocks until done + rtaFramework_Shutdown(data->framework); + printf("Finished shutdown pid %d\n", getpid()); +} + +static void +_stopNonThreaded(TestData *data) +{ + printf("Beginning shutdown pid %d\n", getpid()); + rtaFramework_Teardown(data->framework); + printf("Finished shutdown pid %d\n", getpid()); +} + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + + snprintf(data->bentpipe_Directory, MAXPATHLEN, "/tmp/bentpipe_XXXXXX"); + char *p = mkdtemp(data->bentpipe_Directory); + assertNotNull(p, "Got null from mkdtemp(%s)", data->bentpipe_Directory); + snprintf(data->bentpipe_LocalName, MAXPATHLEN, "%s/bentpipe.sock", data->bentpipe_Directory); + + data->bentpipe = bentpipe_Create(data->bentpipe_LocalName); + bentpipe_SetChattyOutput(data->bentpipe, false); + + printf("Staring bent pipe pid %d\n", getpid()); + bentpipe_Start(data->bentpipe); + printf("Started bent pipe\n"); + + snprintf(data->keystoreName, MAXPATHLEN, "/tmp/keystore_p12_XXXXXX"); + int fd = mkstemp(data->keystoreName); + assertTrue(fd != -1, "Error from mkstemp(%s)", data->keystoreName); + + sprintf(data->keystorePassword, "23439429"); + + bool success = parcPkcs12KeyStore_CreateFile(data->keystoreName, data->keystorePassword, "user", 1024, 30); + assertTrue(success, "parcPublicKeySignerPkcs12Store_CreateFile() failed."); + close(fd); + + data->commandRingBuffer = parcRingBuffer1x1_Create(128, NULL); + data->commandNotifier = parcNotifier_Create(); + data->framework = rtaFramework_Create(data->commandRingBuffer, data->commandNotifier); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + if (rtaFramework_GetStatus(data->framework) == FRAMEWORK_RUNNING) { + _stopThreaded(data); + } else { + _stopNonThreaded(data); + } + + parcRingBuffer1x1_Release(&data->commandRingBuffer); + parcNotifier_Release(&data->commandNotifier); + + printf("Destroying framework pid %d\n", getpid()); + rtaFramework_Destroy(&data->framework); + + bentpipe_Stop(data->bentpipe); + bentpipe_Destroy(&data->bentpipe); + unlink(data->keystoreName); + unlink(data->bentpipe_LocalName); + rmdir(data->bentpipe_Directory); + + parcMemory_Deallocate((void **) &data); +} + + +/** + * @function assertConnectionOpen + * @abstract Block on reading the 1st message out of the socket. It's the connection ready message. + * @discussion + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + */ +static void +_assertConnectionOpen(int fd) +{ + CCNxMetaMessage *firstMessage; + + rtaTransport_Recv(NULL, fd, &firstMessage, CCNxStackTimeout_Never); + + assertTrue(ccnxMetaMessage_IsControl(firstMessage), "not a control message"); + + CCNxControl *control = ccnxMetaMessage_GetControl(firstMessage); + + NotifyStatus *status = notifyStatus_ParseJSON(ccnxControl_GetJson(control)); + ccnxMetaMessage_Release(&firstMessage); + + assertTrue(notifyStatus_IsConnectionOpen(status), "Expected notifyStatus_IsConnectionOpen to be true"); + notifyStatus_Release(&status); +} + + +/** + * @function openConnection + * @abstract Opens a connection and fills in the socket pair + * @discussion + * uses rtaFramework_ExecuteOpen to directly create, does not go over the command pair + * + * @param <#param1#> + * @return <#return#> + */ +static void +_openConnection(RtaFramework *framework, CCNxTransportConfig *transportConfig, int stack_id, int socketPairOutput[]) +{ + socketpair(PF_LOCAL, SOCK_STREAM, 0, socketPairOutput); + + struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 }; + + setsockopt(socketPairOutput[0], SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + setsockopt(socketPairOutput[0], SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(socketPairOutput[1], SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + setsockopt(socketPairOutput[1], SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + + RtaCommandOpenConnection *openConnection = rtaCommandOpenConnection_Create(stack_id, socketPairOutput[1], socketPairOutput[0], + ccnxConnectionConfig_GetJson(ccnxTransportConfig_GetConnectionConfig(transportConfig))); + + _rtaFramework_ExecuteOpenConnection(framework, openConnection); + rtaCommandOpenConnection_Release(&openConnection); + + rtaFramework_NonThreadedStepCount(framework, 10); + _assertConnectionOpen(socketPairOutput[1]); +} + +static bool +_readAndCompareName(int fd, CCNxName *truthName) +{ + CCNxMetaMessage *test_msg; + + int res = rtaTransport_Recv(NULL, fd, &test_msg, CCNxStackTimeout_Never); + assertTrue(res == 0, "Got error receiving on bob's socket: %s (%d)", strerror(errno), errno); + + assertNotNull(test_msg, "Got null message from Bob"); + + assertTrue(ccnxMetaMessage_IsInterest(test_msg), "Got wrong type, expected Interest but got other"); + + CCNxInterest *interest = ccnxMetaMessage_GetInterest(test_msg); + + assertTrue(ccnxName_Compare(truthName, ccnxInterest_GetName(interest)) == 0, "Names did not compare") + { + ccnxName_Display(ccnxInterest_GetName(interest), 3); + ccnxName_Display(truthName, 3); + } + + ccnxMetaMessage_Release(&test_msg); + + return true; +} + +// ========================== + +LONGBOW_TEST_RUNNER(rta_Framework_Commands) +{ + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Framework_Commands) +{ + printf("\n********\n%s starting\n\n", __func__); + + srandom((int) time(NULL)); + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Framework_Commands) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _rtaFramework_ExecuteCloseConnection); + LONGBOW_RUN_TEST_CASE(Local, _rtaFramework_ExecuteCreateStack); + LONGBOW_RUN_TEST_CASE(Local, _rtaFramework_ExecuteOpenConnection); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + parcSecurity_Init(); + +#if __APPLE__ + pthread_setname_np(longBowTestCase_GetName(testCase)); +#else + pthread_setname_np(pthread_self(), longBowTestCase_GetName(testCase)); +#endif + + TestData *data = _commonSetup(); + _runNonThreaded(data); + + longBowTestCase_SetClipBoardData(testCase, data); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + _commonTeardown(data); + parcSecurity_Fini(); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _rtaFramework_ExecuteCloseConnection) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + int stack_id = 5; + + CCNxTransportConfig *params = _createParams(data->bentpipe_LocalName, data->keystoreName, data->keystorePassword); + + RtaCommandCreateProtocolStack *createStack = + rtaCommandCreateProtocolStack_Create(stack_id, ccnxTransportConfig_GetStackConfig(params)); + + _rtaFramework_ExecuteCreateStack(data->framework, createStack); + rtaCommandCreateProtocolStack_Release(&createStack); + + // now use three connections, then close 1 and make sure other 2 still ok + { + int alice_pair[2], bob_pair[2], charlie_pair[2]; + + _openConnection(data->framework, params, stack_id, alice_pair); + _openConnection(data->framework, params, stack_id, bob_pair); + _openConnection(data->framework, params, stack_id, charlie_pair); + + CCNxInterest *firstInterest = trafficTools_CreateInterest(); + + // send will consume the message, so copy out the name + CCNxName *truth_name = ccnxName_Copy(ccnxInterest_GetName(firstInterest)); + + CCNxMetaMessage *message = ccnxMetaMessage_CreateFromInterest(firstInterest); + bool success = rtaTransport_Send(NULL, alice_pair[1], message, CCNxStackTimeout_Never); + assertTrue(success, "Got error sending on alice's socket: %s (%d)", strerror(errno), errno); + ccnxMetaMessage_Release(&message); + + // *** Read bob + rtaFramework_NonThreadedStepCount(data->framework, 10); + _readAndCompareName(bob_pair[1], truth_name); + + // *** Read Charlie + rtaFramework_NonThreadedStepCount(data->framework, 10); + _readAndCompareName(charlie_pair[1], truth_name); + + // Close charlie and make sure alice + bob still happy + RtaCommandCloseConnection *closeConnection = rtaCommandCloseConnection_Create(charlie_pair[1]); + _rtaFramework_ExecuteCloseConnection(data->framework, closeConnection); + rtaCommandCloseConnection_Release(&closeConnection); + rtaFramework_NonThreadedStepCount(data->framework, 10); + + // send another interest + CCNxInterest *secondInterest = trafficTools_CreateInterest(); + message = ccnxMetaMessage_CreateFromInterest(secondInterest); + + success = rtaTransport_Send(NULL, alice_pair[1], message, CCNxStackTimeout_Never); + assertTrue(success, "Got error sending on alice's socket: %s (%d)", strerror(errno), errno); + ccnxMetaMessage_Release(&message); + + // make sure bob gets it + rtaFramework_NonThreadedStepCount(data->framework, 10); + _readAndCompareName(bob_pair[1], truth_name); + + ccnxName_Release(&truth_name); + ccnxInterest_Release(&firstInterest); + ccnxInterest_Release(&secondInterest); + } + + ccnxTransportConfig_Destroy(¶ms); +} + +LONGBOW_TEST_CASE(Local, _rtaFramework_ExecuteCreateStack) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + int stack_id = 4; + CCNxTransportConfig *params = _createParams(data->bentpipe_LocalName, data->keystoreName, data->keystorePassword); + RtaCommandCreateProtocolStack *createStack = + rtaCommandCreateProtocolStack_Create(stack_id, ccnxTransportConfig_GetStackConfig(params)); + + + // this call skirts around threading + _rtaFramework_ExecuteCreateStack(data->framework, createStack); + + FrameworkProtocolHolder *holder; + holder = rtaFramework_GetProtocolStackByStackId(data->framework, stack_id); + assertNotNull(holder, "There is no protocol holder for this stack, not created?"); + + ccnxTransportConfig_Destroy(¶ms); + rtaCommandCreateProtocolStack_Release(&createStack); +} + +LONGBOW_TEST_CASE(Local, _rtaFramework_ExecuteOpenConnection) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + int stack_id = 4; + CCNxTransportConfig *params = _createParams(data->bentpipe_LocalName, data->keystoreName, data->keystorePassword); + + RtaCommandCreateProtocolStack *createStack = + rtaCommandCreateProtocolStack_Create(stack_id, ccnxTransportConfig_GetStackConfig(params)); + _rtaFramework_ExecuteCreateStack(data->framework, createStack); + rtaCommandCreateProtocolStack_Release(&createStack); + + // now create two connections and make sure they work + { + // now create + int alice_pair[2], bob_pair[2]; + socketpair(PF_LOCAL, SOCK_STREAM, 0, alice_pair); + socketpair(PF_LOCAL, SOCK_STREAM, 0, bob_pair); + + _openConnection(data->framework, params, stack_id, alice_pair); + _openConnection(data->framework, params, stack_id, bob_pair); + + CCNxInterest *interest = trafficTools_CreateInterest(); + + //ccnxInterest_Display(interest, 0); + + // send will consume the message, so copy out the name + CCNxName *truth_name = ccnxName_Copy(ccnxInterest_GetName(interest)); + + //ccnxName_Display(truth_name, 0); + + // now send it down the stack + CCNxMetaMessage *message = ccnxMetaMessage_CreateFromInterest(interest); + bool success = rtaTransport_Send(NULL, alice_pair[1], message, CCNxStackTimeout_Never); + assertTrue(success, "Got error sending on alice's socket: %s (%d)", strerror(errno), errno); + ccnxMetaMessage_Release(&message); + + rtaFramework_NonThreadedStepCount(data->framework, 10); + _readAndCompareName(bob_pair[1], truth_name); + + ccnxName_Release(&truth_name); + ccnxInterest_Release(&interest); + } + + ccnxTransportConfig_Destroy(¶ms); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Framework_Commands); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_NonThreaded.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_NonThreaded.c new file mode 100644 index 00000000..fb73e15c --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_NonThreaded.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "../rta_Framework_NonThreaded.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(rta_Framework_NonThreaded) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Framework_NonThreaded) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Framework_NonThreaded) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Framework_NonThreaded); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Services.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Services.c new file mode 100644 index 00000000..6bb51f0f --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Services.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "../rta_Framework_Services.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(rta_Framework_Services) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Framework_Services) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Framework_Services) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Framework_Services); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Threaded.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Threaded.c new file mode 100644 index 00000000..74ea2f64 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Threaded.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "../rta_Framework_Threaded.c" +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(rta_Framework_Threaded) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Framework_Threaded) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Framework_Threaded) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Framework_Threaded); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Logger.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Logger.c new file mode 100644 index 00000000..240293fc --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Logger.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_Logger.c" +#include <stdio.h> +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +LONGBOW_TEST_RUNNER(rta_Logger) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Logger) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Logger) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ========================================================== + +/* + * _testWritter will vsprintf to this buffer + */ +#define _logLength 1024 +static char _lastLogMessage[_logLength]; + +static int +_testWriter(const char *message) +{ + int written = 0; + written = snprintf(_lastLogMessage, _logLength, "%s", message); + return written; +} + +static PARCLogReporter * +_testWriter_Acquire(const PARCLogReporter *reporter) +{ + return parcObject_Acquire(reporter); +} + +static void +_testWriter_Release(PARCLogReporter **reporterPtr) +{ + parcObject_Release((void **) reporterPtr); +} + +static void +_testWriter_Report(PARCLogReporter *reporter, const PARCLogEntry *entry) +{ + char *string = parcLogEntry_ToString(entry); + _testWriter(string); + parcMemory_Deallocate((void **) &string); +} + +static PARCLogReporter * +_testWriter_Create(void) +{ + return parcLogReporter_Create(_testWriter_Acquire, _testWriter_Release, _testWriter_Report, NULL); +} + +// ========================================================== + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaLogger_FacilityString_Found); + LONGBOW_RUN_TEST_CASE(Global, rtaLogger_FacilityString_NotFound); + LONGBOW_RUN_TEST_CASE(Global, rtaLogger_Create); + LONGBOW_RUN_TEST_CASE(Global, rtaLogger_Acquire); + LONGBOW_RUN_TEST_CASE(Global, rtaLogger_SetLogLevel); + LONGBOW_RUN_TEST_CASE(Global, rtaLogger_IsLoggable_True); + LONGBOW_RUN_TEST_CASE(Global, rtaLogger_IsLoggable_False); + LONGBOW_RUN_TEST_CASE(Global, rtaLogger_Log_IsLoggable); + LONGBOW_RUN_TEST_CASE(Global, rtaLogger_Log_IsNotLoggable); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaLogger_FacilityString_Found) +{ + for (RtaLoggerFacility i = 0; i < RtaLoggerFacility_END; i++) { + const char *test = rtaLogger_FacilityString(i); + assertNotNull(test, "Got null string for facility %d", i); + } +} + +LONGBOW_TEST_CASE(Global, rtaLogger_FacilityString_NotFound) +{ + const char *test = rtaLogger_FacilityString(1000); + assertTrue(strcmp(test, "Unknown") == 0, "Got wrong string for unknown facility"); +} + +LONGBOW_TEST_CASE(Global, rtaLogger_Create) +{ + PARCLogReporter *reporter = _testWriter_Create(); + RtaLogger *logger = rtaLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + + rtaLogger_Release(&logger); +} + +LONGBOW_TEST_CASE(Global, rtaLogger_Acquire) +{ + PARCLogReporter *reporter = _testWriter_Create(); + RtaLogger *logger = rtaLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + + RtaLogger *copy = rtaLogger_Acquire(logger); + rtaLogger_Release(&logger); + rtaLogger_Release(©); +} + +LONGBOW_TEST_CASE(Global, rtaLogger_SetLogLevel) +{ + PARCLogReporter *reporter = _testWriter_Create(); + RtaLogger *logger = rtaLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + + rtaLogger_SetLogLevel(logger, RtaLoggerFacility_Framework, PARCLogLevel_Off); + + PARCLogLevel test = parcLog_GetLevel(logger->loggerArray[RtaLoggerFacility_Framework]); + assertTrue(test == PARCLogLevel_Off, "wrong log level, expected %d got %d", PARCLogLevel_Off, test); + rtaLogger_Release(&logger); +} + +LONGBOW_TEST_CASE(Global, rtaLogger_IsLoggable_True) +{ + PARCLogReporter *reporter = _testWriter_Create(); + RtaLogger *logger = rtaLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + + rtaLogger_SetLogLevel(logger, RtaLoggerFacility_Framework, PARCLogLevel_Warning); + bool isLoggable = rtaLogger_IsLoggable(logger, RtaLoggerFacility_Framework, PARCLogLevel_Warning); + assertTrue(isLoggable, "Did not get true for isLoggable when expecting true"); + rtaLogger_Release(&logger); +} + +LONGBOW_TEST_CASE(Global, rtaLogger_IsLoggable_False) +{ + PARCLogReporter *reporter = _testWriter_Create(); + RtaLogger *logger = rtaLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + + rtaLogger_SetLogLevel(logger, RtaLoggerFacility_Framework, PARCLogLevel_Warning); + bool isLoggable = rtaLogger_IsLoggable(logger, RtaLoggerFacility_Framework, PARCLogLevel_Debug); + assertFalse(isLoggable, "Logging debug to warning facility should have been false"); + rtaLogger_Release(&logger); +} + +LONGBOW_TEST_CASE(Global, rtaLogger_Log_IsLoggable) +{ + PARCLogReporter *reporter = _testWriter_Create(); + RtaLogger *logger = rtaLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + + rtaLogger_SetLogLevel(logger, RtaLoggerFacility_Framework, PARCLogLevel_Warning); + memset(_lastLogMessage, 0, _logLength); + + rtaLogger_Log(logger, RtaLoggerFacility_Framework, PARCLogLevel_Warning, __func__, "hello"); + assertTrue(strlen(_lastLogMessage) > 0, "Did not write to log message"); + rtaLogger_Release(&logger); +} + +LONGBOW_TEST_CASE(Global, rtaLogger_Log_IsNotLoggable) +{ + PARCLogReporter *reporter = _testWriter_Create(); + RtaLogger *logger = rtaLogger_Create(reporter, parcClock_Wallclock()); + parcLogReporter_Release(&reporter); + + rtaLogger_SetLogLevel(logger, RtaLoggerFacility_Framework, PARCLogLevel_Warning); + memset(_lastLogMessage, 0, _logLength); + + rtaLogger_Log(logger, RtaLoggerFacility_Framework, PARCLogLevel_Debug, __func__, "hello"); + assertTrue(strlen(_lastLogMessage) == 0, "Should not have written to log message"); + rtaLogger_Release(&logger); +} + + +// ========================================================== + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Logger); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} + diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ProtocolStack.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ProtocolStack.c new file mode 100644 index 00000000..c14d799a --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ProtocolStack.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <stdio.h> + +#include <parc/algol/parc_SafeMemory.h> + +#include <LongBow/unit-test.h> + +LONGBOW_TEST_RUNNER(rta_ProtocolStack) +{ + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_ProtocolStack) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_ProtocolStack) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Local) +{ +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_ProtocolStack); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_WebService.c b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_WebService.c new file mode 100644 index 00000000..4b2fb015 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_WebService.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_WebService.c" +#include <LongBow/unit-test.h> +#include <parc/algol/parc_SafeMemory.h> + +#include <signal.h> +#include <pthread.h> +#include <errno.h> +#include <arpa/inet.h> + +LONGBOW_TEST_RUNNER(rta_WebService) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_WebService) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_WebService) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaWebService_Create_Destroy); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +struct sigaction save_sigchld; +struct sigaction save_sigpipe; + +static void +blockSigChild() +{ + struct sigaction ignore_action; + ignore_action.sa_handler = SIG_IGN; + sigemptyset(&ignore_action.sa_mask); + ignore_action.sa_flags = 0; + + sigaction(SIGCHLD, NULL, &save_sigchld); + sigaction(SIGPIPE, NULL, &save_sigpipe); + + sigaction(SIGCHLD, &ignore_action, NULL); + sigaction(SIGPIPE, &ignore_action, NULL); +} + +static void +unblockSigChild() +{ + sigaction(SIGCHLD, &save_sigchld, NULL); + sigaction(SIGPIPE, &save_sigpipe, NULL); +} + +LONGBOW_TEST_CASE(Global, rtaWebService_Create_Destroy) +{ + int fds[2]; + int failure = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds); + assertFalse(failure, "error on socketpair: (%d) %s", errno, strerror(errno)); + + RtaFramework *framework = rtaFramework_Create(fds[1]); + + // we should be runing on port 9090, so the string popen() gets + // will look like this: + // tcp4 0 0 127.0.0.1.9090 *.* LISTEN + + blockSigChild(); + FILE *fp = popen("netstat -an -p tcp", "r"); + assertNotNull(fp, "Got null opening netstat for reading"); + + char str[1035]; + bool found = false; + while (fgets(str, sizeof(str) - 1, fp) != NULL) { + if (strstr(str, "127.0.0.1.9090") != NULL) { + found = true; + break; + } + + if (strstr(str, "127.0.0.1:9090") != NULL) { + found = true; + break; + } + } + + pclose(fp); + + rtaFramework_Destroy(&framework); + + close(fds[0]); + close(fds[1]); + unblockSigChild(); + + assertTrue(found, "Did not find 127.0.0.1.9090 in netstat output"); +} + + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, rtaWebService_ProcessHelloRequest); + LONGBOW_RUN_TEST_CASE(Local, rtaWebService_ProcessRequest); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, rtaWebService_ProcessHelloRequest) +{ +#ifndef __APPLE__ + testSkip("Test broken on non-darwin"); +#endif + + blockSigChild(); + int fds[2]; + int failure = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds); + assertFalse(failure, "error on socketpair: (%d) %s", errno, strerror(errno)); + + RtaFramework *framework = rtaFramework_Create(fds[0]); + rtaFramework_Start(framework); + rtaFramework_WaitForStatus(framework, FRAMEWORK_RUNNING); + + int fd = socket(AF_INET, SOCK_STREAM, 0); + + struct sockaddr_in sin; + sin.sin_addr.s_addr = inet_addr("127.0.0.1"); + sin.sin_port = htons(9090); + + failure = connect(fd, (struct sockaddr *) &sin, sizeof(sin)); + assertFalse(failure, "error on connect: (%d) %s", errno, strerror(errno)); + + char request[] = "GET /hello HTTP/1.1\r\n\r\n"; + ssize_t write_length = write(fd, request, sizeof(request)); + assertFalse(write_length < 0, "Error writing: (%d) %s", errno, strerror(errno)); + + + struct truth_s { + char *line; + } truth[] = { + { .line = "HTTP/1.1 200 OK\r\n" }, + { .line = "" }, // do not care line for Date + { .line = "Content-Length: 18\r\n" }, + { .line = "Content-Type: text/html; charset=ISO-8859-1\r\n" }, + { .line = "\r\n" }, + { .line = "Requested: /hello\n" }, + { .line = NULL } + }; + + // read response line by line + FILE *fh = fdopen(fd, "r"); + int count = 0; + while (!feof(fh) && truth[count].line != NULL) { + assertNotNull(truth[count].line, "read too many lines: %d", count); + + char response[16384]; + fgets(response, sizeof(response), fh); + if (truth[count].line[0] != '\0') { + bool result = strcmp(truth[count].line, response) == 0; + + if (!result) { + // we need to cleanup the server or the next test will fail + rtaFramework_Shutdown(framework, fds[1]); + rtaFramework_Destroy(&framework); + close(fds[0]); + close(fds[1]); + unblockSigChild(); + assertTrue(result, "mismatched lines, expected '%s' got '%s'", truth[count].line, response); + } + } + count++; + } + fclose(fh); + + rtaFramework_Shutdown(framework, fds[1]); + rtaFramework_Destroy(&framework); + close(fds[0]); + close(fds[1]); + + unblockSigChild(); +} + +LONGBOW_TEST_CASE(Local, rtaWebService_ProcessRequest) +{ +#ifndef __APPLE__ + testSkip("Test broken on non-darwin"); +#endif + + blockSigChild(); + int fds[2]; + int failure = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds); + assertFalse(failure, "error on socketpair: (%d) %s", errno, strerror(errno)); + + RtaFramework *framework = rtaFramework_Create(fds[0]); + rtaFramework_Start(framework); + rtaFramework_WaitForStatus(framework, FRAMEWORK_RUNNING); + + int fd = socket(AF_INET, SOCK_STREAM, 0); + + struct sockaddr_in sin; + sin.sin_addr.s_addr = inet_addr("127.0.0.1"); + sin.sin_port = htons(9090); + + failure = connect(fd, (struct sockaddr *) &sin, sizeof(sin)); + assertFalse(failure, "error on connect: (%d) %s", errno, strerror(errno)); + + char request[] = "GET /foo HTTP/1.1\r\n\r\n"; + write(fd, request, sizeof(request)); + + struct truth_s { + char *line; + } truth[] = { + { .line = "HTTP/1.1 404 Document was not found\r\n" }, + { .line = "Content-Type: text/html\r\n" }, + { .line = "Connection: close\r\n" }, + { .line = "" }, // do not care line for Date + { .line = "Content-Length: 116\r\n" }, + { .line = "\r\n" }, + { .line = "<HTML><HEAD>\n" }, + { .line = "<TITLE>404 Document was not found</TITLE>\n" }, + { .line = "</HEAD><BODY>\n" }, + { .line = "<H1>Document was not found</H1>\n" }, + { .line = "</BODY></HTML>\n" }, + { .line = NULL } + }; + + // read response line by line + FILE *fh = fdopen(fd, "r"); + int count = 0; + while (!feof(fh) && truth[count].line != NULL) { + assertNotNull(truth[count].line, "read too many lines: %d", count); + + char response[16384]; + fgets(response, sizeof(response), fh); + if (truth[count].line[0] != '\0') { + assertTrue(strcmp(truth[count].line, response) == 0, "mismatched lines, expected '%s' got '%s'", truth[count].line, response); + } + count++; + } + fclose(fh); + + rtaFramework_Shutdown(framework, fds[1]); + rtaFramework_Destroy(&framework); + close(fds[0]); + close(fds[1]); + unblockSigChild(); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_WebService); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/rta_Transport.c b/libccnx-transport-rta/ccnx/transport/transport_rta/rta_Transport.c new file mode 100644 index 00000000..9724322c --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/rta_Transport.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This is the API-thread's interface to the RTA framework. It is thread-safe + * and executes in the API's thread. + * + * The only data maintained here is a mapping from the SYSTEM parameters hash + * to the stack_id. + * + * Communication with the Framework is done over a socket pair. + */ +#include <config.h> + +#include <LongBow/runtime.h> + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <pthread.h> +#include <errno.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> + +#include <string.h> +#include <fcntl.h> +#include <sys/socket.h> + +#include <parc/algol/parc_Memory.h> +//#include <parc/logging/parc_Log.h> +//#include <parc/logging/parc_LogReporterTextStdout.h> +#include <parc/concurrent/parc_RingBuffer_1x1.h> +#include <parc/concurrent/parc_Notifier.h> +#include <parc/algol/parc_Deque.h> +#include <parc/concurrent/parc_Synchronizer.h> + +#include <ccnx/transport/transport_rta/rta_Transport.h> +#include <ccnx/transport/common/transport_private.h> +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_ProtocolStack.h> +#include <ccnx/transport/transport_rta/commands/rta_Command.h> +#include <ccnx/transport/transport_rta/core/components.h> +#include <ccnx/transport/transport_rta/core/rta_ConnectionTable.h> + +// These are some internal diagnostic counters used in the debugger +// for when things are going really bad. They are incremented on each +// call to read or write. +unsigned rta_transport_reads = 0; +unsigned rta_transport_read_spin = 0; +unsigned rta_transport_writes = 0; + +// =================================================== +// The external interface + +const struct transport_operations rta_ops = { + .Create = (void * (*)(void))rtaTransport_Create, + .Open = (int (*)(void *, CCNxTransportConfig *))rtaTransport_Open, + .Send = (int (*)(void *, int, CCNxMetaMessage *, const struct timeval *restrict timeout))rtaTransport_Send, + .Recv = (TransportIOStatus (*)(void *, int, CCNxMetaMessage **, const struct timeval *restrict timeout))rtaTransport_Recv, + .Close = (int (*)(void *, int))rtaTransport_Close, + .Destroy = (int (*)(void **))rtaTransport_Destroy, + .PassCommand = (int (*)(void *, void *))rtaTransport_PassCommand +}; + +/** + * @typedef _StackEntry + * @abstract Tracks the JSON descriptions of protocol stacks + * @constant hash The hash of the JSON description + * @constant stack_id the id of the stack associated with that hash + * @constant list The linked-list member + * @discussion <#Discussion#> + */ +typedef struct json_hash_table { + PARCHashCode hash; + int stack_id; +} _StackEntry; + +typedef struct socket_pair { + int up; + int down; +} _RTASocketPair; + +struct rta_transport { + RtaFramework *framework; /**< The RTA Framework holding the transport */ + + PARCRingBuffer1x1 *commandRingBuffer; /**< Written from Transport down to Framework */ + + PARCNotifier *commandNotifier; /**< Shared with the Framework to indicates writes to the ring buffer */ + + unsigned int nextStackId; + + PARCDeque *list; +}; + +static _StackEntry * +_rtaTransport_GetStack(const RTATransport *transport, PARCHashCode hash) +{ + _StackEntry *result = NULL; + + PARCIterator *iterator = parcDeque_Iterator(transport->list); + while (parcIterator_HasNext(iterator)) { + _StackEntry *entry = parcIterator_Next(iterator); + if (entry->hash == hash) { + result = entry; + break; + } + } + parcIterator_Release(&iterator); + + return result; +} + +static _StackEntry * +_rtaTransport_AddStack(RTATransport *transport, CCNxStackConfig *stackConfig) +{ + PARCHashCode hash = ccnxStackConfig_HashCode(stackConfig); + + _StackEntry *entry = parcMemory_AllocateAndClear(sizeof(_StackEntry)); + assertNotNull(entry, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_StackEntry)); + entry->hash = hash; + entry->stack_id = transport->nextStackId++; + + parcDeque_Append(transport->list, entry); + + return entry; +} + +static void +_rtaTransport_CommandBufferEntryDestroyer(void **entryPtr) +{ +} + +static bool +_rtaTransport_SendCommandToFramework(RTATransport *transport, const RtaCommand *command) +{ + bool success = rtaCommand_Write(command, transport->commandRingBuffer); + if (success) { + parcNotifier_Notify(transport->commandNotifier); + return true; + } + return false; +} + +RTATransport * +rtaTransport_Create(void) +{ + RTATransport *transport = parcMemory_AllocateAndClear(sizeof(RTATransport)); + + if (transport != NULL) { + transport->nextStackId = 1; + + transport->commandRingBuffer = parcRingBuffer1x1_Create(128, _rtaTransport_CommandBufferEntryDestroyer); + transport->commandNotifier = parcNotifier_Create(); + + transport->framework = rtaFramework_Create(transport->commandRingBuffer, transport->commandNotifier); + assertNotNull(transport->framework, "rtaFramework_Create returned null"); + + rtaFramework_Start(transport->framework); + transport->list = parcDeque_Create(); + } + + return transport; +} + +int +rtaTransport_Destroy(RTATransport **ctxPtr) +{ + assertNotNull(ctxPtr, "called with null context pointer"); + RTATransport *transport = *ctxPtr; + + // %%%%% LOCK (notice this lock never gets unlocked, it just gets deleted) + parcDeque_Lock(transport->list); + + // This blocks until shutdown (state FRAMEWORK_SHUTDOWN) + rtaFramework_Shutdown(transport->framework); + + // This will close and drain all the API fds + rtaFramework_Destroy(&transport->framework); + + parcNotifier_Release(&transport->commandNotifier); + parcRingBuffer1x1_Release(&transport->commandRingBuffer); + + // Destroy the state we have stored locally to map JSON protocol stack descriptions + // to stack_id identifiers. + + for (size_t index = 0; index < parcDeque_Size(transport->list); index++) { + _StackEntry *entry = parcDeque_GetAtIndex(transport->list, index); + parcMemory_Deallocate((void **) &entry); + } + + parcDeque_Release(&transport->list); + + parcMemory_Deallocate((void **) ctxPtr); + +// printf("rta_transport writes=%9u reads=%9u spins=%9u\n", rta_transport_writes, rta_transport_reads, rta_transport_read_spin); + return 0; +} + +static _RTASocketPair +_rtaTransport_CreateSocketPair(const RTATransport *transport, int bufferSize) +{ + int fds[2]; + + bool success = (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) == 0); + assertTrue(success, "socketpair(PF_LOCAL, SOCK_STREAM, ...) failed."); + + _RTASocketPair result = { .up = fds[0], .down = fds[1] }; + + // Set buffer size + int sendbuff = bufferSize; + + success = (setsockopt(result.up, SOL_SOCKET, SO_RCVBUF, &sendbuff, sizeof(sendbuff)) == 0); + assertTrue(success, "Expected success for setsockopt SO_RCVBUF"); + + success = (setsockopt(result.down, SOL_SOCKET, SO_RCVBUF, &sendbuff, sizeof(sendbuff)) == 0); + assertTrue(success, "Expected success for setsockopt SO_RCVBUF"); + + return result; +} + +/** + * Returns the protocol stack entry from our table + * + * Determine if we already have a protocol stack with the same structure as the user asks for. + * If so, return that entry, otherwise return NULL + * + * @param [in] transport The RTA transport + * @param [in] transportConfig the configuration the user is asking for + * + * @return non-NULL The existing protocol stack holder + * @return NULL Configuration does not exist + */ +static _StackEntry * +_rtaTransport_GetProtocolStackEntry(RTATransport *transport, CCNxTransportConfig *transportConfig) +{ + PARCHashCode hash = ccnxStackConfig_HashCode(ccnxTransportConfig_GetStackConfig(transportConfig)); + + _StackEntry *stack = _rtaTransport_GetStack(transport, hash); + return stack; +} + +/** + * Add a protocol stack + * + * Adds an entry to our local table of Config -> stack_id mapping and sends a + * command over the command socket to create the protocol stack. + * + * @param [in] transport The RTA transport + * @param [in] transportConfig the user specified configuration + * + * @return non-NULL The holder of the protocol stack mapping + * @return NULL An error + */ +static _StackEntry * +_rtaTransport_AddProtocolStackEntry(RTATransport *transport, const CCNxTransportConfig *transportConfig) +{ + CCNxStackConfig *stackConfig = ccnxTransportConfig_GetStackConfig(transportConfig); + + _StackEntry *stack = _rtaTransport_AddStack(transport, stackConfig); + + RtaCommandCreateProtocolStack *createStack = rtaCommandCreateProtocolStack_Create(stack->stack_id, stackConfig); + + // request for a new protocol stack, create it + + // now actually create the protocol stack by writing a command over the thread boundary + // using the Command socket. + RtaCommand *command = rtaCommand_CreateCreateProtocolStack(createStack); + _rtaTransport_SendCommandToFramework(transport, command); + + rtaCommand_Release(&command); + rtaCommandCreateProtocolStack_Release(&createStack); + + return stack; +} + +/** + * Create a new connection + * + * We have resolved that a matching protocol stack exists, and is represented by + * protocolStackHashEntry. We now want to send a command over the command socket to + * create a connection in that stack. + * + * @param [in] transport The RTA transport + * @param [in] transportConfig The user requested configuration + * @param [in] protocolStackHashEntry The protocol stack holder + * @param [in] pair A _RTASocketPair representing the queue of data between the API and the transport stack. + */ +static void +_rtaTransport_CreateConnection(RTATransport *transport, CCNxTransportConfig *transportConfig, _StackEntry *stack, _RTASocketPair pair) +{ + RtaCommandOpenConnection *openConnection = + rtaCommandOpenConnection_Create(stack->stack_id, + pair.up, + pair.down, + ccnxConnectionConfig_GetJson(ccnxTransportConfig_GetConnectionConfig(transportConfig))); + + RtaCommand *command = rtaCommand_CreateOpenConnection(openConnection); + _rtaTransport_SendCommandToFramework(transport, command); + + rtaCommand_Release(&command); + rtaCommandOpenConnection_Release(&openConnection); +} + +int +rtaTransport_Open(RTATransport *transport, CCNxTransportConfig *transportConfig) +{ + ccnxTransportConfig_OptionalAssertValid(transportConfig); + + assertNotNull(transport, "Parameter transport must be a valid RTATransport"); + + _RTASocketPair pair = _rtaTransport_CreateSocketPair(transport, sizeof(void *) * 128); + + parcDeque_Lock(transport->list); + { + _StackEntry *stack = _rtaTransport_GetProtocolStackEntry(transport, transportConfig); + if (stack == NULL) { + stack = _rtaTransport_AddProtocolStackEntry(transport, transportConfig); + } + assertNotNull(stack, "Got NULL hash entry from _rtaTransport_AddProtocolStackEntry"); + + _rtaTransport_CreateConnection(transport, transportConfig, stack, pair); + } + parcDeque_Unlock(transport->list); + + return pair.up; +} + +/** + * timeout is either NULL or a pointer to an unsigned integer containing the number of microseconds to wait for input. + * + * @return <0 An error occured + * @return 0 A timeout occurred waiting for the filedescriptor to have some output space available. + * @return >0 The filedescriptor has some output space available. + */ +static int +_rtaTransport_SendSelect(const int fd, const uint64_t *microSeconds) +{ + struct timeval timeval; + fd_set writeSet; + + FD_ZERO(&writeSet); // clear the set + FD_SET(fd, &writeSet); // add our file descriptor to the set + + struct timeval *timeout = NULL; + + if (microSeconds != NULL) { + timeval.tv_sec = (int) (*microSeconds / 1000000); + timeval.tv_usec = (int) (*microSeconds % 1000000); + timeout = &timeval; + } + + int selectResult = select(fd + 1, NULL, &writeSet, NULL, timeout); + + return selectResult; +} + +bool +rtaTransport_Send(RTATransport *transport, int queueId, const CCNxMetaMessage *message, const uint64_t *microSeconds) +{ + // Acquire a reference to the incoming CCNxMetaMessage so if the caller releases it immediately, + // a reference still exists for the transport. This reference is released once the + // message is processed lower in the stack. + CCNxMetaMessage *metaMessage = ccnxMetaMessage_Acquire(message); + + rta_transport_writes++; + + int selectResult = _rtaTransport_SendSelect(queueId, microSeconds); + if (selectResult < 0) { + // We couldn't send it. Release our reference and return signaling failure. + ccnxMetaMessage_Release(&metaMessage); + return false; + } else if (selectResult == 0) { + errno = EWOULDBLOCK; + ccnxMetaMessage_Release(&metaMessage); + return false; + } else if (selectResult > 0) { + ssize_t count = write(queueId, &metaMessage, sizeof(&metaMessage)); + if (count == sizeof(&metaMessage)) { + return true; + } + } + + // We couldn't send it. Release our reference and return signaling failure. + ccnxMetaMessage_Release(&metaMessage); + + return false; +} + +//#if 1 +/** + * @return -1 An error occured + * @return 0 A timeout occurred waiting for the filedescriptor to have some input available. + * @return >0 The filedescriptor has some input ready. + */ +static int +_rtaTransport_ReceiveSelect(const int fd, const uint64_t *microSeconds) +{ + fd_set readSet; + + FD_ZERO(&readSet); // clear the set + FD_SET(fd, &readSet); // add our file descriptor to the set + + struct timeval *timeout = NULL; + struct timeval timeval; + + if (microSeconds != NULL) { + timeval.tv_sec = (int) (*microSeconds / 1000000); + timeval.tv_usec = (int) (*microSeconds % 1000000); + timeout = &timeval; + } + int selectResult = select(fd + 1, &readSet, NULL, NULL, (struct timeval *) timeout); + + return selectResult; +} + +TransportIOStatus +rtaTransport_Recv(RTATransport *transport, const int queueId, CCNxMetaMessage **msgPtr, const uint64_t *microSeconds) +{ + // The effect here is to transfer the reference to the CCNxMetaMessage to the application-side thread. + // Thus, no acquire or release here as the caller is responsible for releasing the CCNxMetaMessage + + int selectResult = _rtaTransport_ReceiveSelect(queueId, microSeconds); + + if (selectResult == -1) { + // errno should have been set by the select(2) system call. + return TransportIOStatus_Error; + } else if (selectResult == 0) { + // errno = EWOULDBLOCK; + errno = ENOMSG; + return TransportIOStatus_Timeout; + } + + size_t remaining = sizeof(&*msgPtr); + uint8_t *bytes = (uint8_t *) msgPtr; + + do { + ssize_t nread = read(queueId, &bytes[sizeof(&*msgPtr) - remaining], remaining); + if (nread == -1 && errno != EINTR) { + return TransportIOStatus_Error; + } + if (nread == 0) { + rta_transport_read_spin++; + } + remaining -= nread; + } while (remaining > 0); + + rta_transport_reads++; + + errno = 0; + return TransportIOStatus_Success; +} +//#else +///** +// * @return -1 An error occured +// * @return 0 A timeout occurred waiting for the filedescriptor to have some input available. +// * @return >0 The filedescriptor has some input ready. +// */ +//static int +//_rtaTransport_Select(const int fd, const struct timeval *restrict timeout) +//{ +// fd_set readSet; +// +// FD_ZERO(&readSet); // clear the set +// FD_SET(fd, &readSet); // add our file descriptor to the set +// +// int selectResult = select(fd + 1, &readSet, NULL, NULL, (struct timeval *) timeout); +// +// return selectResult; +//} +// +//TransportIOStatus +//rtaTransport_Recv(RTATransport *transport, const int queueId, CCNxMetaMessage **msgPtr, const struct timeval *restrict timeout) +//{ +// // The effect here is to transfer the reference to the CCNxMetaMessage to the application-side thread. +// // Thus, no acquire or release here as the caller is responsible for releasing the CCNxMetaMessage +// +// int selectResult = _rtaTransport_Select(queueId, timeout); +// +// if (selectResult == -1) { +// // errno should have been set by the select(2) system call. +// return TransportIOStatus_Error; +// } else if (selectResult == 0) { +//// errno = EWOULDBLOCK; +// errno = ENOMSG; +// return TransportIOStatus_Timeout; +// } +// +// size_t remaining = sizeof(&*msgPtr); +// uint8_t *bytes = (uint8_t *) msgPtr; +// +// do { +// ssize_t nread = read(queueId, &bytes[sizeof(&*msgPtr) - remaining], remaining); +// if (nread == -1 && errno != EINTR) { +// return TransportIOStatus_Error; +// } +// if (nread == 0) { +// rta_transport_read_spin++; +// } +// remaining -= nread; +// } while (remaining > 0); +// +// rta_transport_reads++; +// +// errno = 0; +// return TransportIOStatus_Success; +//} +//#endif + +int +rtaTransport_Close(RTATransport *transport, int api_fd) +{ + RtaCommandCloseConnection *commandClose = rtaCommandCloseConnection_Create(api_fd); + RtaCommand *command = rtaCommand_CreateCloseConnection(commandClose); + rtaCommandCloseConnection_Release(&commandClose); + + _rtaTransport_SendCommandToFramework(transport, command); + + rtaCommand_Release(&command); + + return 0; +} + +int +rtaTransport_PassCommand(RTATransport *transport, const RtaCommand *rtacommand) +{ + _rtaTransport_SendCommandToFramework(transport, rtacommand); + + return 0; +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/rta_Transport.h b/libccnx-transport-rta/ccnx/transport/transport_rta/rta_Transport.h new file mode 100644 index 00000000..44ac7cb0 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/rta_Transport.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <stdint.h> +#include <ccnx/transport/common/transport.h> + +/** + * rtaTransport executes in the API's thread. It glues the bottom half of + * the Transport API to the RTA transport. It owns and manages a worker thread + * in which the event schduler executes. + * + * rtaTransport is thread safe. You may have multiple threads using the same + * transport context. + * + * Inside the worker thread, the event scheduler executes without locks. Therefore we need + * to message pass to it and have it execute our commands in a managed callback. + * This is done by passing commands (JSON) over a socket pair. + * + * Inside the worker thread, rta_Framework provides service utilities to components + * and connectors. It also manages the command socket. + * + * When an API calls <code>int rtaTransport_Open(CCNxJSON *params)</code>, rtaTransport + * will create a socket pair and give one back to the api (api_fd) and send one to + * rtaFramework (transport_fd). + * + * The socket commands are (in JSON): + * + * PARAMS := existing SYSTEM and USER JSON objects, i.e.: + * { "SYSTEM" : {...}, "USER" : {...} } + * + * { "RTA" : { "CREATE STACK" : stack_id, PARAMS } + * { "RTA" : { "OPEN" : [stack_id, api_fd, transport_fd], PARAMS } } + * { "RTA" : { "CLOSE": transport_fd } } + * { "RTA" : { "DESTROY STACK": stack_id } } + * { "RTA" : { "SHUTDOWN" } + * + * See rta_Commands.h for an implementation of this. + */ +#ifndef Libccnx_rta_Transport_h +#define Libccnx_rta_Transport_h + +#include <ccnx/transport/common/transport.h> +#include <ccnx/transport/transport_rta/commands/rta_Command.h> + +/** + * Transport Ready To Assemble context + * + */ +struct rta_transport; +typedef struct rta_transport RTATransport; + +/** + * Structure of function points to operate on Transport RTA + * + */ +extern const struct transport_operations rta_ops; + +/** + * Create the transport. No locks here, as rtaFramework_Create and rtaFramework_Start + * are thread-safe functions and we dont maintain any data. + * + */ +RTATransport *rtaTransport_Create(void); + +int rtaTransport_Destroy(RTATransport **ctxPtr); + +int rtaTransport_Open(RTATransport *ctx, CCNxTransportConfig *transportConfig); + +/** + * Send a CCNxMetaMessage on the outbound direction of the stack. + * + * @param [in] transport A pointer to a valid RTATransport instance. + * @param [in] queueId The identifier of the asynchronous queue between the top and bottom halves of the stack. + * @param [in] message A pointer to a valid CCNxMetaMessage instance. + * + * @return true The send was successful + * @return false The send was not successful + */ +//bool rtaTransport_Send(RTATransport *transport, int queueId, const CCNxMetaMessage *message, const struct timeval *restrict timeout); +bool rtaTransport_Send(RTATransport *transport, int queueId, const CCNxMetaMessage *message, const uint64_t *microSeconds); + +TransportIOStatus rtaTransport_Recv(RTATransport *transport, const int queueId, CCNxMetaMessage **msgPtr, const uint64_t *microSeconds); + +int rtaTransport_Close(RTATransport *transport, int desc); + +int rtaTransport_PassCommand(RTATransport *transport, const RtaCommand *rtacommand); + +#endif // Libccnx_rta_Transport_h diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/test/.gitignore b/libccnx-transport-rta/ccnx/transport/transport_rta/test/.gitignore new file mode 100644 index 00000000..90005943 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/test/.gitignore @@ -0,0 +1,19 @@ +rtatest +test_bent_pipe +test_component_Ccnd_Registrar +test_component_Codec_Ccnb +test_component_Codec_Tlv_Hmac +test_connector_Api +test_connector_Forwarder_Ccnd +test_connector_Forwarder_Flan +test_connector_Forwarder_Local +test_fc_vegas +test_multi_connections +test_rta_Commands +test_rta_ConnectionTable +test_rta_Framework +test_rta_Framework_Commands +test_rta_WebService +test_system_passthrough +test_connector_Forwarder_Metis + diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/test/CMakeLists.txt b/libccnx-transport-rta/ccnx/transport/transport_rta/test/CMakeLists.txt new file mode 100644 index 00000000..c52b0166 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/test/CMakeLists.txt @@ -0,0 +1,13 @@ +# Enable gcov output for the tests +add_definitions(--coverage) +set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage") + +set(TestsExpectedToPass + test_multi_connections + test_rta_Transport +) + + +foreach(test ${TestsExpectedToPass}) + AddTest(${test}) +endforeach() diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/test/README b/libccnx-transport-rta/ccnx/transport/transport_rta/test/README new file mode 100644 index 00000000..8a325828 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/test/README @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +These are system tests, not specific to any one component or connector diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/test/test_multi_connections.c b/libccnx-transport-rta/ccnx/transport/transport_rta/test/test_multi_connections.c new file mode 100644 index 00000000..8f0051a0 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/test/test_multi_connections.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include <sys/un.h> +#include <strings.h> +#include <fcntl.h> +#include <limits.h> + +#include <LongBow/unit-test.h> + + +#include <ccnx/api/control/cpi_ControlMessage.h> +#include <ccnx/common/ccnx_ContentObject.h> + +#include <parc/security/parc_Pkcs12KeyStore.h> +#include <parc/security/parc_PublicKeySigner.h> +#include <parc/security/parc_Security.h> +#include <parc/algol/parc_SafeMemory.h> + +#include <ccnx/transport/transport_rta/config/config_All.h> +#include <ccnx/transport/transport_rta/core/rta_Framework.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_Commands.c> +#include <ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.h> + +#include "../../test_tools/bent_pipe.h" +#include <ccnx/transport/test_tools/traffic_tools.h> + +static const char local_name[] = "/tmp/beta"; +static const char alice_keystore_name[] = "/tmp/alice_keystore"; +static const char bob_keystore_name[] = "/tmp/bob_keystore"; + +static int alice_fd; +static int bob_fd; +static TransportContext *transport_context; +static CCNxTransportConfig *alice_params; +static CCNxTransportConfig *bob_params; + +// reflector for FWD_LOCAL +static BentPipeState *bentpipe; + +static int rnd_fd; + +// for statistics +static double total_delay; +static double total_bytes_per_sec; +static unsigned item_count; + +// ====================================================== + +static CCNxTransportConfig * +MultipleConnections_createParams(const char *local_name, const char *keystore_name, const char *keystore_passwd, const char *nonce) +{ + assertNotNull(local_name, "Got null keystore name\n"); + assertNotNull(keystore_name, "Got null keystore name\n"); + assertNotNull(keystore_passwd, "Got null keystore passwd\n"); + + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + + apiConnector_ProtocolStackConfig( + tlvCodec_ProtocolStackConfig( + localForwarder_ProtocolStackConfig( + protocolStack_ComponentsConfigArgs(stackConfig, + apiConnector_GetName(), + tlvCodec_GetName(), + localForwarder_GetName(), + NULL) + ))); + + CCNxConnectionConfig *connConfig = apiConnector_ConnectionConfig( + tlvCodec_ConnectionConfig( + localForwarder_ConnectionConfig(ccnxConnectionConfig_Create(), local_name))); + + publicKeySigner_ConnectionConfig(connConfig, keystore_name, keystore_passwd); + + + // add the special nonce + PARCJSONValue *value = parcJSONValue_CreateFromCString(nonce); + ccnxStackConfig_Add(stackConfig, "nonce", value); + parcJSONValue_Release(&value); + + CCNxTransportConfig *result = ccnxTransportConfig_Create(stackConfig, connConfig); + ccnxStackConfig_Release(&stackConfig); + + return result; +} + +/** + * @function sendRandomObject + * @abstract Sends a content object over the given socket. + * @discussion + * The payload of the content object is a "struct timeval" for timing purposes. + * + * @param Transport socket to use + * @return A copy of the content object sent + */ +static CCNxContentObject * +sendRandomObject(int output_fd, int fixed_size) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + + if (fixed_size < (int) sizeof(tv)) { + fixed_size = sizeof(tv); + } + + uint8_t *buffer = parcMemory_Allocate(fixed_size); + assertNotNull(buffer, "parcMemory_Allocate(%d) returned NULL", fixed_size); + memcpy(buffer, (uint8_t *) &tv, sizeof(tv)); + + PARCBuffer *contents = parcBuffer_Flip(parcBuffer_CreateFromArray(buffer, fixed_size)); + CCNxContentObject *object = trafficTools_CreateContentObjectWithPayload(contents); + parcBuffer_Release(&contents); + + // Return value not checked. + // This creates a reference to object, so we still hold the memory and can return it + CCNxMetaMessage *meta = ccnxMetaMessage_CreateFromContentObject(object); + Transport_Send(output_fd, meta); + ccnxMetaMessage_Release(&meta); + + parcMemory_Deallocate((void **) &buffer); + + // truth_co and truth_msg will be freed by Transport_Send + return object; +} + +/** + * @function recvAndCompare + * @abstract Block on receiving a message on the input_fd, then assert its the same as the truth_obj. + * @discussion + * <#Discussion#> + * + * @param <#param1#> + * @return <#return#> + */ +static bool +recvAndCompare(int input_fd, CCNxContentObject *truth_obj) +{ + struct timeval now, *then, delta; + + CCNxMetaMessage *test_msg; + int res = Transport_Recv(input_fd, &test_msg); + + assertTrue(res == 0, "got error from Transport_Recv (%d)", res); + + // We can't directly compare the two dictionaries with CCNxTlvDictionary_Equals(), + // because the test_obj that we read in was signed in the Transport when + // it was sent. So the dictionaries are different. + + // So, instead, compare the payload - which should have the time at which the ContentObject + // was created. + + CCNxContentObject *testObject = ccnxMetaMessage_GetContentObject(test_msg); + PARCBuffer *contentsA = ccnxContentObject_GetPayload(testObject); + PARCBuffer *contentsB = ccnxContentObject_GetPayload(truth_obj); + assertTrue(parcBuffer_Equals(contentsA, contentsB), "Payloads do not compare"); + + then = (struct timeval *) parcBuffer_Overlay(contentsA, 0); + + gettimeofday(&now, NULL); + timersub(&now, then, &delta); + + double delay = delta.tv_sec + 1E-6 * delta.tv_usec; + double bytes_per_sec = parcBuffer_Remaining(parcBuffer_Rewind(contentsA)) / delay; + + total_delay += delay; + total_bytes_per_sec += bytes_per_sec; + item_count++; + + ccnxMetaMessage_Release(&test_msg); + return true; +} + +static void +assertConnectionOpen(int fd) +{ + // wait for the CONNECTION_OPEN messages + CCNxMetaMessage *firstMessage; + Transport_Recv(fd, &firstMessage); + + assertTrue(ccnxMetaMessage_IsControl(firstMessage), "Expected first message to be a control message"); + + CCNxControl *control = ccnxMetaMessage_GetControl(firstMessage); + + if (ccnxControl_IsNotification(control)) { + NotifyStatus *status = ccnxControl_GetNotifyStatus(control); + + assertTrue(notifyStatus_IsConnectionOpen(status), "Expected notifyStatus_IsConnectionOpen to be true"); + + notifyStatus_Release(&status); + } + + ccnxMetaMessage_Release(&firstMessage); +} + +static void +stackSetup(const char *alice_nonce, const char *bob_nonce) +{ + unlink(local_name); + + bentpipe = bentpipe_Create(local_name); + bentpipe_SetChattyOutput(bentpipe, false); + bentpipe_Start(bentpipe); + + transport_context = Transport_Create(TRANSPORT_RTA); + + assertNotNull(transport_context, "transportRta_Create() returned null"); + + unlink(alice_keystore_name); + unlink(bob_keystore_name); + + bool success = parcPkcs12KeyStore_CreateFile(alice_keystore_name, "23456", "alice", 1024, 30); + assertTrue(success, "parcPkcs12Store_CreateFile() failed."); + success = parcPkcs12KeyStore_CreateFile(bob_keystore_name, "34567", "bob", 2048, 15); + assertTrue(success, "parcPkcs12Store_CreateFile() failed."); + + alice_params = MultipleConnections_createParams(local_name, alice_keystore_name, "23456", alice_nonce); + bob_params = MultipleConnections_createParams(local_name, bob_keystore_name, "34567", bob_nonce); + + // open a connection, this will cause accpet() to fire + alice_fd = Transport_Open(alice_params); + bob_fd = Transport_Open(bob_params); + + assertFalse(alice_fd < 0, "Transport_Open returned error"); + assertFalse(bob_fd < 0, "Transport_Open returned error"); + + assertConnectionOpen(alice_fd); + assertConnectionOpen(bob_fd); +} + +static void +stackTearDown(const char *alice_nonce, const char *bob_nonce) +{ + assertTrue(unlink(alice_keystore_name) == 0 || errno == ENOENT, + "Unable to unlink the file %s: %s", alice_keystore_name, strerror(errno)); + + assertTrue(unlink(bob_keystore_name) == 0 || errno == ENOENT, + "Unable to unlink the file %s: %s", bob_keystore_name, strerror(errno)); + + Transport_Destroy(&transport_context); + bentpipe_Stop(bentpipe); + bentpipe_Destroy(&bentpipe); + + ccnxTransportConfig_Destroy(&alice_params); + ccnxTransportConfig_Destroy(&bob_params); +} + +#include <parc/algol/parc_Object.h> + +/** + * @function ping + * @abstract Send a message from one socket to another socket + * @discussion + * Send a content object from one socket to another, then esure the + * unsigned parts of the received message compare to the sent message. + * + * There's a minimum size (sizeof struct timeval). If the fixed_size is + * larger than that minimum, we'll pad out to the fixed size. + * + * @param fixed_size is the payload content size. + * @return <#return#> + */ +static bool +ping(int from_fd, int to_fd, int fixed_size) +{ + CCNxContentObject *object = sendRandomObject(from_fd, fixed_size); + bool success = recvAndCompare(to_fd, object); + assertTrue(success, "sent and received didn't compare!\n"); + ccnxContentObject_Release(&object); + return success; +} +/** + * use -1 for random size, othewise anything larger than 16 works + * for the payload size + */ +static void +playPingPong(int fixed_size) +{ + int loops = 10; + while (loops-- > 0) { + // send down alice and up bob, then bob to alice + ping(alice_fd, bob_fd, fixed_size); + ping(bob_fd, alice_fd, fixed_size); + } +} + +// ====================================================== + + +LONGBOW_TEST_RUNNER(MultipleConnections) +{ + LONGBOW_RUN_TEST_FIXTURE(SameStack); + LONGBOW_RUN_TEST_FIXTURE(DifferentStacks); +} + +LONGBOW_TEST_RUNNER_SETUP(MultipleConnections) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + rnd_fd = open("/dev/urandom", O_RDONLY); + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_RUNNER_TEARDOWN(MultipleConnections) +{ + close(rnd_fd); + return LONGBOW_STATUS_SUCCEEDED; +} + +// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + +/* + * Same Stack tests multiple connections within the same + * protocol stack + */ + +LONGBOW_TEST_FIXTURE(SameStack) +{ + LONGBOW_RUN_TEST_CASE(SameStack, alice_bob_pingpong); +} + +LONGBOW_TEST_FIXTURE_SETUP(SameStack) +{ + parcSecurity_Init(); + stackSetup("apple", "apple"); + + total_delay = total_bytes_per_sec = 0.0; + item_count = 0; + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(SameStack) +{ + longBowDebug("average delay %.6f sec, avg bytes/sec %.3f\n", + total_delay / item_count, total_bytes_per_sec / item_count); + + stackTearDown("apple", "apple"); + + parcSecurity_Fini(); + + if (parcMemory_Outstanding() != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + parcSafeMemory_ReportAllocation(STDOUT_FILENO); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(SameStack, alice_bob_pingpong) +{ + playPingPong(8192); +} + +// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + +/* + * DifferentStacks tests multiple connections through + * different stacks in the same transport + */ + +LONGBOW_TEST_FIXTURE(DifferentStacks) +{ + LONGBOW_RUN_TEST_CASE(DifferentStacks, alice_bob_pingpong); +} + +LONGBOW_TEST_FIXTURE_SETUP(DifferentStacks) +{ + parcSecurity_Init(); + stackSetup("apple", "oranges"); + total_delay = total_bytes_per_sec = 0.0; + item_count = 0; + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(DifferentStacks) +{ + stackTearDown("apple", "oranges"); + + parcSecurity_Fini(); + + if (parcMemory_Outstanding() != 0) { + printf("('%s' leaks memory by %d (allocs - frees)) ", longBowTestCase_GetName(testCase), parcMemory_Outstanding()); + return LONGBOW_STATUS_MEMORYLEAK; + } + + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(DifferentStacks, alice_bob_pingpong) +{ + playPingPong(-1); +} + +// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +// $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(MultipleConnections); + exit(longBowMain(argc, argv, testRunner, NULL)); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/test/test_rta_Commands.c b/libccnx-transport-rta/ccnx/transport/transport_rta/test/test_rta_Commands.c new file mode 100644 index 00000000..4f4d7cb0 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/test/test_rta_Commands.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. +#include "../rta_Commands.c" + +#include "../core/components.h" +#include <ccnx/transport/transport_rta/config/config_All.h> +#include <parc/algol/parc_SafeMemory.h> +#include <LongBow/unit-test.h> +#include <strings.h> + +static TransportConfig * +Test_createParams(const char *local_name) +{ + assertNotNull(local_name, "%s got null keystore name\n", __func__); + + ConnectionConfig *connConfig = apiConnector_ConnectionConfig(localForwarder_ConnectionConfig(ccnxConnectionConfig_Create(), local_name)); + + CCNxStackConfig *stackConfig = apiConnector_ProtocolStackConfig( + localForwarder_ProtocolStackConfig( + protocolStack_ComponentsConfigArgs( + ccnxStackConfig_Create(), apiConnector_GetName(), localForwarder_GetName(), NULL))); + + return transportConfig_Create(stackConfig, connConfig); +} + +LONGBOW_TEST_RUNNER(rta_Commands) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Commands) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Commands) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE(Global) +{ + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Close); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_CreateStack); + + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_DestroyStack); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_GetClose); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_GetCreateStack); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_GetDestroyStack); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_GetOpen); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_GetType); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Open); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Read_Write); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_Shutdown); + LONGBOW_RUN_TEST_CASE(Global, rtaCommand_CreateTransmitStatistics); + LONGBOW_RUN_TEST_CASE(Global, CommandTransmitStatistics_FromJSON); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaCommand_Close) +{ + CommandClose commmandClose = { .api_fd = 7 }; + char *truth = "{ \"RTA\" : { \"CLOSE\" : 7 } }"; + PARCJSON *truth_json = parcJSON_ParseString(truth); + char *truth_formatted = ccnxJson_ToString(truth_json); + char *test; + + RtaCommand *command = rtaCommand_Close(commmandClose); + + assertTrue(command->type == RTA_COMMAND_CLOSE, "Type not RTA_COMMAND_CLOSE"); + + test = ccnxJson_ToString(command->command); + assertTrue(strcasecmp(test, truth_formatted) == 0, + "JSON does not match\nexpected: %s\ngot: %s\n", + truth_formatted, test); + + rtaCommand_Destroy(&command); + parcJSON_Release(&truth_json); + parcMemory_Deallocate((void **) &test); + parcMemory_Deallocate((void **) &truth_formatted); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_CreateStack) +{ + TransportConfig *params = Test_createParams("/tmp/fwd"); + CCNxStackConfig *stackConfig = transportConfig_GetProtocolStackConfig(params); + char *params_str = ccnxJson_ToString(ccnxStackConfig_GetJson(stackConfig)); + + CommandCreateStack commandCreateStack = { .stack_id = 9, .params = ccnxStackConfig_GetJson(stackConfig) }; + char *truth = "{ \"RTA\" : { \"CREATE STACK\" : 9, \"PARAMS\" : %s } }"; + char buffer[1024]; + + sprintf(buffer, truth, params_str); + + PARCJSON *truth_json = parcJSON_ParseString(buffer); + char *truth_formatted = ccnxJson_ToString(truth_json); + char *test; + + RtaCommand *command = rtaCommand_CreateStack(commandCreateStack); + + assertTrue(command->type == RTA_COMMAND_CREATESTACK, "Type not RTA_COMMAND_CREATESTACK"); + + test = ccnxJson_ToString(command->command); + assertTrue(strcasecmp(test, truth_formatted) == 0, + "JSON does not match\nexpected: %s\ngot: %s\n", + truth_formatted, test); + + rtaCommand_Destroy(&command); + parcJSON_Release(&truth_json); + transportConfig_Destroy(¶ms); + parcMemory_Deallocate((void **) &test); + parcMemory_Deallocate((void **) &truth_formatted); + parcMemory_Deallocate((void **) ¶ms_str); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_DestroyStack) +{ + CommandDestroyStack commandDestroyStack = { .stack_id = 2 }; + char *truth = "{ \"RTA\" : { \"DESTROY STACK\" : 2 } }"; + PARCJSON *truth_json = parcJSON_ParseString(truth); + char *truth_formatted = ccnxJson_ToString(truth_json); + char *test; + + RtaCommand *command = rtaCommand_DestroyStack(commandDestroyStack); + + assertTrue(command->type == RTA_COMMAND_DESTROYSTACK, "Type not RTA_COMMAND_DESTROYSTACK"); + + test = ccnxJson_ToString(command->command); + assertTrue(strcasecmp(test, truth_formatted) == 0, + "JSON does not match\nexpected: %s\ngot: %s\n", + truth_formatted, test); + + rtaCommand_Destroy(&command); + parcJSON_Release(&truth_json); + parcMemory_Deallocate((void **) &test); + parcMemory_Deallocate((void **) &truth_formatted); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_GetClose) +{ + CommandClose commmandClose = { .api_fd = 7 }; + CommandClose test; + + RtaCommand *command = rtaCommand_Close(commmandClose); + rtaCommand_GetClose(command, &test); + assertTrue(memcmp(&commmandClose, &test, sizeof(CommandClose)) == 0, "structures do not match"); + rtaCommand_Destroy(&command); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_GetCreateStack) +{ + TransportConfig *params = Test_createParams("/tmp/fwd"); + CCNxStackConfig *stackConfig = transportConfig_GetProtocolStackConfig(params); + + CommandCreateStack commandCreateStack = { .stack_id = 9, .params = ccnxStackConfig_GetJson(stackConfig) }; + CommandCreateStack test; + + RtaCommand *command = rtaCommand_CreateStack(commandCreateStack); + rtaCommand_GetCreateStack(command, &test); + + assertTrue(test.stack_id == 9, "Wrong stack id, expected %d got %d", 9, test.stack_id); + + char *truth_params = ccnxJson_ToString(ccnxStackConfig_GetJson(stackConfig)); + char *test_params = ccnxJson_ToString(test.params); + assertTrue(strcasecmp(truth_params, test_params) == 0, "params strings did not match"); + + parcMemory_Deallocate((void **) &truth_params); + parcMemory_Deallocate((void **) &test_params); + rtaCommand_Destroy(&command); + transportConfig_Destroy(¶ms); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_GetDestroyStack) +{ + CommandDestroyStack commandDestroyStack = { .stack_id = 133434 }; + CommandDestroyStack test; + + RtaCommand *command = rtaCommand_DestroyStack(commandDestroyStack); + rtaCommand_GetDestroyStack(command, &test); + assertTrue(memcmp(&commandDestroyStack, &test, sizeof(CommandDestroyStack)) == 0, "structures do not match"); + rtaCommand_Destroy(&command); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_GetOpen) +{ + TransportConfig *params = Test_createParams("/tmp/fwd"); + CCNxStackConfig *stackConfig = transportConfig_GetProtocolStackConfig(params); + + CommandOpen commandOpen = { .stack_id = 9, .api_fd = 77, .transport_fd = 102, .params = ccnxStackConfig_GetJson(stackConfig) }; + CommandOpen test; + RtaCommand *command = rtaCommand_Open(commandOpen); + rtaCommand_GetOpen(command, &test); + + assertTrue(test.stack_id == 9, "Wrong stack id, expected %d got %d", 9, test.stack_id); + assertTrue(test.api_fd == 77, "Wrong api_fd, expected %d got %d", 77, test.api_fd); + assertTrue(test.transport_fd == 102, "Wrong transport_fd, expected %d got %d", 102, test.transport_fd); + + char *truth_params = ccnxJson_ToString(ccnxStackConfig_GetJson(stackConfig)); + char *test_params = ccnxJson_ToString(test.params); + assertTrue(strcasecmp(truth_params, test_params) == 0, "params strings did not match"); + + parcMemory_Deallocate((void **) &truth_params); + parcMemory_Deallocate((void **) &test_params); + rtaCommand_Destroy(&command); + transportConfig_Destroy(¶ms); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_GetType) +{ + CommandDestroyStack commandDestroyStack = { .stack_id = 2 }; + RtaCommand *command = rtaCommand_DestroyStack(commandDestroyStack); + assertTrue(rtaCommand_GetType(command) == RTA_COMMAND_DESTROYSTACK, "Type not RTA_COMMAND_DESTROYSTACK"); + rtaCommand_Destroy(&command); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_Open) +{ + TransportConfig *params = Test_createParams("/tmp/fwd"); + CCNxStackConfig *stackConfig = transportConfig_GetProtocolStackConfig(params); + + char *params_str = ccnxJson_ToString(ccnxStackConfig_GetJson(stackConfig)); + + CommandOpen commandOpen = { .stack_id = 9, .api_fd = 77, .transport_fd = 102, .params = ccnxStackConfig_GetJson(stackConfig) }; + char *truth = "{ \"RTA\" : { \"OPEN\" : [9, 77, 102], \"PARAMS\" : %s } }"; + char buffer[1024]; + + sprintf(buffer, truth, params_str); + + PARCJSON *truth_json = parcJSON_ParseString(buffer); + char *truth_formatted = ccnxJson_ToString(truth_json); + char *test; + + RtaCommand *command = rtaCommand_Open(commandOpen); + + assertTrue(command->type == RTA_COMMAND_OPEN, "Type not RTA_COMMAND_OPEN"); + + test = ccnxJson_ToString(command->command); + assertTrue(strcasecmp(test, truth_formatted) == 0, + "JSON does not match\nexpected: %s\ngot: %s\n", + truth_formatted, test); + + rtaCommand_Destroy(&command); + parcJSON_Release(&truth_json); + transportConfig_Destroy(¶ms); + parcMemory_Deallocate((void **) &test); + parcMemory_Deallocate((void **) &truth_formatted); + parcMemory_Deallocate((void **) ¶ms_str); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_Read_Write) +{ + int fds[2]; + pipe(fds); + + CommandDestroyStack commandDestroyStack = { .stack_id = 2 }; + RtaCommand *command = rtaCommand_DestroyStack(commandDestroyStack); + rtaCommand_Write(command, fds[1]); + RtaCommand *test_command = rtaCommand_Read(fds[0]); + CommandDestroyStack test; + rtaCommand_GetDestroyStack(test_command, &test); + assertTrue(memcmp(&commandDestroyStack, &test, sizeof(commandDestroyStack)) == 0, "memcmp did not match"); + + rtaCommand_Destroy(&command); + rtaCommand_Destroy(&test_command); + close(fds[1]); + close(fds[0]); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_Shutdown) +{ + char *truth = "{ \"RTA\" : { \"SHUTDOWN\" : 1 } }"; + PARCJSON *truth_json = parcJSON_ParseString(truth); + char *truth_formatted = ccnxJson_ToString(truth_json); + char *test; + + RtaCommand *command = rtaCommand_Shutdown(); + + assertTrue(command->type == RTA_COMMAND_SHUTDOWN, "Type not RTA_COMMAND_SHUTDOWN"); + + test = ccnxJson_ToString(command->command); + assertTrue(strcasecmp(test, truth_formatted) == 0, + "JSON does not match\nexpected: %s\ngot: %s\n", + truth_formatted, test); + + rtaCommand_Destroy(&command); + parcJSON_Release(&truth_json); + parcMemory_Deallocate((void **) &test); + parcMemory_Deallocate((void **) &truth_formatted); +} + +LONGBOW_TEST_CASE(Global, rtaCommand_CreateTransmitStatistics) +{ + char *truth = "{ \"RTA\" : { \"TransmitStatistics\" : { \"fileName\": \"/tmp/foo\", \"timeval\" : { " + "\"seconds\" : 1, \"microseconds\": 2 } } } }\n"; + + PARCJSON *truth_json = parcJSON_ParseString(truth); + char *truth_formatted = ccnxJson_ToString(truth_json); + + CommandTransmitStatistics transmitStatistics = { + .timeval = { .tv_sec = 1, .tv_usec = 2 }, + .fileName = "/tmp/foo" + }; + + RtaCommand *command = CommandTransmitStatistics_ToRtaCommand(transmitStatistics); + + assertTrue(command->type == RTA_COMMAND_TRANSMIT_STATISTICS, + "Expected RTA_COMMAND_TRANSMIT_STATISTICS, actual %d", command->type); + + char *test = ccnxJson_ToString(command->command); + assertTrue(strcasecmp(test, truth_formatted) == 0, + "JSON does not match\nexpected: %s\ngot: %s\n", + truth_formatted, test); + + rtaCommand_Destroy(&command); + parcJSON_Release(&truth_json); + parcMemory_Deallocate((void **) &test); + parcMemory_Deallocate((void **) &truth_formatted); +} + +LONGBOW_TEST_CASE(Global, CommandTransmitStatistics_FromJSON) +{ + CommandTransmitStatistics transmitStatistics = { + .timeval = { .tv_sec = 1, .tv_usec = 2 }, + .fileName = "/tmp/foo" + }; + RtaCommand *command = CommandTransmitStatistics_ToRtaCommand(transmitStatistics); + + CommandTransmitStatistics actual; + CommandTransmitStatistics_FromRtaCommand(command, &actual); + + assertTrue(transmitStatistics.timeval.tv_sec == actual.timeval.tv_sec, "tv_sec failed to be equal"); + assertTrue(transmitStatistics.timeval.tv_usec == actual.timeval.tv_usec, "tv_usec failed to be equal"); + assertTrue(strcmp(transmitStatistics.fileName, actual.fileName) == 0, "fileName failed to be equal"); + + rtaCommand_Destroy(&command); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Commands); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} diff --git a/libccnx-transport-rta/ccnx/transport/transport_rta/test/test_rta_Transport.c b/libccnx-transport-rta/ccnx/transport/transport_rta/test/test_rta_Transport.c new file mode 100644 index 00000000..9f6f0611 --- /dev/null +++ b/libccnx-transport-rta/ccnx/transport/transport_rta/test/test_rta_Transport.c @@ -0,0 +1,701 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// Include the file(s) containing the functions to be tested. +// This permits internal static functions to be visible to this Test Framework. + +#include "../rta_Transport.c" +#include <ccnx/transport/transport_rta/config/config_All.h> +#include <ccnx/transport/transport_rta/core/rta_Framework_private.h> +#include <ccnx/transport/transport_rta/components/component_Testing.h> + +#include <ccnx/common/ccnx_WireFormatMessage.h> + +#include <parc/algol/parc_SafeMemory.h> +#include <ccnx/transport/test_tools/traffic_tools.h> + +#include <LongBow/unit-test.h> + +typedef struct test_data { + RTATransport *transport; + CCNxMetaMessage *msg; +} TestData; + +static TestData * +_commonSetup(void) +{ + TestData *data = parcMemory_AllocateAndClear(sizeof(TestData)); + assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData)); + data->transport = rtaTransport_Create(); + return data; +} + +static void +_commonTeardown(TestData *data) +{ + rtaTransport_Destroy(&data->transport); + if (data->msg) { + ccnxMetaMessage_Release(&data->msg); + } + + parcMemory_Deallocate((void **) &data); +} + +static CCNxTransportConfig * +createSimpleConfig(TestData *data) +{ + // API connector -> Testing Lower component + + CCNxStackConfig *stackConfig = + testingLower_ProtocolStackConfig(apiConnector_ProtocolStackConfig(ccnxStackConfig_Create())); + + CCNxConnectionConfig *connConfig = + testingLower_ConnectionConfig( + tlvCodec_ConnectionConfig( + apiConnector_ConnectionConfig( + ccnxConnectionConfig_Create()))); + + protocolStack_ComponentsConfigArgs(stackConfig, apiConnector_GetName(), testingLower_GetName(), NULL); + + CCNxTransportConfig *result = ccnxTransportConfig_Create(stackConfig, connConfig); + ccnxStackConfig_Release(&stackConfig); + return result; +} + +/** + * Peek inside the RTA framework's connection table + * + * We will look inside the RTA framework's thread to find a connection by the API_FD. + * + * @param [in] data The test data, holding the transport + * @param [in] api_fd The API FD to lookup + * @param [in] usec_timeout How long to busy wait looking in the connection table (micro seconds) + * + * @return NULL It was not in the table after the timeout period + * @return non-null The connection corresponding to api_fd + * + * Example: + * @code + * <#example#> + * @endcode + */ +static RtaConnection * +lookupRtaConnectionInsideFramework(TestData *data, int api_fd, unsigned usec_timeout) +{ + // busy loop looking for connection to give RTA thread time to process it. + // Remember, we're operating in the "API" thread when issuing these commands. + struct timeval t0; + long timer_usec = 0; + gettimeofday(&t0, NULL); + bool timeout = false; + RtaConnection *conn = NULL; + while (conn == NULL && !timeout) { + usleep(500); + conn = rtaConnectionTable_GetByApiFd(data->transport->framework->connectionTable, api_fd); + struct timeval t1; + gettimeofday(&t1, NULL); + timersub(&t1, &t0, &t1); + timer_usec = t1.tv_sec * 1000000 + t1.tv_usec; + timeout = timer_usec > usec_timeout ? true : false; + } + + if (conn) { + printf("Found connection %p after %.6f seconds\n", (void *) conn, timer_usec * 1E-6); + } + + return conn; +} + +/** + * Wait for a connection to go away + * + * <#Paragraphs Of Explanation#> + * + * @param [<#in out in,out#>] <#name#> <#description#> + * + * @return <#value#> <#explanation#> + * + * Example: + * @code + * <#example#> + * @endcode + */ +static bool +lookupNullRtaConnectionInsideFramework(TestData *data, int api_fd, unsigned usec_timeout) +{ + // busy loop looking for connection to give RTA thread time to process it. + // Remember, we're operating in the "API" thread when issuing these commands. + struct timeval t0; + long timer_usec = 0; + gettimeofday(&t0, NULL); + bool timeout = false; + + // initialize to non-null + RtaConnection *conn = (void *) 1; + while (conn != NULL && !timeout) { + usleep(500); + conn = rtaConnectionTable_GetByApiFd(data->transport->framework->connectionTable, api_fd); + struct timeval t1; + gettimeofday(&t1, NULL); + timersub(&t1, &t0, &t1); + timer_usec = t1.tv_sec * 1000000 + t1.tv_usec; + timeout = timer_usec > usec_timeout ? true : false; + } + + if (conn == NULL) { + printf("Found no connection %p after %.6f seconds\n", (void *) conn, timer_usec * 1E-6); + } + + // if its null, return true + return (conn == NULL); +} + + +// ================================================================================== +// Runner + +LONGBOW_TEST_RUNNER(rta_Transport) +{ + // The following Test Fixtures will run their corresponding Test Cases. + // Test Fixtures are run in the order specified, but all tests should be idempotent. + // Never rely on the execution order of tests or share state between them. + LONGBOW_RUN_TEST_FIXTURE(Global); + LONGBOW_RUN_TEST_FIXTURE(Local); +} + +// The Test Runner calls this function once before any Test Fixtures are run. +LONGBOW_TEST_RUNNER_SETUP(rta_Transport) +{ + parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory); + return LONGBOW_STATUS_SUCCEEDED; +} + +// The Test Runner calls this function once after all the Test Fixtures are run. +LONGBOW_TEST_RUNNER_TEARDOWN(rta_Transport) +{ + return LONGBOW_STATUS_SUCCEEDED; +} + +// ================================================================================== +// Global + +LONGBOW_TEST_FIXTURE(Global) +{ + // These are still static functions, but they are the function pointers used + // in the transport function structure. They comprise the public API. + LONGBOW_RUN_TEST_CASE(Global, rtaTransport_Close); + LONGBOW_RUN_TEST_CASE(Global, rtaTransport_Create_Destroy); + LONGBOW_RUN_TEST_CASE(Global, rtaTransport_Open); + LONGBOW_RUN_TEST_CASE(Global, rtaTransport_PassCommand); + + LONGBOW_RUN_TEST_CASE(Global, rtaTransport_Recv_OK); + LONGBOW_RUN_TEST_CASE(Global, rtaTransport_Recv_WouldBlock); + + LONGBOW_RUN_TEST_CASE(Global, rtaTransport_Send_OK); + LONGBOW_RUN_TEST_CASE(Global, rtaTransport_Send_WouldBlock); + +// LONGBOW_RUN_TEST_CASE(Global, unrecoverable); +} + +LONGBOW_TEST_FIXTURE_SETUP(Global) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Global) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Global, rtaTransport_Close) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTransportConfig *config = createSimpleConfig(data); + + int api_fd = rtaTransport_Open(data->transport, config); + + RtaConnection *conn = lookupRtaConnectionInsideFramework(data, api_fd, 1E+6); + assertNotNull(conn, "Could not find connection"); + + rtaTransport_Close(data->transport, api_fd); + + // now wait until it's gone + bool gone = lookupNullRtaConnectionInsideFramework(data, api_fd, 1E+6); + assertTrue(gone, "Did not remove connection after 1 second timeout"); + + ccnxTransportConfig_Destroy(&config); +} + +LONGBOW_TEST_CASE(Global, rtaTransport_Create_Destroy) +{ + RTATransport *transport = rtaTransport_Create(); + assertNotNull(transport, "rtaTransport_Create() returns NULL"); + + rtaTransport_Destroy(&transport); + assertNull(transport, "rtaTransport_Destroy did not null paramter"); +} + +LONGBOW_TEST_CASE(Global, rtaTransport_Open) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTransportConfig *config = createSimpleConfig(data); + + int api_fd = rtaTransport_Open(data->transport, config); + + RtaConnection *conn = lookupRtaConnectionInsideFramework(data, api_fd, 1E+6); + assertNotNull(conn, "Could not find connection"); + + ccnxTransportConfig_Destroy(&config); +} + +/** + * PassCommand sends a user RTA Command over the command channel. + * This test will intercept the transport side of the command channel so + * we can easily verify the command went through. + */ +LONGBOW_TEST_CASE(Global, rtaTransport_PassCommand) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + PARCRingBuffer1x1 *previousRingBuffer = data->transport->commandRingBuffer; + PARCNotifier *previousNotifier = data->transport->commandNotifier; + + PARCRingBuffer1x1 *testRingBuffer = parcRingBuffer1x1_Create(32, NULL); + PARCNotifier *testNotifier = parcNotifier_Create(); + + + // Insert our new socket pair so we can intercept the commands + // No acquire here because we will be resetting them and destroying all in this scope + data->transport->commandRingBuffer = testRingBuffer; + data->transport->commandNotifier = testNotifier; + + // Create a simple command to send + RtaCommand *command = rtaCommand_CreateShutdownFramework(); + rtaTransport_PassCommand(data->transport, command); + rtaCommand_Release(&command); + + RtaCommand *testCommand = rtaCommand_Read(testRingBuffer); + assertNotNull(testCommand, "Got null command from the ring buffer."); + assertTrue(rtaCommand_IsShutdownFramework(testCommand), "Command not a shutdown framework"); + + // All's well + + rtaCommand_Release(&testCommand); + + // now restore the sockets so things close up nicely + data->transport->commandRingBuffer = previousRingBuffer; + data->transport->commandNotifier = previousNotifier; + + parcRingBuffer1x1_Release(&testRingBuffer); + parcNotifier_Release(&testNotifier); +} + +LONGBOW_TEST_CASE(Global, rtaTransport_Recv_OK) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + int api_fd, transport_fd; + + _RTASocketPair pair = _rtaTransport_CreateSocketPair(data->transport, 128 * 1024); + api_fd = pair.up; + transport_fd = pair.down; + + // Set non-blocking flag + int flags = fcntl(api_fd, F_GETFL, NULL); + assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno); + int failure = fcntl(api_fd, F_SETFL, flags | O_NONBLOCK); + assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno); + + char *buffer = "born free, as free as the wind blows"; + ssize_t nwritten = write(transport_fd, &buffer, sizeof(&buffer)); + assertTrue(nwritten == sizeof(&buffer), "Wrong write size, expected %zu got %zd", sizeof(&buffer), nwritten); + + CCNxMetaMessage *msg = NULL; + TransportIOStatus result = rtaTransport_Recv(data->transport, api_fd, &msg, CCNxStackTimeout_Never); + assertTrue(result != TransportIOStatus_Error, "Failed to read a good socket"); + assertTrue((void *) msg == (void *) buffer, "Read wrong pointer, got %p expected %p", (void *) msg, (void *) buffer); + + close(api_fd); + close(transport_fd); +} + +LONGBOW_TEST_CASE(Global, rtaTransport_Recv_WouldBlock) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + int api_fd, transport_fd; + + _RTASocketPair pair = _rtaTransport_CreateSocketPair(data->transport, 128 * 1024); + api_fd = pair.up; + transport_fd = pair.down; + + // Set non-blocking flag + int flags = fcntl(api_fd, F_GETFL, NULL); + assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno); + int failure = fcntl(api_fd, F_SETFL, flags | O_NONBLOCK); + assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno); + + // Don't write anything + + CCNxMetaMessage *msg = NULL; + TransportIOStatus result = rtaTransport_Recv(data->transport, api_fd, &msg, CCNxStackTimeout_Immediate); + assertTrue(result == TransportIOStatus_Timeout, "Should have returned failure due to blocking"); + + close(api_fd); + close(transport_fd); +} + + +/** + * This function will receive what the API Connector sends down the stack + */ +static void +mockDowncallRead(PARCEventQueue *queue, PARCEventType type, void *stack) +{ + TransportMessage *tm = rtaComponent_GetMessage(queue); + assertNotNull(tm, "got null transport message"); + + CCNxTlvDictionary *dictionary = transportMessage_GetDictionary(tm); + CCNxCodecNetworkBufferIoVec *vec = ccnxWireFormatMessage_GetIoVec(dictionary); + const struct iovec *iov = ccnxCodecNetworkBufferIoVec_GetArray(vec); + + // we encapsualted a pointer to this counter inside the wire format + unsigned *downcallReadCountPtr = iov[0].iov_base; + (*downcallReadCountPtr)++; + + transportMessage_Destroy(&tm); +} + +CCNxCodecNetworkBufferMemoryBlockFunctions memfunc = { + .allocator = NULL, + .deallocator = NULL +}; + +/** + * This test does not actually need to receive the message in TestingLower. It could have passed + * any socket pair to rtaTransport_Send and inspected the result immediately. + */ +LONGBOW_TEST_CASE(Global, rtaTransport_Send_OK) +{ + testing_null_ops.downcallRead = mockDowncallRead; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTransportConfig *config = createSimpleConfig(data); + + unsigned downcallReadCount = 0; + + CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&memfunc, NULL, sizeof(downcallReadCount), (uint8_t *) &downcallReadCount); + CCNxCodecNetworkBufferIoVec *vec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff); + CCNxTlvDictionary *wire = ccnxWireFormatMessage_FromInterestPacketTypeIoVec(CCNxTlvDictionary_SchemaVersion_V1, vec); + + int api_fd = rtaTransport_Open(data->transport, config); + + CCNxMetaMessage *msg = ccnxMetaMessage_Acquire(wire); + bool success = rtaTransport_Send(data->transport, api_fd, msg, CCNxStackTimeout_Never); + assertTrue(success, "Got error writing to api_fd %d\n", api_fd); + ccnxMetaMessage_Release(&msg); + + // now spin on it + unsigned maxTries = 2000; // about 1 second + while ((downcallReadCount == 0) && (maxTries > 0)) { + maxTries--; + usleep(500); + } + + printf("Read message after %d tries\n", 2000 - maxTries); + + ccnxTlvDictionary_Release(&wire); + ccnxCodecNetworkBufferIoVec_Release(&vec); + ccnxCodecNetworkBuffer_Release(&netbuff); + + ccnxTransportConfig_Destroy(&config); +} + +/** + * Fill up the socket with junk, then make sure it would blocks + */ +LONGBOW_TEST_CASE(Global, rtaTransport_Send_WouldBlock) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + int api_fd, transport_fd; + + _RTASocketPair pair = _rtaTransport_CreateSocketPair(data->transport, 128 * 1024); + api_fd = pair.up; + transport_fd = pair.down; + + // Set non-blocking flag + int flags = fcntl(api_fd, F_GETFL, NULL); + assertTrue(flags != -1, "fcntl failed to obtain file descriptor flags (%d)\n", errno); + int failure = fcntl(api_fd, F_SETFL, flags | O_NONBLOCK); + assertFalse(failure, "fcntl failed to set file descriptor flags (%d)\n", errno); + + // write junk until it would block + char buffer[1024]; + while (write(api_fd, buffer, 1024) > 0) { + ; + } + + assertTrue(errno == EWOULDBLOCK, "wrote until it would block, but got some other error: (%d) %s", errno, strerror(errno)); + + // now call the function to test and make sure it does the right thing + // if it would block + CCNxTlvDictionary *interest = trafficTools_CreateDictionaryInterest(); + CCNxMetaMessage *msg = ccnxMetaMessage_CreateFromInterest(interest); + + bool success = rtaTransport_Send(data->transport, api_fd, msg, CCNxStackTimeout_Immediate); + printf("success %d, errno %d expected %d\n", success, errno, EWOULDBLOCK); + + assertFalse(success, "Send did not return a failure, even though it would have blocked"); + assertTrue(errno == EWOULDBLOCK, "wrote until it would block, but got some other error: (%d) %s", errno, strerror(errno)); + + ccnxMetaMessage_Release(&msg); + ccnxTlvDictionary_Release(&interest); + + close(api_fd); + close(transport_fd); +} + +/** + * Pass it an invalid socket. This will cause a trap in the send code. + */ +LONGBOW_TEST_CASE_EXPECTS(Global, rtaTransport_Send_Error, .event = &LongBowTrapUnrecoverableState) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTlvDictionary *interest = trafficTools_CreateDictionaryInterest(); + data->msg = ccnxMetaMessage_Acquire(interest); + ccnxTlvDictionary_Release(&interest); + + rtaTransport_Send(data->transport, 999, data->msg, CCNxStackTimeout_Immediate); +} + +LONGBOW_TEST_CASE_EXPECTS(Global, unrecoverable, .event = &LongBowTrapUnrecoverableState) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTlvDictionary *interest = trafficTools_CreateDictionaryInterest(); + data->msg = ccnxMetaMessage_CreateFromInterest(interest); + ccnxTlvDictionary_Release(&interest); + + rtaTransport_Send(NULL, 999, data->msg, CCNxStackTimeout_Immediate); + + ccnxMetaMessage_Release(&(data->msg)); +} + +// ================================================================================== +// Local + +LONGBOW_TEST_FIXTURE(Local) +{ + LONGBOW_RUN_TEST_CASE(Local, _rtaTransport_AddStack); + LONGBOW_RUN_TEST_CASE(Local, _rtaTransport_GetStack); + LONGBOW_RUN_TEST_CASE(Local, _rtaTransport_GetStack_Missing); + + LONGBOW_RUN_TEST_CASE(Local, _rtaTransport_CreateSocketPair); + LONGBOW_RUN_TEST_CASE(Local, _rtaTransport_GetProtocolStackEntry_Exists); + LONGBOW_RUN_TEST_CASE(Local, _rtaTransport_GetProtocolStackEntry_NotExists); + LONGBOW_RUN_TEST_CASE(Local, _rtaTransport_AddProtocolStackEntry); + LONGBOW_RUN_TEST_CASE(Local, _rtaTransport_CreateConnection); +} + +LONGBOW_TEST_FIXTURE_SETUP(Local) +{ + longBowTestCase_SetClipBoardData(testCase, _commonSetup()); + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_FIXTURE_TEARDOWN(Local) +{ + _commonTeardown(longBowTestCase_GetClipBoardData(testCase)); + + uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO); + if (outstandingAllocations != 0) { + printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations); + return LONGBOW_STATUS_MEMORYLEAK; + } + return LONGBOW_STATUS_SUCCEEDED; +} + +LONGBOW_TEST_CASE(Local, _rtaTransport_CreateSocketPair) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + int a, b; + + _RTASocketPair pair = _rtaTransport_CreateSocketPair(data->transport, 128 * 1024); + a = pair.up; + b = pair.down; + assertFalse(a < 0, "socket a is error: %d", a); + assertFalse(b < 0, "socket b is error: %d", b); + + ssize_t nwritten = write(a, &a, sizeof(a)); + assertTrue(nwritten == sizeof(a), "Wrong write size, expected %zu got %zd", sizeof(a), nwritten); + + int test; + ssize_t nread = read(b, &test, sizeof(test)); + assertTrue(nread == sizeof(test), "Wrong read size, expected %zu got %zd", sizeof(test), nread); + + assertTrue(test == a, "read wrong value, got %d wrote %d", test, a); + + close(a); + close(b); +} + + +LONGBOW_TEST_CASE(Local, _rtaTransport_GetProtocolStackEntry_Exists) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTransportConfig *config = createSimpleConfig(data); + +// uint64_t hash = ccnxStackConfig_HashCode(ccnxTransportConfig_GetStackConfig(config)); + + _StackEntry *truth = _rtaTransport_AddStack(data->transport, ccnxTransportConfig_GetStackConfig(config)); + + _StackEntry *test = _rtaTransport_GetProtocolStackEntry(data->transport, config); + + assertTrue(test == truth, "Wrong pointer, got %p expected %p", (void *) test, (void *) truth); + + ccnxTransportConfig_Destroy(&config); +} + +LONGBOW_TEST_CASE(Local, _rtaTransport_GetProtocolStackEntry_NotExists) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTransportConfig *config = createSimpleConfig(data); + + _rtaTransport_AddStack(data->transport, ccnxTransportConfig_GetStackConfig(config)); + + // Now create the missing one to lookup + // this one will have 2x api connectors listed + CCNxStackConfig *missingStackConfig = + apiConnector_ProtocolStackConfig(apiConnector_ProtocolStackConfig(ccnxStackConfig_Create())); + CCNxConnectionConfig *missingConnConfig = apiConnector_ConnectionConfig(ccnxConnectionConfig_Create()); + + CCNxTransportConfig *missingConfig = ccnxTransportConfig_Create(missingStackConfig, missingConnConfig); + ccnxStackConfig_Release(&missingStackConfig); + + _StackEntry *test = _rtaTransport_GetProtocolStackEntry(data->transport, missingConfig); + + assertNull(test, "Wrong pointer, got %p expected %p", (void *) test, (void *) NULL); + ccnxTransportConfig_Destroy(&missingConfig); + ccnxTransportConfig_Destroy(&config); +} + +LONGBOW_TEST_CASE(Local, _rtaTransport_AddProtocolStackEntry) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTransportConfig *config = createSimpleConfig(data); + + _StackEntry *entry = _rtaTransport_AddProtocolStackEntry(data->transport, config); + assertNotNull(entry, "Got null entry from _rtaTransport_AddProtocolStackEntry"); + + ccnxTransportConfig_Destroy(&config); +} + +LONGBOW_TEST_CASE(Local, _rtaTransport_CreateConnection) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxTransportConfig *config = createSimpleConfig(data); + + _StackEntry *entry = _rtaTransport_AddProtocolStackEntry(data->transport, config); + + _RTASocketPair pair = _rtaTransport_CreateSocketPair(data->transport, 128 * 1024); + + _rtaTransport_CreateConnection(data->transport, config, entry, pair); + + // wait up to 1 second + RtaConnection *conn = lookupRtaConnectionInsideFramework(data, pair.up, 1E+6); + assertNotNull(conn, "Could not find connection in connection table, timeout at %.6f seconds", 1.0); + + ccnxTransportConfig_Destroy(&config); +} + +LONGBOW_TEST_CASE(Local, _rtaTransport_AddStack) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + _StackEntry *entry = _rtaTransport_AddStack(data->transport, stackConfig); + + uint64_t hash = ccnxStackConfig_HashCode(stackConfig); + _StackEntry *test = _rtaTransport_GetStack(data->transport, hash); + assertTrue(test == entry, "Wrong pointer, got %p expected %p", (void *) test, (void *) entry); + + ccnxStackConfig_Release(&stackConfig); +} + +LONGBOW_TEST_CASE(Local, _rtaTransport_GetStack) +{ + struct test_vector { + uint64_t hash; + int stackid; + _StackEntry *entry; + } vector[] = { + { .hash = 20, .stackid = 30, .entry = NULL }, + { .hash = 10, .stackid = 77, .entry = NULL }, + { .hash = 990, .stackid = 31, .entry = NULL }, + { .hash = 0, .stackid = 0, .entry = NULL }, + }; + + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + + char key[10]; + for (int i = 0; vector[i].hash != 0; i++) { + sprintf(key, "key%d", i); + PARCJSONValue *json = parcJSONValue_CreateFromNULL(); + ccnxStackConfig_Add(stackConfig, key, json); + parcJSONValue_Release(&json); + vector[i].hash = ccnxStackConfig_HashCode(stackConfig); + vector[i].entry = _rtaTransport_AddStack(data->transport, stackConfig); + } + ccnxStackConfig_Release(&stackConfig); + + // now look them up + for (int i = 0; vector[i].hash != 0; i++) { + _StackEntry *test = _rtaTransport_GetStack(data->transport, vector[i].hash); + assertTrue(test == vector[i].entry, "Wrong pointer, got %p expected %p", (void *) test, (void *) vector[i].entry); + } +} + +LONGBOW_TEST_CASE(Local, _rtaTransport_GetStack_Missing) +{ + TestData *data = longBowTestCase_GetClipBoardData(testCase); + + CCNxStackConfig *stackConfig = ccnxStackConfig_Create(); + _rtaTransport_AddStack(data->transport, stackConfig); + + PARCJSONValue *json = parcJSONValue_CreateFromNULL(); + ccnxStackConfig_Add(stackConfig, "someKey", json); + parcJSONValue_Release(&json); + + _StackEntry *test = _rtaTransport_GetStack(data->transport, ccnxStackConfig_HashCode(stackConfig)); + + ccnxStackConfig_Release(&stackConfig); + assertNull(test, "Wrong pointer, got %p expected %p", (void *) test, NULL); +} + +int +main(int argc, char *argv[]) +{ + LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(rta_Transport); + int exitStatus = longBowMain(argc, argv, testRunner, NULL); + longBowTestRunner_Destroy(&testRunner); + exit(exitStatus); +} |