aboutsummaryrefslogtreecommitdiffstats
path: root/libccnx-transport-rta/ccnx/transport
diff options
context:
space:
mode:
authorLuca Muscariello <lumuscar+fdio@cisco.com>2017-02-23 20:44:26 +0100
committerLuca Muscariello <lumuscar+fdio@cisco.com>2017-02-23 19:51:14 +0000
commitd18ae43123fcd7604d1c36a1ec8450dbe6071824 (patch)
tree2d49fc3aabd0f2607251c854565648d47b56b2e9 /libccnx-transport-rta/ccnx/transport
parent9b30fc10fb1cbebe651e5a107e8ca5b24de54675 (diff)
Initial commit: ccnxlibs.
Change-Id: I1b376527a7dd01a6b9e083a6cb646955902f45c0 Signed-off-by: Luca Muscariello <lumuscar+fdio@cisco.com>
Diffstat (limited to 'libccnx-transport-rta/ccnx/transport')
-rw-r--r--libccnx-transport-rta/ccnx/transport/.gitignore35
-rw-r--r--libccnx-transport-rta/ccnx/transport/CMakeLists.txt216
-rw-r--r--libccnx-transport-rta/ccnx/transport/README24
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/ccnx_ConnectionConfig.c123
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/ccnx_ConnectionConfig.h190
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/ccnx_StackConfig.c160
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/ccnx_StackConfig.h327
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/ccnx_TransportConfig.c120
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/ccnx_TransportConfig.h229
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/test/.gitignore4
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/test/CMakeLists.txt16
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_ConnectionConfig.c183
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_StackConfig.c226
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_TransportConfig.c226
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/test/test_transport.c136
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/test/test_transport_Message.c15
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/test/test_transport_MetaMessage.c283
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/test/test_transport_Stack.c191
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/transport.c123
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/transport.h262
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/transport_Message.c198
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/transport_Message.h175
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/transport_MetaMessage.c193
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/transport_MetaMessage.h515
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/transport_Stack.c137
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/transport_Stack.h354
-rw-r--r--libccnx-transport-rta/ccnx/transport/common/transport_private.h38
-rw-r--r--libccnx-transport-rta/ccnx/transport/librta_About.c44
-rw-r--r--libccnx-transport-rta/ccnx/transport/librta_About.h54
-rw-r--r--libccnx-transport-rta/ccnx/transport/test_tools/bent_pipe.c850
-rw-r--r--libccnx-transport-rta/ccnx/transport/test_tools/bent_pipe.h148
-rw-r--r--libccnx-transport-rta/ccnx/transport/test_tools/ethersend.c211
-rw-r--r--libccnx-transport-rta/ccnx/transport/test_tools/pktgen.c195
-rw-r--r--libccnx-transport-rta/ccnx/transport/test_tools/test/.gitignore1
-rw-r--r--libccnx-transport-rta/ccnx/transport/test_tools/test/test_bent_pipe.c397
-rw-r--r--libccnx-transport-rta/ccnx/transport/test_tools/traffic_tools.c292
-rw-r--r--libccnx-transport-rta/ccnx/transport/test_tools/traffic_tools.h284
-rw-r--r--libccnx-transport-rta/ccnx/transport/test_tools/write_packets.c84
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_Command.c365
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_Command.h619
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCloseConnection.c61
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCloseConnection.h145
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCreateProtocolStack.c113
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandCreateProtocolStack.h274
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandDestroyProtocolStack.c59
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandDestroyProtocolStack.h141
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.c96
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandOpenConnection.h219
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.c81
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/rta_CommandTransmitStatistics.h125
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/.gitignore7
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/CMakeLists.txt18
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_Command.c475
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandCloseConnection.c125
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandCreateProtocolStack.c215
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandDestroyProtocolStack.c125
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandOpenConnection.c199
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/commands/test/test_rta_CommandTransmitStatistics.c170
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/component_Vegas.c673
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/test/test_component_Vegas.c696
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/test/test_vegas_Session.c672
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/vegas_Session.c1379
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/Flowcontrol_Vegas/vegas_private.h222
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/codec_Signing.c80
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/codec_Signing.h46
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Codec.h28
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Codec_Tlv.c319
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Flowcontrol.h29
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Testing.c45
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/component_Testing.h41
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/test/CMakeLists.txt16
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_codec_Signing.c61
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Codec_Tlv.c276
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Codec_Tlv_Hmac.c204
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/test/test_component_Testing.c82
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/components/test/testrig_MockFramework.c99
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_All.h45
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ApiConnector.c52
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ApiConnector.h89
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Ccnb.xc31
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Tlv.c62
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Codec_Tlv.h92
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_CryptoCache.h41
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_FlowControl_Vegas.c50
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_FlowControl_Vegas.h92
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Local.c72
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Local.h102
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Metis.c81
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Forwarder_Metis.h118
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_InMemoryVerifier.c52
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_InMemoryVerifier.h81
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ProtocolStack.c121
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_ProtocolStack.h114
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_PublicKeySigner.c106
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_PublicKeySigner.h112
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Signer.c65
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_Signer.h51
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_SymmetricKeySigner.c95
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_SymmetricKeySigner.h103
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_TestingComponent.c73
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/config_TestingComponent.h137
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/CMakeLists.txt22
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_ApiConnector.c141
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Codec_Tlv.c154
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_FlowControl_Vegas.c141
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Forwarder_Local.c152
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Forwarder_Metis.c154
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_InMemoryVerifier.c122
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_ProtocolStack.c167
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_PublicKeySigner.c133
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_Signer.c141
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_SymmetricKeySigner.c135
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/test_config_TestingComponent.c186
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/config/test/testrig_RtaConfigCommon.c66
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Api.c264
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Api.h22
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder.h30
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder_Local.c552
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/connector_Forwarder_Metis.c1712
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/rta_ApiConnection.c634
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/rta_ApiConnection.h83
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/CMakeLists.txt16
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Api.c61
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Local.c249
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_connector_Forwarder_Metis.c1350
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/connectors/test/test_rta_ApiConnection.c278
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/components.h56
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta.h29
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Component.c127
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Component.h258
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentQueue.h30
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentStats.c124
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ComponentStats.h172
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Connection.c383
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Connection.h457
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ConnectionTable.c250
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ConnectionTable.h109
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework.c469
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework.h181
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Commands.c450
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Commands.h53
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.c204
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_NonThreaded.h79
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Services.c44
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Services.h125
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Threaded.c170
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_Threaded.h56
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Framework_private.h163
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Logger.c188
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_Logger.h227
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ProtocolStack.c786
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/rta_ProtocolStack.h379
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/.gitignore10
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/CMakeLists.txt23
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Component.c247
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ComponentStats.c189
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Connection.c82
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ConnectionTable.c309
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework.c298
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Commands.c449
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_NonThreaded.c83
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Services.c84
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Framework_Threaded.c83
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_Logger.c222
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_ProtocolStack.c81
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/core/test/test_rta_WebService.c301
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/rta_Transport.c543
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/rta_Transport.h101
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/test/.gitignore19
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/test/CMakeLists.txt13
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/test/README17
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/test/test_multi_connections.c424
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/test/test_rta_Commands.c381
-rw-r--r--libccnx-transport-rta/ccnx/transport/transport_rta/test/test_rta_Transport.c701
174 files changed, 35090 insertions, 0 deletions
diff --git a/libccnx-transport-rta/ccnx/transport/.gitignore b/libccnx-transport-rta/ccnx/transport/.gitignore
new file mode 100644
index 00000000..1f13e4f3
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/.gitignore
@@ -0,0 +1,35 @@
+*.lo
+*.la
+*.pc
+bin/
+lib/
+include/
+Libevent/test/
+Libevent/sample/
+Libevent/.deps/
+Libevent/.libs/
+Libevent/Makefile
+Libevent/config.h
+Libevent/config.log
+Libevent/config.status
+Libevent/libtool
+Libevent/stamp-h1
+
+transport_rta/tlv_1.0/test/test_tlv_DecoderParsers
+transport_rta/tlv_1.0/test/test_tlv_Encoder
+transport_rta/tlv_1.0/test/test_tlv_EncoderCodecs
+transport_rta/test/test_component_Codec_Tlv
+
+transport_rta/core/test/test_rta_ComponentStats
+transport_rta/connectors/test/test_rta_ApiConnection
+
+transport_rta/components/test/test_codec_Signing
+transport_rta/components/test/test_component_Codec_Null
+transport_rta/components/test/test_component_Flowcontrol_Null
+transport_rta/components/test/test_component_Testing
+transport_rta/components/test/test_component_Verifier_Enumerated
+transport_rta/components/test/test_component_Verifier_Locator
+transport_rta/components/test/test_component_Verifier_Null
+
+transport_rta/components/Flowcontrol_Vegas/test/test_component_Vegas
+transport_rta/components/Flowcontrol_Vegas/test/test_vegas_Session
diff --git a/libccnx-transport-rta/ccnx/transport/CMakeLists.txt b/libccnx-transport-rta/ccnx/transport/CMakeLists.txt
new file mode 100644
index 00000000..076b637a
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/CMakeLists.txt
@@ -0,0 +1,216 @@
+set(BASE_HDRS
+ librta_About.h
+ )
+
+set(COMMON_HDRS
+ common/transport.h
+ common/ccnx_TransportConfig.h
+ common/transport_Message.h
+ common/transport_MetaMessage.h
+ common/ccnx_StackConfig.h
+ common/ccnx_ConnectionConfig.h
+ )
+
+source_group(common FILES ${COMMON_HDRS})
+
+set(RTA_CORE_HDRS
+ transport_rta/rta_Transport.h
+ transport_rta/core/components.h
+ transport_rta/core/rta.h
+ transport_rta/core/rta_ComponentQueue.h
+ transport_rta/core/rta_ComponentStats.h
+ transport_rta/core/rta_Connection.h
+ transport_rta/core/rta_ConnectionTable.h
+ transport_rta/core/rta_Framework.h
+ transport_rta/core/rta_Framework_Commands.h
+ transport_rta/core/rta_Framework_NonThreaded.h
+ transport_rta/core/rta_Framework_Services.h
+ transport_rta/core/rta_Framework_Threaded.h
+ transport_rta/core/rta_Framework_private.h
+ transport_rta/core/rta_Logger.h
+ transport_rta/core/rta_ProtocolStack.h
+ test_tools/bent_pipe.h
+ test_tools/traffic_tools.h
+ )
+
+source_group(core FILES ${RTA_CORE_HDRS})
+
+set(TEST_TOOLS_HDRS
+ test_tools/bent_pipe.h
+ test_tools/traffic_tools.h
+ )
+
+set(RTA_COMMANDS_HDRS
+ transport_rta/commands/rta_CommandCloseConnection.h
+ transport_rta/commands/rta_CommandCreateProtocolStack.h
+ transport_rta/commands/rta_CommandDestroyProtocolStack.h
+ transport_rta/commands/rta_Command.h
+ transport_rta/commands/rta_CommandOpenConnection.h
+ transport_rta/commands/rta_CommandTransmitStatistics.h
+ )
+
+source_group(rta_commands FILES ${RTA_COMMANDS_HDRS})
+
+set(RTA_CONFIG_HDRS
+ transport_rta/config/config_ApiConnector.h
+ transport_rta/config/config_Codec_Tlv.h
+ transport_rta/config/config_CryptoCache.h
+ transport_rta/config/config_FlowControl_Vegas.h
+ transport_rta/config/config_Forwarder_Local.h
+ transport_rta/config/config_Forwarder_Metis.h
+ transport_rta/config/config_InMemoryVerifier.h
+ transport_rta/config/config_ProtocolStack.h
+ transport_rta/config/config_PublicKeySigner.h
+ transport_rta/config/config_Signer.h
+ transport_rta/config/config_SymmetricKeySigner.h
+ transport_rta/config/config_TestingComponent.h
+ )
+
+source_group(rta_config FILES ${RTA_CONFIG_HDRS})
+
+set(RTA_CONNECTORS_HDRS
+ transport_rta/connectors/connector_Api.h
+ transport_rta/connectors/rta_ApiConnection.h
+ transport_rta/connectors/connector_Forwarder.h
+ )
+
+source_group(rta_connectors FILES ${RTA_CONNECTORS_HDRS})
+
+set(RTA_COMPONENTS_HDRS
+ transport_rta/components/Flowcontrol_Vegas/vegas_private.h
+ transport_rta/components/codec_Signing.h
+ transport_rta/components/component_Codec.h
+ transport_rta/components/component_Flowcontrol.h
+ transport_rta/components/component_Testing.h
+ )
+
+source_group(rta_components FILES ${RTA_COMPONENTS_HDRS})
+
+set(COMMON_SRCS
+ librta_About.c
+ common/transport.c
+ common/ccnx_TransportConfig.c
+ common/transport_Message.c
+ common/transport_MetaMessage.c
+ common/ccnx_StackConfig.c
+ common/ccnx_ConnectionConfig.c
+ )
+
+source_group(common FILES ${COMMON_SRCS})
+
+set(RTA_CORE_SRCS
+ transport_rta/core/rta_ComponentStats.c
+ transport_rta/core/rta_Component.c
+ transport_rta/core/rta_Connection.c
+ transport_rta/core/rta_ConnectionTable.c
+ transport_rta/core/rta_Framework.c
+ transport_rta/core/rta_Framework_Commands.c
+ transport_rta/core/rta_Framework_Services.c
+ transport_rta/core/rta_Framework_Threaded.c
+ transport_rta/core/rta_Framework_NonThreaded.c
+ transport_rta/core/rta_Logger.c
+ transport_rta/core/rta_ProtocolStack.c
+ transport_rta/rta_Transport.c
+ test_tools/bent_pipe.c
+ test_tools/traffic_tools.c
+ )
+
+source_group(core FILES ${RTA_CORE_SRCS})
+
+set(RTA_COMMANDS_SRCS
+ transport_rta/commands/rta_Command.c
+ transport_rta/commands/rta_CommandCloseConnection.c
+ transport_rta/commands/rta_CommandCreateProtocolStack.c
+ transport_rta/commands/rta_CommandDestroyProtocolStack.c
+ transport_rta/commands/rta_CommandOpenConnection.c
+ transport_rta/commands/rta_CommandTransmitStatistics.c
+ )
+
+source_group(rta_commands FILES ${RTA_COMMANDS_SRCS})
+
+set(RTA_CONFIG_SRCS
+ transport_rta/config/config_ApiConnector.c
+ transport_rta/config/config_Codec_Tlv.c
+ transport_rta/config/config_FlowControl_Vegas.c
+ transport_rta/config/config_Forwarder_Local.c
+ transport_rta/config/config_Forwarder_Metis.c
+ transport_rta/config/config_TestingComponent.c
+ transport_rta/config/config_InMemoryVerifier.c
+ transport_rta/config/config_ProtocolStack.c
+ transport_rta/config/config_PublicKeySigner.c
+ transport_rta/config/config_Signer.c
+ transport_rta/config/config_SymmetricKeySigner.c
+ )
+
+source_group(rta_config FILES ${RTA_CONFIG_SRCS})
+
+set(RTA_CONNECTORS_SRCS
+ transport_rta/connectors/connector_Api.c
+ transport_rta/connectors/rta_ApiConnection.c
+ transport_rta/connectors/connector_Forwarder_Local.c
+ transport_rta/connectors/connector_Forwarder_Metis.c
+ )
+
+source_group(rta_connectors FILES ${RTA_CONNECTORS_SRCS})
+
+set(RTA_COMPONENTS_SRCS
+ transport_rta/components/codec_Signing.c
+ transport_rta/components/component_Codec_Tlv.c
+ transport_rta/components/Flowcontrol_Vegas/component_Vegas.c
+ transport_rta/components/Flowcontrol_Vegas/vegas_Session.c
+ transport_rta/components/component_Testing.c
+ )
+
+source_group(rta_components FILES ${RTA_COMPONENTS_SRCS})
+
+set(TRANSPORT_RTA_SOURCE_FILES
+ ${BASE_HDRS}
+ ${COMMON_HDRS}
+ ${RTA_CORE_HDRS}
+ ${RTA_CONFIG_HDRS}
+ ${RTA_COMPONENTS_HDRS}
+ ${RTA_CONNECTORS_HDRS}
+ ${RTA_COMMANDS_HDRS}
+ ${TEST_TOOLS_HDRS}
+
+ ${COMMON_SRCS}
+ ${RTA_CORE_SRCS}
+ ${RTA_CONFIG_SRCS}
+ ${RTA_COMPONENTS_SRCS}
+ ${RTA_CONNECTORS_SRCS}
+ ${RTA_COMMANDS_SRCS}
+ )
+
+add_library(ccnx_transport_rta STATIC ${TRANSPORT_RTA_SOURCE_FILES})
+add_library(ccnx_transport_rta.shared SHARED ${TRANSPORT_RTA_SOURCE_FILES})
+
+set_target_properties(ccnx_transport_rta.shared PROPERTIES
+ C_STANDARD 99
+ SOVERSION 1
+ VERSION 1.0
+ OUTPUT_NAME ccnx_transport_rta )
+
+set(libccnx_transport_rta_libraries
+ ccnx_transport_rta
+ ccnx_transport_rta.shared
+ )
+
+foreach(lib ${libccnx_transport_rta_libraries})
+ install(TARGETS ${lib} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
+ set_property(TARGET ${lib} PROPERTY C_STANDARD 99)
+endforeach()
+
+install(FILES ${BASE_HDRS} DESTINATION include/ccnx/transport )
+install(FILES ${COMMON_HDRS} DESTINATION include/ccnx/transport/common )
+install(FILES ${TEST_TOOLS_HDRS} DESTINATION include/ccnx/transport/test_tools )
+install(FILES ${RTA_CORE_HDRS} DESTINATION include/ccnx/transport/transport_rta )
+install(FILES ${RTA_CONFIG_HDRS} DESTINATION include/ccnx/transport/transport_rta/config )
+install(FILES ${RTA_COMMANDS_HDRS} DESTINATION include/ccnx/transport/transport_rta/commands )
+
+add_subdirectory(common/test)
+add_subdirectory(transport_rta/test)
+add_subdirectory(transport_rta/commands/test)
+add_subdirectory(transport_rta/components/test)
+add_subdirectory(transport_rta/config/test)
+add_subdirectory(transport_rta/connectors/test)
+add_subdirectory(transport_rta/core/test)
diff --git a/libccnx-transport-rta/ccnx/transport/README b/libccnx-transport-rta/ccnx/transport/README
new file mode 100644
index 00000000..4b346689
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/README
@@ -0,0 +1,24 @@
+/*
+ * 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/
+
+The general transport framework, including the TransportMessage that
+crosses the boundary up to the User API framework.
+
+transport_rta/
+
+A "Ready To Assemble" transport with modular pieces.
+
diff --git a/libccnx-transport-rta/ccnx/transport/common/ccnx_ConnectionConfig.c b/libccnx-transport-rta/ccnx/transport/common/ccnx_ConnectionConfig.c
new file mode 100644
index 00000000..3792d80b
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/ccnx_ConnectionConfig.c
@@ -0,0 +1,123 @@
+/*
+ * 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 subsystems instantiated within components
+ * They define per-connection behavior, not stack structure.
+ *
+ */
+#include <config.h>
+#include <LongBow/runtime.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_DisplayIndented.h>
+
+#include <ccnx/transport/common/ccnx_TransportConfig.h>
+
+struct ccnx_connection_config {
+ PARCJSON *connjson;
+};
+
+bool
+ccnxConnectionConfig_IsValid(const CCNxConnectionConfig *config)
+{
+ bool result = false;
+ if (config != NULL) {
+ result = true;
+ }
+ return result;
+}
+
+void
+ccnxConnectionConfig_AssertValid(const CCNxConnectionConfig *config)
+{
+ assertTrue(ccnxConnectionConfig_IsValid(config), "CCNxConnectionConfig instance is invalid.");
+}
+
+CCNxConnectionConfig *
+ccnxConnectionConfig_Create(void)
+{
+ CCNxConnectionConfig *config = parcMemory_AllocateAndClear(sizeof(CCNxConnectionConfig));
+ assertNotNull(config, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CCNxConnectionConfig));
+ config->connjson = parcJSON_Create();
+ return config;
+}
+
+void
+ccnxConnectionConfig_Destroy(CCNxConnectionConfig **connectionConfigPtr)
+{
+ assertNotNull(connectionConfigPtr, "Parameter must be non-null double pointer");
+
+ CCNxConnectionConfig *config = *connectionConfigPtr;
+ ccnxConnectionConfig_OptionalAssertValid(config);
+
+ parcJSON_Release(&config->connjson);
+ parcMemory_Deallocate((void **) &config);
+ *connectionConfigPtr = NULL;
+}
+
+PARCJSON *
+ccnxConnectionConfig_GetJson(const CCNxConnectionConfig *config)
+{
+ ccnxConnectionConfig_OptionalAssertValid(config);
+
+ return config->connjson;
+}
+
+CCNxConnectionConfig *
+ccnxConnectionConfig_Add(CCNxConnectionConfig *config, const char *key, PARCJSONValue *componentJson)
+{
+ ccnxConnectionConfig_OptionalAssertValid(config);
+
+ parcJSON_AddValue(config->connjson, key, componentJson);
+ return config;
+}
+
+CCNxConnectionConfig *
+ccnxConnectionConfig_Copy(const CCNxConnectionConfig *original)
+{
+ ccnxConnectionConfig_OptionalAssertValid(original);
+
+ CCNxConnectionConfig *copy = parcMemory_AllocateAndClear(sizeof(CCNxConnectionConfig));
+ assertNotNull(copy, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CCNxConnectionConfig));
+ copy->connjson = parcJSON_Copy(original->connjson);
+ return copy;
+}
+
+bool
+ccnxConnectionConfig_Equals(const CCNxConnectionConfig *x, const CCNxConnectionConfig *y)
+{
+ bool result = false;
+
+ if (x == y) {
+ result = true;
+ } else if (x == NULL || y == NULL) {
+ result = false;
+ } else {
+ result = parcJSON_Equals(x->connjson, y->connjson);
+ }
+
+ return result;
+}
+
+void
+ccnxConnectionConfig_Display(const CCNxConnectionConfig *instance, int indentation)
+{
+ parcDisplayIndented_PrintLine(indentation, "ConnectionConfig@%p {", instance);
+ PARCJSON *json = ccnxConnectionConfig_GetJson(instance);
+
+ parcJSON_Display(json, indentation + 1);
+ parcDisplayIndented_PrintLine(indentation, "}");
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/ccnx_ConnectionConfig.h b/libccnx-transport-rta/ccnx/transport/common/ccnx_ConnectionConfig.h
new file mode 100644
index 00000000..37fbcf8c
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/ccnx_ConnectionConfig.h
@@ -0,0 +1,190 @@
+/*
+ * 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 ccnx_ConnectionConfig.
+ * @brief Tranport Stack Connection configuration information.
+ *
+ * These are subsystems instantiated within components
+ * They define per-connection behavior, not stack structure.
+ *
+ */
+#ifndef TransportRTA_connectionConfig_h
+#define TransportRTA_connectionConfig_h
+
+
+struct ccnx_connection_config;
+typedef struct ccnx_connection_config CCNxConnectionConfig;
+
+/**
+ * Create a `CCNxConnectionConfig` instance.
+ *
+ * The instance must be populated with configuration information before it can be used.
+ *
+ *
+ * @return NULL An error occurred.
+ * @return non-NULL A valid `CCNxConnectionConfig` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxConnectionConfig *config = ccnxConnectionConfig_Create();
+ *
+ * ccnxConnectionConfig_Destroy(&config);
+ * @endcode
+ */
+CCNxConnectionConfig *ccnxConnectionConfig_Create(void);
+
+/**
+ * Destroy previously created `CCNxConnectionConfig` instance.
+ *
+ * @param [in] configPtr A pointer to a pointer to a valid `CCNxConnectionConfig` instance that will be set to zero upon return.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxConnectionConfig *config = ccnxConnectionConfig_Create();
+ *
+ * ccnxConnectionConfig_Destroy(&config);
+ * @endcode
+ */
+void ccnxConnectionConfig_Destroy(CCNxConnectionConfig **configPtr);
+
+#ifdef CCNxTransport_DISABLE_VALIDATION
+# define ccnxConnectionConfig_OptionalAssertValid(_instance_)
+#else
+# define ccnxConnectionConfig_OptionalAssertValid(_instance_) ccnxConnectionConfig_AssertValid(_instance_)
+#endif
+
+/**
+ * Determine if an instance of `CCNxTransportConfig` 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] config A pointer to a `CCNxTransportConfig` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxConnectionConfig *config = ccnxConnectionConfig_Create();
+ * ccnxConnectionConfig_IsValid(config);
+ * ccnxConnectionConfig_Destroy(&config);
+ * }
+ * @endcode
+ */
+bool ccnxConnectionConfig_IsValid(const CCNxConnectionConfig *config);
+
+/**
+ * Assert that an instance of `CCNxTransportConfig` 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] config A pointer to a `CCNxTransportConfig` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxConnectionConfig *config = ccnxConnectionConfig_Create();
+ * ccnxConnectionConfig_AssertValid(config);
+ * ccnxConnectionConfig_Destroy(&config);
+ * }
+ * @endcode
+ */
+void ccnxConnectionConfig_AssertValid(const CCNxConnectionConfig *config);
+
+/**
+ * Determine if two `CCNxConnectionConfig` instances are equal.
+ *
+ * The following equivalence relations on non-null `CCNxConnectionConfig` instances are maintained: *
+ * * It is reflexive: for any non-null reference value x, `ccnxConnectionConfig_Equals(x, x)` must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y, `ccnxConnectionConfig_Equals(x, y)` must return true if and only if
+ * `ccnxConnectionConfig_Equals(y x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `ccnxConnectionConfig_Equals(x, y)` returns true and
+ * `ccnxConnectionConfig_Equals(y, z)` returns true,
+ * then `ccnxConnectionConfig_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxConnectionConfig_Equals(x, y)`
+ * consistently return true or consistently return false.
+ *
+ * * For any non-null reference value x, `ccnxConnectionConfig_Equals(x, NULL)` must return false.
+ *
+ * @param [in] x A pointer to a valid CCNxConnectionConfig instance.
+ * @param [in] y A pointer to a valid CCNxConnectionConfig instance.
+ *
+ * @return true The instances x and y are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxConnectionConfig *a = ccnxConnectionConfig_Create();
+ * CCNxConnectionConfig *b = ccnxConnectionConfig_Create();
+ *
+ * if (ccnxConnectionConfig_Equals(a, b)) {
+ * printf("Instances are equal.\n");
+ * }
+ *
+ * ccnxConnectionConfig_Release(&a);
+ * ccnxConnectionConfig_Release(&b);
+ * }
+ * @endcode
+ * @see ccnxConnectionConfig_HashCode
+ */
+bool ccnxConnectionConfig_Equals(const CCNxConnectionConfig *x, const CCNxConnectionConfig *y);
+
+
+/**
+ * Get the underlying JSON representation of a `CCNxConnectionConfig` instance.
+ *
+ * @param [in] config A pointer to a valid `CCNxConnectionConfig` instance.
+ *
+ * @return non-NULL A pointer to a valid PARCJSON instance.
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ */
+PARCJSON *ccnxConnectionConfig_GetJson(const CCNxConnectionConfig *cssonfig);
+
+/**
+ * Add a component's configuration to the connection's configuration. Each component snippit will
+ * result in an addition like this:
+ *
+ * { "key" : { param1 : value1, param2 : value2, ... } }
+ */
+CCNxConnectionConfig *ccnxConnectionConfig_Add(CCNxConnectionConfig *connectionConfig, const char *key, PARCJSONValue *componentJson);
+
+/**
+ * Make a copy of the given CCNxConnectionConfig. The original and copy
+ * must both be destroyed.
+ */
+CCNxConnectionConfig *ccnxConnectionConfig_Copy(const CCNxConnectionConfig *original);
+
+/**
+ * Print a human readable representation of the given instance.
+ *
+ * @param [in] indentation The level of indentation to use to pretty-print the output.
+ * @param [in] instance A pointer to the instance to display.
+ */
+void ccnxConnectionConfig_Display(const CCNxConnectionConfig *instance, int indentation);
+
+#endif
diff --git a/libccnx-transport-rta/ccnx/transport/common/ccnx_StackConfig.c b/libccnx-transport-rta/ccnx/transport/common/ccnx_StackConfig.c
new file mode 100644
index 00000000..b5b55a09
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/ccnx_StackConfig.c
@@ -0,0 +1,160 @@
+/*
+ * 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 <parc/algol/parc_Object.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_DisplayIndented.h>
+
+#include <ccnx/transport/common/ccnx_StackConfig.h>
+
+struct CCNxStackConfig_ {
+ PARCJSON *stackjson;
+};
+
+static void
+_ccnxStackConfig_Finalize(CCNxStackConfig **instancePtr)
+{
+ assertNotNull(instancePtr, "Parameter must be a non-null pointer to a CCNxStackConfig pointer.");
+
+ CCNxStackConfig *instance = *instancePtr;
+ ccnxStackConfig_OptionalAssertValid(instance);
+
+ parcJSON_Release(&instance->stackjson);
+}
+
+parcObject_ImplementAcquire(ccnxStackConfig, CCNxStackConfig);
+
+parcObject_ImplementRelease(ccnxStackConfig, CCNxStackConfig);
+
+parcObject_ExtendPARCObject(CCNxStackConfig, _ccnxStackConfig_Finalize, ccnxStackConfig_Copy, ccnxStackConfig_ToString, ccnxStackConfig_Equals, NULL, ccnxStackConfig_HashCode, ccnxStackConfig_ToJSON);
+
+void
+ccnxStackConfig_AssertValid(const CCNxStackConfig *instance)
+{
+ assertTrue(ccnxStackConfig_IsValid(instance),
+ "CCNxStackConfig is not valid.");
+}
+
+CCNxStackConfig *
+ccnxStackConfig_Create(void)
+{
+ CCNxStackConfig *result = parcObject_CreateInstance(CCNxStackConfig);
+ if (result != NULL) {
+ result->stackjson = parcJSON_Create();
+ }
+
+ return result;
+}
+
+CCNxStackConfig *
+ccnxStackConfig_Copy(const CCNxStackConfig *original)
+{
+ ccnxStackConfig_OptionalAssertValid(original);
+
+ CCNxStackConfig *result = parcObject_CreateInstance(CCNxStackConfig);
+
+ result->stackjson = parcJSON_Copy(original->stackjson);
+
+ return result;
+}
+
+void
+ccnxStackConfig_Display(const CCNxStackConfig *instance, int indentation)
+{
+ parcDisplayIndented_PrintLine(indentation, "CCNxStackConfig@%p {", instance);
+ PARCJSON *json = ccnxStackConfig_GetJson(instance);
+
+ parcJSON_Display(json, indentation + 1);
+ parcDisplayIndented_PrintLine(indentation, "}");
+}
+
+bool
+ccnxStackConfig_Equals(const CCNxStackConfig *x, const CCNxStackConfig *y)
+{
+ bool result = false;
+
+ if (x == y) {
+ result = true;
+ } else if (x == NULL || y == NULL) {
+ result = false;
+ } else {
+ result = parcJSON_Equals(x->stackjson, y->stackjson);
+ }
+
+ return result;
+}
+
+bool
+ccnxStackConfig_IsValid(const CCNxStackConfig *instance)
+{
+ bool result = false;
+ if (instance != NULL) {
+ result = true;
+ }
+ return result;
+}
+
+PARCJSON *
+ccnxStackConfig_ToJSON(const CCNxStackConfig *instance)
+{
+ ccnxStackConfig_OptionalAssertValid(instance);
+
+ return instance->stackjson;
+}
+
+char *
+ccnxStackConfig_ToString(const CCNxStackConfig *instance)
+{
+ PARCJSON *json = ccnxStackConfig_ToJSON(instance);
+
+ char *result = parcJSON_ToString(json);
+
+ return result;
+}
+
+PARCJSONValue *
+ccnxStackConfig_Get(const CCNxStackConfig *config, const char *componentKey)
+{
+ ccnxStackConfig_OptionalAssertValid(config);
+ PARCJSONValue *value = parcJSON_GetValueByName(config->stackjson, componentKey);
+ return value;
+}
+
+PARCHashCode
+ccnxStackConfig_HashCode(const CCNxStackConfig *config)
+{
+ ccnxStackConfig_OptionalAssertValid(config);
+ return parcJSON_HashCode(config->stackjson);
+}
+
+CCNxStackConfig *
+ccnxStackConfig_Add(CCNxStackConfig *config, const char *componentKey, PARCJSONValue *jsonObject)
+{
+ ccnxStackConfig_OptionalAssertValid(config);
+
+ parcJSON_AddValue(config->stackjson, componentKey, jsonObject);
+ return config;
+}
+
+PARCJSON *
+ccnxStackConfig_GetJson(const CCNxStackConfig *config)
+{
+ ccnxStackConfig_OptionalAssertValid(config);
+
+ return (config->stackjson);
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/ccnx_StackConfig.h b/libccnx-transport-rta/ccnx/transport/common/ccnx_StackConfig.h
new file mode 100644
index 00000000..9c3fbbf1
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/ccnx_StackConfig.h
@@ -0,0 +1,327 @@
+/*
+ * 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 ccnx_StackConfig.h
+ * @brief <#Brief Description#>
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef TransportLibrary_ccnx_StackConfig
+#define TransportLibrary_ccnx_StackConfig
+#include <stdbool.h>
+
+#include <parc/algol/parc_JSON.h>
+#include <parc/algol/parc_HashCode.h>
+
+
+struct CCNxStackConfig_;
+typedef struct CCNxStackConfig_ CCNxStackConfig;
+
+/**
+ * Increase the number of references to a `CCNxStackConfig` instance.
+ *
+ * Note that new `CCNxStackConfig` is not created,
+ * only that the given `CCNxStackConfig` reference count is incremented.
+ * Discard the reference by invoking `ccnxStackConfig_Release`.
+ *
+ * @param [in] instance A pointer to a valid CCNxStackConfig instance.
+ *
+ * @return The same value as @p instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *a = ccnxStackConfig_Create();
+ *
+ * CCNxStackConfig *b = ccnxStackConfig_Acquire();
+ *
+ * ccnxStackConfig_Release(&a);
+ * ccnxStackConfig_Release(&b);
+ * }
+ * @endcode
+ */
+CCNxStackConfig *ccnxStackConfig_Acquire(const CCNxStackConfig *instance);
+
+#ifdef TransportLibrary_DISABLE_VALIDATION
+# define ccnxStackConfig_OptionalAssertValid(_instance_)
+#else
+# define ccnxStackConfig_OptionalAssertValid(_instance_) ccnxStackConfig_AssertValid(_instance_)
+#endif
+
+/**
+ * Assert that the given `CCNxStackConfig` instance is valid.
+ *
+ * @param [in] instance A pointer to a valid CCNxStackConfig instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *a = ccnxStackConfig_Create();
+ *
+ * ccnxStackConfig_AssertValid(a);
+ *
+ * printf("Instance is valid.\n");
+ *
+ * ccnxStackConfig_Release(&b);
+ * }
+ * @endcode
+ */
+void ccnxStackConfig_AssertValid(const CCNxStackConfig *instance);
+
+/**
+ * Create an instance of CCNxStackConfig
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @return non-NULL A pointer to a valid CCNxStackConfig instance.
+ * @return NULL An error occurred.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *a = ccnxStackConfig_Create();
+ *
+ * ccnxStackConfig_Release(&b);
+ * }
+ * @endcode
+ */
+CCNxStackConfig *ccnxStackConfig_Create(void);
+
+/**
+ * Create an independent copy the given `PARCBuffer`
+ *
+ * A new buffer is created as a complete copy of the original.
+ *
+ * @param [in] original A pointer to a valid CCNxStackConfig instance.
+ *
+ * @return NULL Memory could not be allocated.
+ * @return non-NULL A pointer to a new `CCNxStackConfig` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *a = ccnxStackConfig_Create();
+ *
+ * CCNxStackConfig *copy = ccnxStackConfig_Copy(&b);
+ *
+ * ccnxStackConfig_Release(&b);
+ * ccnxStackConfig_Release(&copy);
+ * }
+ * @endcode
+ */
+CCNxStackConfig *ccnxStackConfig_Copy(const CCNxStackConfig *original);
+
+/**
+ * Print a human readable representation of the given `CCNxStackConfig`.
+ *
+ * @param [in] instance A pointer to a valid CCNxStackConfig instance.
+ * @param [in] indentation The indentation level to use for printing.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *a = ccnxStackConfig_Create();
+ *
+ * ccnxStackConfig_Display(a, 0);
+ *
+ * ccnxStackConfig_Release(&b);
+ * }
+ * @endcode
+ */
+void ccnxStackConfig_Display(const CCNxStackConfig *instance, int indentation);
+
+/**
+ * Determine if two `CCNxStackConfig` instances are equal.
+ *
+ * The following equivalence relations on non-null `CCNxStackConfig` instances are maintained: *
+ * * It is reflexive: for any non-null reference value x, `ccnxStackConfig_Equals(x, x)` must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y, `ccnxStackConfig_Equals(x, y)` must return true if and only if
+ * `ccnxStackConfig_Equals(y x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `ccnxStackConfig_Equals(x, y)` returns true and
+ * `ccnxStackConfig_Equals(y, z)` returns true,
+ * then `ccnxStackConfig_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxStackConfig_Equals(x, y)`
+ * consistently return true or consistently return false.
+ *
+ * * For any non-null reference value x, `ccnxStackConfig_Equals(x, NULL)` must return false.
+ *
+ * @param [in] x A pointer to a valid CCNxStackConfig instance.
+ * @param [in] y A pointer to a valid CCNxStackConfig instance.
+ *
+ * @return true The instances x and y are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *a = ccnxStackConfig_Create();
+ * CCNxStackConfig *b = ccnxStackConfig_Create();
+ *
+ * if (ccnxStackConfig_Equals(a, b)) {
+ * printf("Instances are equal.\n");
+ * }
+ *
+ * ccnxStackConfig_Release(&a);
+ * ccnxStackConfig_Release(&b);
+ * }
+ * @endcode
+ * @see ccnxStackConfig_HashCode
+ */
+bool ccnxStackConfig_Equals(const CCNxStackConfig *x, const CCNxStackConfig *y);
+
+/**
+ * Determine if an instance of `CCNxStackConfig` 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 valid CCNxStackConfig instance.
+ *
+ * @return true The instance is valid.
+ * @return false The instance is not valid.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *a = ccnxStackConfig_Create();
+ *
+ * if (ccnxStackConfig_IsValid(a)) {
+ * printf("Instance is valid.\n");
+ * }
+ *
+ * ccnxStackConfig_Release(&b);
+ * }
+ * @endcode
+ *
+ */
+bool ccnxStackConfig_IsValid(const CCNxStackConfig *instance);
+
+/**
+ * Release a previously acquired reference to the given `CCNxStackConfig` 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] instancePtr A pointer to a pointer to the instance to release.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *a = ccnxStackConfig_Create();
+ *
+ * ccnxStackConfig_Release(&a);
+ * }
+ * @endcode
+ */
+void ccnxStackConfig_Release(CCNxStackConfig **instancePtr);
+
+/**
+ * Create a `PARCJSON` instance (representation) of the given object.
+ *
+ * @param [in] instance A pointer to a valid CCNxStackConfig instance.
+ *
+ * @return NULL Memory could not be allocated to contain the `PARCJSON` instance.
+ * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate().
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *a = ccnxStackConfig_Create();
+ *
+ * PARCJSON *json = ccnxStackConfig_ToJSON(a);
+ *
+ * printf("JSON representation: %s\n", parcJSON_ToString(json));
+ * parcJSON_Release(&json);
+ *
+ * ccnxStackConfig_Release(&a);
+ * }
+ * @endcode
+ */
+PARCJSON *ccnxStackConfig_ToJSON(const CCNxStackConfig *instance);
+
+/**
+ * Produce a null-terminated string representation of the specified `CCNxStackConfig`.
+ *
+ * The result must be freed by the caller via {@link parcMemory_Deallocate}.
+ *
+ * @param [in] instance A pointer to a valid CCNxStackConfig instance.
+ *
+ * @return NULL Cannot allocate memory.
+ * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *a = ccnxStackConfig_Create();
+ *
+ * char *string = ccnxStackConfig_ToString(a);
+ *
+ * ccnxStackConfig_Release(&a);
+ *
+ * parcMemory_Deallocate(&string);
+ * }
+ * @endcode
+ *
+ * @see ccnxStackConfig_Display
+ */
+char *ccnxStackConfig_ToString(const CCNxStackConfig *instance);
+
+PARCJSONValue *ccnxStackConfig_Get(const CCNxStackConfig *config, const char *componentKey);
+
+/**
+ * Returns a hash code value for the given instance.
+ *
+ * The general contract of the `HashCode` function is:
+ *
+ * Whenever it is invoked on the same instance more than once during an execution of an application,
+ * the `HashCode` function must consistently return the same value,
+ * provided no information in the instance is modified.
+ *
+ * This value need not remain consistent from one execution of an application to another execution of the same application.
+ * If two instances are equal according to the `Equals` function,
+ * then calling the `HashCode` function on each of the two instances must produce the same result.
+ *
+ * It is not required that if two instances are unequal according to the `Equals` function,
+ * then calling the `HashCode` function
+ * on each of the two objects must produce distinct integer results.
+ *
+ * @param [in] instance A pointer to the `CCNxStackConfig` instance.
+ *
+ * @return The hashcode for the given instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxStackConfig *buffer = ccnxStackConfig_Allocate(10);
+ * PARCHashCode hash = ccnxStackConfig_HashCode(buffer);
+ * ccnxStackConfig_Release(&buffer);
+ * }
+ * @endcode
+ */
+PARCHashCode ccnxStackConfig_HashCode(const CCNxStackConfig *instance);
+
+CCNxStackConfig *ccnxStackConfig_Add(CCNxStackConfig *config, const char *componentKey, PARCJSONValue *jsonObject);
+
+PARCJSON *ccnxStackConfig_GetJson(const CCNxStackConfig *config);
+#endif
diff --git a/libccnx-transport-rta/ccnx/transport/common/ccnx_TransportConfig.c b/libccnx-transport-rta/ccnx/transport/common/ccnx_TransportConfig.c
new file mode 100644
index 00000000..f1d55e6c
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/ccnx_TransportConfig.c
@@ -0,0 +1,120 @@
+/*
+ * 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 <parc/algol/parc_Memory.h>
+#include <LongBow/runtime.h>
+
+#include <ccnx/transport/common/ccnx_TransportConfig.h>
+
+struct transport_config {
+ CCNxStackConfig *stackConfig;
+ CCNxConnectionConfig *connConfig;
+};
+
+bool
+ccnxTransportConfig_IsValid(const CCNxTransportConfig *transportConfig)
+{
+ bool result = false;
+
+ if (transportConfig != NULL) {
+ if (ccnxStackConfig_IsValid(transportConfig->stackConfig)) {
+ if (ccnxConnectionConfig_IsValid(transportConfig->connConfig)) {
+ result = true;
+ }
+ }
+ }
+ return result;
+}
+
+void
+ccnxTransportConfig_AssertValid(const CCNxTransportConfig *config)
+{
+ assertTrue(ccnxTransportConfig_IsValid(config), "CCNxTransportConfig instance is invalid.");
+}
+
+CCNxTransportConfig *
+ccnxTransportConfig_Create(CCNxStackConfig *stackConfig, CCNxConnectionConfig *connConfig)
+{
+ ccnxStackConfig_OptionalAssertValid(stackConfig);
+ ccnxConnectionConfig_OptionalAssertValid(connConfig);
+
+ CCNxTransportConfig *result = parcMemory_AllocateAndClear(sizeof(CCNxTransportConfig));
+ assertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CCNxTransportConfig));
+ result->stackConfig = ccnxStackConfig_Acquire(stackConfig);
+ result->connConfig = connConfig;
+ return result;
+}
+
+void
+ccnxTransportConfig_Destroy(CCNxTransportConfig **transportConfigPtr)
+{
+ assertNotNull(transportConfigPtr, "Parameter must be non-null double pointer");
+ ccnxTransportConfig_OptionalAssertValid(*transportConfigPtr);
+
+ CCNxTransportConfig *transConfig = *transportConfigPtr;
+ ccnxStackConfig_Release(&transConfig->stackConfig);
+ ccnxConnectionConfig_Destroy(&transConfig->connConfig);
+ parcMemory_Deallocate((void **) &transConfig);
+ *transportConfigPtr = NULL;
+}
+
+CCNxStackConfig *
+ccnxTransportConfig_GetStackConfig(const CCNxTransportConfig *transportConfig)
+{
+ ccnxTransportConfig_OptionalAssertValid(transportConfig);
+
+ return transportConfig->stackConfig;
+}
+
+CCNxConnectionConfig *
+ccnxTransportConfig_GetConnectionConfig(const CCNxTransportConfig *transportConfig)
+{
+ ccnxTransportConfig_OptionalAssertValid(transportConfig);
+
+ return transportConfig->connConfig;
+}
+
+bool
+ccnxTransportConfig_Equals(const CCNxTransportConfig *x, const CCNxTransportConfig *y)
+{
+ bool result = false;
+
+ if (x == y) {
+ result = true;
+ } else if (x == NULL || y == NULL) {
+ result = false;
+ } else {
+ if (ccnxStackConfig_Equals(x->stackConfig, y->stackConfig)) {
+ result = ccnxConnectionConfig_Equals(x->connConfig, y->connConfig);
+ }
+ }
+
+ return result;
+}
+
+CCNxTransportConfig *
+ccnxTransportConfig_Copy(const CCNxTransportConfig *original)
+{
+ ccnxTransportConfig_OptionalAssertValid(original);
+
+ CCNxTransportConfig *copy = parcMemory_AllocateAndClear(sizeof(CCNxTransportConfig));
+ assertNotNull(copy, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CCNxTransportConfig));
+
+ copy->stackConfig = ccnxStackConfig_Copy(original->stackConfig);
+ copy->connConfig = ccnxConnectionConfig_Copy(original->connConfig);
+ return copy;
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/ccnx_TransportConfig.h b/libccnx-transport-rta/ccnx/transport/common/ccnx_TransportConfig.h
new file mode 100644
index 00000000..e85cc5f9
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/ccnx_TransportConfig.h
@@ -0,0 +1,229 @@
+/*
+ * 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 ccnx_TransportConfig.h
+ * @brief The Transport Configuration information.
+ *
+ * The API composes the stack and connection parameters using these functions.
+ * The examples below are for the RTA Transport.
+ *
+ * <code>
+ * CCNxStackConfig *stackConfig = ccnxStackConfig_Create();
+ * ccnxStackConfig_AppendComponents(stackConfig, { RtaComponentNames[API_CONNECTOR], RtaComponentNames[FC_VEGAS],
+ * RtaComponentNames[CODEC_CCNB], RtaComponentNames[FWD_CCND] } );
+ *
+ * ccnxStackConfig_AppendApiConnector(stackConfig);
+ * ccnxStackConfig_AppendVegasFlowController(stackConfig);
+ * ccnxStackConfig_AppendCcndCodec(stackConfig);
+ * ccnxStackConfig_AppendCcndForwarder(stackConfig);
+ *
+ * CCNxConnectionConfig *connConfig = ccnxConnectionConfig_Create();
+ * ccnxConnectionConfig_publicKeySignerPkcs12Store(connConfig, "/Users/mmosko/keystore.p12", "123abc");
+ * ccnxConnectionConfig_InMemoryVerifier(connConfig);
+ *
+ *
+ * RtaCommand *cmdCreateStack = rtaCommand_CreateStack( (CommandCreateStack) { .stack_id = 7, .params = ccnxStackConfig_GetJson(stackConfig) } );
+ * rtaCommand_write(cmdCreateStack, command_fd);
+ * ccnxStackConfig_Release(&stackConfig);
+ *
+ * RtaCommand *cmdOpen = rtaCommand_Open( (CommandOpen) { .stack_id = 7, .api_fd = 12, .transport_fd = 13, .params = connecitonConfig_GetJson(connConfig) } );
+ * rtaCommand_write(cmdCreateStack, command_fd);
+ * ccnxConnectionConfig_Destroy(&connConfig);
+ * </code>
+ *
+ */
+#ifndef Libccnx_transport_Configuration_h
+#define Libccnx_transport_Configuration_h
+
+#include <stdarg.h>
+
+#include <ccnx/transport/common/ccnx_StackConfig.h>
+#include <ccnx/transport/common/ccnx_ConnectionConfig.h>
+
+struct transport_config;
+typedef struct transport_config CCNxTransportConfig;
+
+/**
+ * Create a `CCNxTransportConfig` instance.
+ *
+ * The instance must be populated with configuration information before it can be used.
+ *
+ * @param [in] stackConfig A pointer to a valid `CCNxStackConfig` instance.
+ * @param [in] connectionConfig A pointer to a valid `CCNxConnectionConfig` instance.
+ * @return NULL An error occurred.
+ * @return non-NULL A valid `CCNxTransportConfig` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connectionConfig);
+ *
+ * ccnxTransportConfig_Destroy(&config);
+ * @endcode
+ */
+CCNxTransportConfig *ccnxTransportConfig_Create(CCNxStackConfig *stackConfig, CCNxConnectionConfig *connectionConfig);
+
+#ifdef CCNxTransport_DISABLE_VALIDATION
+# define ccnxTransportConfig_OptionalAssertValid(_instance_)
+#else
+# define ccnxTransportConfig_OptionalAssertValid(_instance_) ccnxTransportConfig_AssertValid(_instance_)
+#endif
+/**
+ * Assert that an instance of `CCNxTransportConfig` 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] config A pointer to a `CCNxTransportConfig` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connectionConfig);
+ * ccnxTransportConfig_AssertValid(config);
+ * ccnxTransportConfig_Destroy(&config);
+ * }
+ * @endcode
+ * @see ccnxTransportConfig_IsValid
+ */
+void ccnxTransportConfig_AssertValid(const CCNxTransportConfig *config);
+
+/**
+ * Destroy previously created `CCNxTransportConfig` instance.
+ *
+ * @param [in] configPtr A pointer to a pointer to a valid `CCNxTransportConfig` instance that will be set to zero upon return.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connectionConfig);
+ *
+ * ccnxTransportConfig_Destroy(&config);
+ * }
+ * @endcode
+ */
+void ccnxTransportConfig_Destroy(CCNxTransportConfig **configPtr);
+
+/**
+ * Get the `CCNxStackConfig` instance in the given `CCNxTransportConfig`
+ *
+ * @param [in] config A pointer to a valid `CCNxTransportConfig` instance.
+ *
+ * @return A pointer to the `CCNxStackConfig` instance in the given `CCNxTransportConfig`
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connectionConfig);
+ *
+ * CCNxStackConfig *stack = ccnxTransportConfig_GetStackConfig(config);
+ *
+ * ccnxTransportConfig_Destroy(&config);
+ * }
+ * @endcode
+ */
+CCNxStackConfig *ccnxTransportConfig_GetStackConfig(const CCNxTransportConfig *config);
+
+/**
+ * Get the `CCNxConnectionConfig` instance in the given `CCNxTransportConfig`
+ *
+ * @param [in] config A pointer to a valid `CCNxTransportConfig` instance.
+ *
+ * @return A pointer to the `CCNxConnectionConfig` instance in the given `CCNxTransportConfig`
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connectionConfig);
+ *
+ * CCNxConnectionConfig *connection = ccnxTransportConfig_GetConnectionConfig(config);
+ *
+ * ccnxTransportConfig_Destroy(&config);
+ * }
+ * @endcode
+ */
+CCNxConnectionConfig *ccnxTransportConfig_GetConnectionConfig(const CCNxTransportConfig *config);
+
+/**
+ * Determine if an instance of `CCNxTransportConfig` 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] config A pointer to a `CCNxTransportConfig` instance.
+ *
+ * @return true The instance is valid.
+ * @return false The instance is not valid.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxTransportConfig *config = ccnxTransportConfig_Create(stackConfig, connectionConfig);
+ * ccnxTransportConfig_IsValid(config);
+ * ccnxTransportConfig_Destroy(&config);
+ * }
+ * @endcode
+ * @see ccnxTransportConfig_AssertValid
+ */
+bool ccnxTransportConfig_IsValid(const CCNxTransportConfig *config);
+/**
+ * Determine if two `CCNxTransportConfig` instances are equal.
+ *
+ * The following equivalence relations on non-null `CCNxTransportConfig` instances are maintained: *
+ * * It is reflexive: for any non-null reference value x, `ccnxTransportConfig_Equals(x, x)` must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y, `ccnxTransportConfig_Equals(x, y)` must return true if and only if
+ * `ccnxTransportConfig_Equals(y x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `ccnxTransportConfig_Equals(x, y)` returns true and
+ * `ccnxTransportConfig_Equals(y, z)` returns true,
+ * then `ccnxTransportConfig_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple invocations of `ccnxTransportConfig_Equals(x, y)`
+ * consistently return true or consistently return false.
+ *
+ * * For any non-null reference value x, `ccnxTransportConfig_Equals(x, NULL)` must return false.
+ *
+ * @param [in] x A pointer to a valid CCNxTransportConfig instance.
+ * @param [in] y A pointer to a valid CCNxTransportConfig instance.
+ *
+ * @return true The instances x and y are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxTransportConfig *a = ccnxTransportConfig_Create();
+ * CCNxTransportConfig *b = ccnxTransportConfig_Create();
+ *
+ * if (ccnxTransportConfig_Equals(a, b)) {
+ * printf("Instances are equal.\n");
+ * }
+ *
+ * ccnxTransportConfig_Release(&a);
+ * ccnxTransportConfig_Release(&b);
+ * }
+ * @endcode
+ * @see ccnxTransportConfig_HashCode
+ */
+bool ccnxTransportConfig_Equals(const CCNxTransportConfig *x, const CCNxTransportConfig *y);
+
+/**
+ * Make a copy of the given TransportConfig. The original and copy
+ * must both be destroyed.
+ */
+CCNxTransportConfig *ccnxTransportConfig_Copy(const CCNxTransportConfig *original);
+#endif // Libccnx_transport_Configuration_h
diff --git a/libccnx-transport-rta/ccnx/transport/common/test/.gitignore b/libccnx-transport-rta/ccnx/transport/common/test/.gitignore
new file mode 100644
index 00000000..6fd12a5f
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/test/.gitignore
@@ -0,0 +1,4 @@
+test_transport_MetaMessage
+test_ccnx_ConnectionConfig
+test_ccnx_StackConfig
+test_ccnx_TransportConfig
diff --git a/libccnx-transport-rta/ccnx/transport/common/test/CMakeLists.txt b/libccnx-transport-rta/ccnx/transport/common/test/CMakeLists.txt
new file mode 100644
index 00000000..c964fc69
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/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_transport_MetaMessage
+ test_ccnx_ConnectionConfig
+ test_ccnx_StackConfig
+ test_ccnx_TransportConfig
+)
+
+
+foreach(test ${TestsExpectedToPass})
+ AddTest(${test})
+endforeach()
+
diff --git a/libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_ConnectionConfig.c b/libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_ConnectionConfig.c
new file mode 100644
index 00000000..2bc4d121
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_ConnectionConfig.c
@@ -0,0 +1,183 @@
+/*
+ * 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 Runner.
+#include "../ccnx_ConnectionConfig.c"
+
+#include <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/testing/parc_MemoryTesting.h>
+#include <parc/testing/parc_ObjectTesting.h>
+
+LONGBOW_TEST_RUNNER(ccnx_ConnectionConfig)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified here, but every test must be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+ LONGBOW_RUN_TEST_FIXTURE(Static);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(ccnx_ConnectionConfig)
+{
+ 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(ccnx_ConnectionConfig)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, ccnxConnectionConfig_Add);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxConnectionConfig_AssertValid);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxConnectionConfig_Equals);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxConnectionConfig_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxConnectionConfig_CreateDestroy);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxConnectionConfig_Display);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxConnectionConfig_GetJson);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxConnectionConfig_IsValid);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ parcSafeMemory_ReportAllocation(STDOUT_FILENO);
+
+ if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, ccnxConnectionConfig_Add)
+{
+ CCNxConnectionConfig *config = ccnxConnectionConfig_Create();
+
+ PARCJSONValue *val = parcJSONValue_CreateFromNULL();
+ ccnxConnectionConfig_Add(config, "key", val);
+ parcJSONValue_Release(&val);
+
+ ccnxConnectionConfig_Destroy(&config);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxConnectionConfig_AssertValid)
+{
+ CCNxConnectionConfig *config = ccnxConnectionConfig_Create();
+ ccnxConnectionConfig_AssertValid(config);
+ ccnxConnectionConfig_Destroy(&config);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxConnectionConfig_Equals)
+{
+ CCNxConnectionConfig *x = ccnxConnectionConfig_Create();
+ CCNxConnectionConfig *y = ccnxConnectionConfig_Create();
+ CCNxConnectionConfig *z = ccnxConnectionConfig_Create();
+ CCNxConnectionConfig *u1 = ccnxConnectionConfig_Create();
+ PARCJSONValue *val = parcJSONValue_CreateFromNULL();
+ ccnxConnectionConfig_Add(u1, "key", val);
+ parcJSONValue_Release(&val);
+
+ parcObjectTesting_AssertEqualsFunction(ccnxConnectionConfig_Equals, x, y, z, u1);
+
+ ccnxConnectionConfig_Destroy(&x);
+ ccnxConnectionConfig_Destroy(&y);
+ ccnxConnectionConfig_Destroy(&z);
+ ccnxConnectionConfig_Destroy(&u1);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxConnectionConfig_Copy)
+{
+ CCNxConnectionConfig *x = ccnxConnectionConfig_Create();
+ PARCJSONValue *val = parcJSONValue_CreateFromNULL();
+ ccnxConnectionConfig_Add(x, "key", val);
+ parcJSONValue_Release(&val);
+
+ CCNxConnectionConfig *y = ccnxConnectionConfig_Copy(x);
+ assertTrue(ccnxConnectionConfig_Equals(x, y), "Expected the copy to be equal to the original");
+ ccnxConnectionConfig_Destroy(&x);
+ ccnxConnectionConfig_Destroy(&y);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxConnectionConfig_CreateDestroy)
+{
+ CCNxConnectionConfig *config = ccnxConnectionConfig_Create();
+ assertNotNull(config, "Expected non-NULL result from ccnxConnectionConfig_Create.");
+ ccnxConnectionConfig_Destroy(&config);
+ assertNull(config, "Expected NULL result from ccnxConnectionConfig_Destroy");
+}
+
+LONGBOW_TEST_CASE(Global, ccnxConnectionConfig_Display)
+{
+ CCNxConnectionConfig *config = ccnxConnectionConfig_Create();
+ ccnxConnectionConfig_Display(config, 0);
+
+ ccnxConnectionConfig_Destroy(&config);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxConnectionConfig_GetJson)
+{
+ CCNxConnectionConfig *config = ccnxConnectionConfig_Create();
+
+ PARCJSON *json = ccnxConnectionConfig_GetJson(config);
+
+ assertNotNull(json, "Expected ccnxConnectionConfig_GetJson result to be non-null.");
+ ccnxConnectionConfig_Destroy(&config);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxConnectionConfig_IsValid)
+{
+ CCNxConnectionConfig *config = ccnxConnectionConfig_Create();
+ assertTrue(ccnxConnectionConfig_IsValid(config), "Expected ccnxConnectionConfig_Create result to be valid.");
+
+ ccnxConnectionConfig_Destroy(&config);
+ assertFalse(ccnxConnectionConfig_IsValid(config), "Expected ccnxConnectionConfig_Destroy result to be invalid.");
+}
+
+LONGBOW_TEST_FIXTURE(Static)
+{
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Static)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Static)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_ConnectionConfig);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_StackConfig.c b/libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_StackConfig.c
new file mode 100644
index 00000000..4f1ee7dc
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_StackConfig.c
@@ -0,0 +1,226 @@
+/*
+ * 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 Runner.
+#include "../ccnx_StackConfig.c"
+#include <LongBow/unit-test.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/testing/parc_MemoryTesting.h>
+#include <parc/testing/parc_ObjectTesting.h>
+
+LONGBOW_TEST_RUNNER(ccnx_StackConfig)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified here, but every test must be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+ LONGBOW_RUN_TEST_FIXTURE(Static);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(ccnx_StackConfig)
+{
+ 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(ccnx_StackConfig)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_AddGet);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_AssertValid);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_CreateAcquireRelease);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_Display);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_Equals);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_HashCode);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_GetJson);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_IsValid);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_ToJSON);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxStackConfig_ToString);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_AddGet)
+{
+ CCNxStackConfig *instance = ccnxStackConfig_Create();
+
+ PARCJSONValue *expected = parcJSONValue_CreateFromNULL();
+ ccnxStackConfig_Add(instance, "key", expected);
+
+ PARCJSONValue *actual = ccnxStackConfig_Get(instance, "key");
+
+ assertTrue(parcJSONValue_Equals(expected, actual), "ccnxStackConfig_Get did not return what was 'added'");
+
+ parcJSONValue_Release(&expected);
+
+ ccnxStackConfig_Release(&instance);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_AssertValid)
+{
+ CCNxStackConfig *instance = ccnxStackConfig_Create();
+ ccnxStackConfig_AssertValid(instance);
+
+ ccnxStackConfig_Release(&instance);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_Copy)
+{
+ CCNxStackConfig *instance = ccnxStackConfig_Create();
+ CCNxStackConfig *copy = ccnxStackConfig_Copy(instance);
+ assertTrue(ccnxStackConfig_Equals(instance, copy), "Expected the copy to be equal to the original");
+
+ ccnxStackConfig_Release(&instance);
+ ccnxStackConfig_Release(&copy);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_CreateAcquireRelease)
+{
+ CCNxStackConfig *config = ccnxStackConfig_Create();
+ assertNotNull(config, "Expected non-NULL result from ccnxConnectionConfig_Create.");
+
+ CCNxStackConfig *reference = ccnxStackConfig_Acquire(config);
+
+ ccnxStackConfig_Release(&config);
+ assertNull(config, "Expected NULL result from ccnxConnectionConfig_Destroy");
+ ccnxStackConfig_Release(&reference);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_Display)
+{
+ CCNxStackConfig *config = ccnxStackConfig_Create();
+ ccnxStackConfig_Display(config, 1);
+
+ ccnxStackConfig_Release(&config);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_Equals)
+{
+ CCNxStackConfig *x = ccnxStackConfig_Create();
+ CCNxStackConfig *y = ccnxStackConfig_Create();
+ CCNxStackConfig *z = ccnxStackConfig_Create();
+
+ CCNxStackConfig *u1 = ccnxStackConfig_Create();
+ PARCJSONValue *val = parcJSONValue_CreateFromNULL();
+ ccnxStackConfig_Add(u1, "key", val);
+ parcJSONValue_Release(&val);
+
+ parcObjectTesting_AssertEquals(x, y, z, NULL);
+
+ ccnxStackConfig_Release(&x);
+ ccnxStackConfig_Release(&y);
+ ccnxStackConfig_Release(&z);
+ ccnxStackConfig_Release(&u1);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_HashCode)
+{
+ CCNxStackConfig *instance = ccnxStackConfig_Create();
+ uint64_t hashCode = ccnxStackConfig_HashCode(instance);
+ printf("%" PRIu64 "\n", hashCode);
+ ccnxStackConfig_Release(&instance);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_GetJson)
+{
+ CCNxStackConfig *instance = ccnxStackConfig_Create();
+ PARCJSON *json = ccnxStackConfig_GetJson(instance);
+
+ assertNotNull(json, "Expected non-null JSON");
+ ccnxStackConfig_Release(&instance);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_IsValid)
+{
+ CCNxStackConfig *instance = ccnxStackConfig_Create();
+ assertTrue(ccnxStackConfig_IsValid(instance), "Expected ccnxStackConfig_Create to result in a valid instance.");
+
+ ccnxStackConfig_Release(&instance);
+ assertFalse(ccnxStackConfig_IsValid(instance), "Expected ccnxStackConfig_Create to result in an invalid instance.");
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_ToJSON)
+{
+ CCNxStackConfig *instance = ccnxStackConfig_Create();
+ PARCJSON *json = ccnxStackConfig_ToJSON(instance);
+ assertNotNull(json, "Expected non-null JSON");
+
+ ccnxStackConfig_Release(&instance);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_ToString)
+{
+ CCNxStackConfig *instance = ccnxStackConfig_Create();
+ char *string = ccnxStackConfig_ToString(instance);
+ assertNotNull(string, "Expected non-null ccnxStackConfig_ToString");
+
+ parcMemory_Deallocate((void **) &string);
+ ccnxStackConfig_Release(&instance);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxStackConfig_Get)
+{
+ CCNxStackConfig *instance = ccnxStackConfig_Create();
+
+ ccnxStackConfig_Release(&instance);
+}
+
+LONGBOW_TEST_FIXTURE(Static)
+{
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Static)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Static)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_StackConfig);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_TransportConfig.c b/libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_TransportConfig.c
new file mode 100644
index 00000000..8b954419
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/test/test_ccnx_TransportConfig.c
@@ -0,0 +1,226 @@
+/*
+ * 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 Runner.
+#include "../ccnx_TransportConfig.c"
+
+#include <LongBow/unit-test.h>
+
+#include <parc/testing/parc_MemoryTesting.h>
+#include <parc/testing/parc_ObjectTesting.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+
+LONGBOW_TEST_RUNNER(ccnx_TransportConfig)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified here, but every test must be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+ LONGBOW_RUN_TEST_FIXTURE(Static);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(ccnx_TransportConfig)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_TransportConfig)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, ccnxTransportConfig_AssertValid);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxTransportConfig_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxTransportConfig_CreateDestroy);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxTransportConfig_GetConnectionConfig);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxTransportConfig_Equals);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxTransportConfig_GetStackConfig);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxTransportConfig_IsValid_True);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxTransportConfig_IsValid_False);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ parcSafeMemory_ReportAllocation(STDOUT_FILENO);
+
+ if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, ccnxTransportConfig_AssertValid)
+{
+ CCNxStackConfig *stack = ccnxStackConfig_Create();
+ CCNxConnectionConfig *connection = ccnxConnectionConfig_Create();
+
+ CCNxTransportConfig *x = ccnxTransportConfig_Create(stack, connection);
+
+ ccnxTransportConfig_AssertValid(x);
+
+ ccnxStackConfig_Release(&stack);
+
+ ccnxTransportConfig_Destroy(&x);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxTransportConfig_Copy)
+{
+ CCNxStackConfig *stack = ccnxStackConfig_Create();
+ CCNxConnectionConfig *connection = ccnxConnectionConfig_Create();
+
+ CCNxTransportConfig *x = ccnxTransportConfig_Create(stack, connection);
+ assertNotNull(x, "Expected non-null result from ccnxTransportConfig_Create");
+ ccnxStackConfig_Release(&stack);
+
+ CCNxTransportConfig *y = ccnxTransportConfig_Copy(x);
+
+ assertTrue(ccnxTransportConfig_Equals(x, y), "Expected ccnxTransportConfig_Copy result to be equal to the original");
+
+ ccnxTransportConfig_Destroy(&x);
+ ccnxTransportConfig_Destroy(&y);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxTransportConfig_Equals)
+{
+ CCNxStackConfig *stack = ccnxStackConfig_Create();
+ CCNxConnectionConfig *connection = ccnxConnectionConfig_Create();
+
+ CCNxTransportConfig *x = ccnxTransportConfig_Create(stack, ccnxConnectionConfig_Copy(connection));
+ CCNxTransportConfig *y = ccnxTransportConfig_Create(stack, ccnxConnectionConfig_Copy(connection));
+ CCNxTransportConfig *z = ccnxTransportConfig_Create(stack, ccnxConnectionConfig_Copy(connection));
+
+ CCNxStackConfig *otherStack = ccnxStackConfig_Create();
+ PARCJSONValue *val = parcJSONValue_CreateFromNULL();
+ ccnxStackConfig_Add(otherStack, "key", val);
+ CCNxTransportConfig *u1 = ccnxTransportConfig_Create(otherStack, ccnxConnectionConfig_Copy(connection));
+
+ CCNxConnectionConfig *otherConnection = ccnxConnectionConfig_Create();
+ ccnxConnectionConfig_Add(otherConnection, "key", val);
+
+ CCNxTransportConfig *u2 = ccnxTransportConfig_Create(stack, otherConnection);
+
+ parcObjectTesting_AssertEqualsFunction(ccnxTransportConfig_Equals, x, y, z, u1, u2);
+
+ assertTrue(ccnxTransportConfig_Equals(x, y), "Expected ccnxTransportConfig_Copy result to be equal to the original");
+
+ parcJSONValue_Release(&val);
+ ccnxStackConfig_Release(&stack);
+ ccnxStackConfig_Release(&otherStack);
+ ccnxConnectionConfig_Destroy(&connection);
+
+ ccnxTransportConfig_Destroy(&x);
+ ccnxTransportConfig_Destroy(&y);
+ ccnxTransportConfig_Destroy(&z);
+ ccnxTransportConfig_Destroy(&u1);
+ ccnxTransportConfig_Destroy(&u2);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxTransportConfig_CreateDestroy)
+{
+ CCNxStackConfig *stack = ccnxStackConfig_Create();
+ CCNxConnectionConfig *connection = ccnxConnectionConfig_Create();
+
+ CCNxTransportConfig *x = ccnxTransportConfig_Create(stack, connection);
+ assertNotNull(x, "Expected non-null result from ccnxTransportConfig_Create");
+ ccnxStackConfig_Release(&stack);
+
+ ccnxTransportConfig_Destroy(&x);
+ assertNull(x, "Expected null result from ccnxStackConfig_Release");
+}
+
+LONGBOW_TEST_CASE(Global, ccnxTransportConfig_GetConnectionConfig)
+{
+ CCNxStackConfig *stack = ccnxStackConfig_Create();
+ CCNxConnectionConfig *connection = ccnxConnectionConfig_Create();
+
+ CCNxTransportConfig *config = ccnxTransportConfig_Create(stack, connection);
+ ccnxStackConfig_Release(&stack);
+
+ CCNxConnectionConfig *actual = ccnxTransportConfig_GetConnectionConfig(config);
+
+ assertTrue(connection == actual, "Expected ccnxTransportConfig_GetConnectionConfig to return the original.");
+
+ ccnxTransportConfig_Destroy(&config);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxTransportConfig_GetStackConfig)
+{
+ CCNxStackConfig *stack = ccnxStackConfig_Create();
+ CCNxConnectionConfig *connection = ccnxConnectionConfig_Create();
+
+ CCNxTransportConfig *config = ccnxTransportConfig_Create(stack, connection);
+
+ CCNxStackConfig *actual = ccnxTransportConfig_GetStackConfig(config);
+
+ assertTrue(stack == actual, "Expected ccnxTransportConfig_GetStackConfig to return the original.");
+
+ ccnxStackConfig_Release(&stack);
+ ccnxTransportConfig_Destroy(&config);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxTransportConfig_IsValid_True)
+{
+ CCNxStackConfig *stack = ccnxStackConfig_Create();
+ CCNxConnectionConfig *connection = ccnxConnectionConfig_Create();
+
+ CCNxTransportConfig *x = ccnxTransportConfig_Create(stack, connection);
+ assertTrue(ccnxTransportConfig_IsValid(x), "Expected ccnxTransportConfig_Create to return a valid instance.");
+ ccnxStackConfig_Release(&stack);
+
+ ccnxTransportConfig_Destroy(&x);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxTransportConfig_IsValid_False)
+{
+ assertFalse(ccnxTransportConfig_IsValid(NULL), "Expected NULL to be an invalid instance.");
+}
+
+LONGBOW_TEST_FIXTURE(Static)
+{
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Static)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Static)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(ccnx_TransportConfig);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/test/test_transport.c b/libccnx-transport-rta/ccnx/transport/common/test/test_transport.c
new file mode 100644
index 00000000..2642acd6
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/test/test_transport.c
@@ -0,0 +1,136 @@
+/*
+ * 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 Runner.
+#include "../transport.c"
+
+#include <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/testing/parc_TestingMemory.h>
+
+LONGBOW_TEST_RUNNER(transport)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified here, but every test must be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+ LONGBOW_RUN_TEST_FIXTURE(Static);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(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(transport)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, Transport_Create_RTA);
+ LONGBOW_RUN_TEST_CASE(Global, Transport_Close);
+ LONGBOW_RUN_TEST_CASE(Global, Transport_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, Transport_Open);
+ LONGBOW_RUN_TEST_CASE(Global, Transport_PassCommand);
+ LONGBOW_RUN_TEST_CASE(Global, Transport_Recv);
+ LONGBOW_RUN_TEST_CASE(Global, Transport_Send);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ LongBowStatus result = LONGBOW_STATUS_SUCCEEDED;
+ if (parcTestingMemory_ExpectedOutstanding(0, "%s", longBowTestCase_GetFullName(testCase)) == false) {
+ result = LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return result;
+}
+
+LONGBOW_TEST_CASE(Global, Transport_Close)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Global, Transport_Create_RTA)
+{
+ TransportContext *transport = Transport_Create(TRANSPORT_RTA);
+
+ Transport_Destroy(&transport);
+}
+
+LONGBOW_TEST_CASE(Global, Transport_Destroy)
+{
+ TransportContext *transport = Transport_Create(TRANSPORT_RTA);
+
+ Transport_Destroy(&transport);
+}
+
+LONGBOW_TEST_CASE(Global, Transport_Open)
+{
+ TransportContext *transport = Transport_Create(TRANSPORT_RTA);
+
+
+ Transport_Destroy(&transport);
+}
+
+LONGBOW_TEST_CASE(Global, Transport_PassCommand)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Global, Transport_Recv)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Global, Transport_Send)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_FIXTURE(Static)
+{
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Static)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Static)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(transport);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/test/test_transport_Message.c b/libccnx-transport-rta/ccnx/transport/common/test/test_transport_Message.c
new file mode 100644
index 00000000..ec9e3a04
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/test/test_transport_Message.c
@@ -0,0 +1,15 @@
+/*
+ * 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.
+ */
+
diff --git a/libccnx-transport-rta/ccnx/transport/common/test/test_transport_MetaMessage.c b/libccnx-transport-rta/ccnx/transport/common/test/test_transport_MetaMessage.c
new file mode 100644
index 00000000..3896d945
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/test/test_transport_MetaMessage.c
@@ -0,0 +1,283 @@
+/*
+ * 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 "../transport_MetaMessage.c"
+#include <stdio.h>
+
+#include <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <ccnx/common/validation/ccnxValidation_CRC32C.h>
+
+LONGBOW_TEST_RUNNER(ccnx_MetaMessage)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+
+ 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(ccnx_MetaMessage)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(ccnx_MetaMessage)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_Acquire_Release);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_CreateFromContentObject);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_CreateFromControl);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_CreateFromInterest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_Display);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_GetContentObject);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_GetControl);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_GetInterest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_IsContentObject);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_IsControl);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_IsInterest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxMetaMessage_EncodeDecode);
+}
+
+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, ccnxMetaMessage_Acquire_Release)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar");
+ CCNxInterest *interest = ccnxInterest_CreateSimple(name);
+ CCNxMetaMessage *portalMessage = ccnxMetaMessage_CreateFromInterest(interest);
+
+ CCNxMetaMessage *ref1 = ccnxMetaMessage_Acquire(portalMessage);
+ CCNxMetaMessage *ref2 = ccnxMetaMessage_Acquire(portalMessage);
+ CCNxMetaMessage *ref3 = ccnxMetaMessage_Acquire(portalMessage);
+
+ ccnxMetaMessage_Release(&ref1);
+ assertNull(ref1, "Expected pointer to pointer to be null after Release()");
+
+ ccnxMetaMessage_Release(&ref2);
+ assertNull(ref2, "Expected pointer to pointer to be null after Release()");
+
+ ccnxMetaMessage_Release(&ref3);
+ assertNull(ref3, "Expected pointer to pointer to be null after Release()");
+
+ ccnxMetaMessage_Release(&portalMessage);
+ ccnxInterest_Release(&interest);
+ ccnxName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_CreateFromContentObject)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar");
+ PARCBuffer *payload = parcBuffer_WrapCString("This is some data. It's not good data, but it is data.");
+ CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload);
+
+ CCNxMetaMessage *portalMessage = ccnxMetaMessage_CreateFromContentObject(contentObject);
+ assertNotNull(portalMessage, "Expected a non-null portal message");
+ ccnxMetaMessage_Release(&portalMessage);
+
+ ccnxContentObject_Release(&contentObject);
+ parcBuffer_Release(&payload);
+ ccnxName_Release(&name);
+}
+
+#ifndef BUGZID_712
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_CreateFromControl)
+{
+ testUnimplemented("");
+}
+#endif // !BUGZID_712
+
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_CreateFromInterest)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar");
+ CCNxInterest *interest = ccnxInterest_CreateSimple(name);
+
+ CCNxMetaMessage *portalMessage = ccnxMetaMessage_CreateFromInterest(interest);
+ assertNotNull(portalMessage, "Expected a non-null portal message");
+ ccnxMetaMessage_Release(&portalMessage);
+
+ ccnxInterest_Release(&interest);
+ ccnxName_Release(&name);
+}
+
+#ifndef BUGZID_712
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_Display)
+{
+ testUnimplemented("");
+}
+#endif // !BUGZID_712
+
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_GetContentObject)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar");
+ PARCBuffer *payload = parcBuffer_WrapCString("This is some data. It's not good data, but it is data.");
+ CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload);
+
+ CCNxMetaMessage *portalMessage = ccnxMetaMessage_CreateFromContentObject(contentObject);
+
+ CCNxContentObject *reference = ccnxMetaMessage_GetContentObject(portalMessage);
+
+#ifndef BUGZID_712
+ // TODO: We need a ccnxContentObject_Equals()!
+ // assertTrue(ccnxContentObject_Equals(contentObject, reference), "Expected reference to equal original contentObject");
+#endif // !BUGZID_712
+ ccnxContentObject_AssertValid(reference);
+
+ ccnxMetaMessage_Release(&portalMessage);
+
+ ccnxContentObject_Release(&contentObject);
+ parcBuffer_Release(&payload);
+ ccnxName_Release(&name);
+}
+
+#ifndef BUGZID_712
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_GetControl)
+{
+ testUnimplemented("");
+}
+#endif // !BUGZID_712
+
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_GetInterest)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar");
+ CCNxInterest *interest = ccnxInterest_CreateSimple(name);
+ CCNxMetaMessage *portalMessage = ccnxMetaMessage_CreateFromInterest(interest);
+ CCNxInterest *reference = ccnxMetaMessage_GetInterest(portalMessage);
+
+ assertTrue(ccnxInterest_Equals(interest, reference), "Expected reference to equal original interest");
+ ccnxInterest_AssertValid(reference);
+
+ ccnxInterest_Release(&reference);
+ ccnxMetaMessage_Release(&portalMessage);
+ ccnxName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_IsContentObject)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar");
+ PARCBuffer *payload = parcBuffer_WrapCString("This is some data. It's not good data, but it is data.");
+ CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload);
+
+ CCNxMetaMessage *portalMessage = ccnxMetaMessage_CreateFromContentObject(contentObject);
+
+ assertTrue(ccnxMetaMessage_IsContentObject(portalMessage), "Expected portal message to be an ContentObject");
+ assertFalse(ccnxMetaMessage_IsInterest(portalMessage), "Did not expect portal message to be an Interest");
+ assertFalse(ccnxMetaMessage_IsControl(portalMessage), "Did not expect portal message to be a Control message");
+
+ ccnxMetaMessage_Release(&portalMessage);
+ ccnxContentObject_Release(&contentObject);
+ parcBuffer_Release(&payload);
+ ccnxName_Release(&name);
+}
+
+#ifndef BUGZID_712
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_IsControl)
+{
+ testUnimplemented("");
+}
+#endif // !BUGZID_712
+
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_IsInterest)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar");
+ CCNxInterest *interest = ccnxInterest_CreateSimple(name);
+ CCNxMetaMessage *portalMessage = ccnxMetaMessage_CreateFromInterest(interest);
+
+ assertTrue(ccnxMetaMessage_IsInterest(portalMessage), "Expected portal message to be an Interest");
+ assertFalse(ccnxMetaMessage_IsContentObject(portalMessage), "Did not expect portal message to be a ContentObject");
+ assertFalse(ccnxMetaMessage_IsControl(portalMessage), "Did not expect portal message to be a Control message");
+
+ ccnxMetaMessage_Release(&portalMessage);
+ ccnxInterest_Release(&interest);
+ ccnxName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_EncodeDecode)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar");
+ CCNxInterest *interest = ccnxInterest_CreateSimple(name);
+ ccnxName_Release(&name);
+
+ PARCSigner *signer = ccnxValidationCRC32C_CreateSigner(); // Would really be SHA256 or something.
+
+ // Encode it to wire format.
+ PARCBuffer *wireFormatBuffer = ccnxMetaMessage_CreateWireFormatBuffer(interest, signer);
+
+ // Now decode it from wireformat.
+ CCNxMetaMessage *decodedMessage = ccnxMetaMessage_CreateFromWireFormatBuffer(wireFormatBuffer);
+
+ // At this point, the unpacked dictionary should be equivalent to the original interest.
+ assertTrue(ccnxInterest_Equals(interest, decodedMessage), "Expected an equivalent interest to be unpacked");
+
+ parcBuffer_Release(&wireFormatBuffer);
+ ccnxInterest_Release(&interest);
+ ccnxMetaMessage_Release(&decodedMessage);
+ parcSigner_Release(&signer);
+}
+
+#ifndef BUGZID_712
+LONGBOW_TEST_CASE(Global, ccnxMetaMessage_Release)
+{
+ testUnimplemented("");
+}
+#endif // !BUGZID_712
+
+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(ccnx_MetaMessage);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/test/test_transport_Stack.c b/libccnx-transport-rta/ccnx/transport/common/test/test_transport_Stack.c
new file mode 100644
index 00000000..4a10d962
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/test/test_transport_Stack.c
@@ -0,0 +1,191 @@
+/*
+ * 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 "../transport_Stack.c"
+
+#include <LongBow/testing.h>
+#include <LongBow/debugging.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/algol/parc_DisplayIndented.h>
+
+#include <parc/testing/parc_MemoryTesting.h>
+#include <parc/testing/parc_ObjectTesting.h>
+
+LONGBOW_TEST_RUNNER(transport_Stack)
+{
+ // 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(CreateAcquireRelease);
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(transport_Stack)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(transport_Stack)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(CreateAcquireRelease)
+{
+ LONGBOW_RUN_TEST_CASE(CreateAcquireRelease, CreateRelease);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(CreateAcquireRelease)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(CreateAcquireRelease)
+{
+ if (!parcMemoryTesting_ExpectedOutstanding(0, "%s leaked memory.", longBowTestCase_GetFullName(testCase))) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(CreateAcquireRelease, CreateRelease)
+{
+ TransportStack *instance = transportStack_Create();
+ assertNotNull(instance, "Expected non-null result from transportStack_Create();");
+
+ parcObjectTesting_AssertAcquireReleaseContract(instance);
+
+ transportStack_Release(&instance);
+ assertNull(instance, "Expected null result from transportStack_Release();");
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, transportStack_Compare);
+ LONGBOW_RUN_TEST_CASE(Global, transportStack_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, transportStack_Display);
+ LONGBOW_RUN_TEST_CASE(Global, transportStack_Equals);
+ LONGBOW_RUN_TEST_CASE(Global, transportStack_HashCode);
+ LONGBOW_RUN_TEST_CASE(Global, transportStack_IsValid);
+ LONGBOW_RUN_TEST_CASE(Global, transportStack_ToJSON);
+ LONGBOW_RUN_TEST_CASE(Global, transportStack_ToString);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ if (!parcMemoryTesting_ExpectedOutstanding(0, "%s mismanaged memory.", longBowTestCase_GetFullName(testCase))) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, transportStack_Compare)
+{
+ testUnimplemented("");
+}
+
+LONGBOW_TEST_CASE(Global, transportStack_Copy)
+{
+ TransportStack *instance = transportStack_Create();
+ TransportStack *copy = transportStack_Copy(instance);
+ assertTrue(transportStack_Equals(instance, copy), "Expected the copy to be equal to the original");
+
+ transportStack_Release(&instance);
+ transportStack_Release(&copy);
+}
+
+LONGBOW_TEST_CASE(Global, transportStack_Display)
+{
+ TransportStack *instance = transportStack_Create();
+ transportStack_Display(instance, 0);
+ transportStack_Release(&instance);
+}
+
+LONGBOW_TEST_CASE(Global, transportStack_Equals)
+{
+ TransportStack *x = transportStack_Create();
+ TransportStack *y = transportStack_Create();
+ TransportStack *z = transportStack_Create();
+
+ parcObjectTesting_AssertEquals(x, y, z, NULL);
+
+ transportStack_Release(&x);
+ transportStack_Release(&y);
+ transportStack_Release(&z);
+}
+
+LONGBOW_TEST_CASE(Global, transportStack_HashCode)
+{
+ TransportStack *x = transportStack_Create();
+ TransportStack *y = transportStack_Create();
+
+ parcObjectTesting_AssertHashCode(x, y);
+
+ transportStack_Release(&x);
+ transportStack_Release(&y);
+}
+
+LONGBOW_TEST_CASE(Global, transportStack_IsValid)
+{
+ TransportStack *instance = transportStack_Create();
+ assertTrue(transportStack_IsValid(instance), "Expected transportStack_Create to result in a valid instance.");
+
+ transportStack_Release(&instance);
+ assertFalse(transportStack_IsValid(instance), "Expected transportStack_Release to result in an invalid instance.");
+}
+
+LONGBOW_TEST_CASE(Global, transportStack_ToJSON)
+{
+ TransportStack *instance = transportStack_Create();
+
+ PARCJSON *json = transportStack_ToJSON(instance);
+
+ parcJSON_Release(&json);
+
+ transportStack_Release(&instance);
+}
+
+LONGBOW_TEST_CASE(Global, transportStack_ToString)
+{
+ TransportStack *instance = transportStack_Create();
+
+ char *string = transportStack_ToString(instance);
+
+ assertNotNull(string, "Expected non-NULL result from transportStack_ToString");
+
+ parcMemory_Deallocate((void **) &string);
+ transportStack_Release(&instance);
+}
+
+int
+main(int argc, char *argv[argc])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(transport_Stack);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
+
+
diff --git a/libccnx-transport-rta/ccnx/transport/common/transport.c b/libccnx-transport-rta/ccnx/transport/common/transport.c
new file mode 100644
index 00000000..0f9e564d
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/transport.c
@@ -0,0 +1,123 @@
+/*
+ * 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 "transport.h"
+#include "transport_private.h"
+#include "rta_Transport.h"
+
+struct transport_context {
+ TransportTypes transport_type;
+ struct transport_operations ops;
+ void *transport_data;
+ unsigned references;
+};
+
+// the one global transport, for now
+static TransportContext *the_context = NULL;
+
+TransportContext *
+Transport_Create(TransportTypes type)
+{
+ if (the_context == NULL) {
+ switch (type) {
+ case TRANSPORT_RTA:
+ the_context = parcMemory_Allocate(sizeof(TransportContext));
+ assertNotNull(the_context, "TransportContext could not be allocated, parcMemory_Allocate(%zu) returned NULL", sizeof(TransportContext));
+
+ the_context->references = 0;
+ the_context->ops = rta_ops;
+ the_context->transport_data = the_context->ops.Create();
+ the_context->transport_type = type;
+ break;
+
+ default:
+ fprintf(stderr, "%s unknown transport type %d\n", __func__, type);
+ abort();
+ break;
+ }
+ }
+
+ if (the_context->transport_type == type) {
+ the_context->references++;
+ return the_context;
+ }
+
+ fprintf(stderr, "%s transport type %d not of request type %d\n",
+ __func__, the_context->transport_type, type);
+ abort();
+}
+
+int
+Transport_Open(CCNxTransportConfig *transportConfig)
+{
+ assertNotNull(the_context, "The TransportContext is NULL.");
+ assertNotNull(transportConfig, "The parameter transportConfig must be a non-null CCNxTransportConfig pointer");
+ return the_context->ops.Open(the_context->transport_data, transportConfig);
+}
+
+int
+Transport_Send(int desc, CCNxMetaMessage *msg_in)
+{
+ assertNotNull(the_context, "the_context is null");
+ return the_context->ops.Send(the_context->transport_data, desc, msg_in, CCNxStackTimeout_Never);
+}
+
+TransportIOStatus
+Transport_Recv(int desc, CCNxMetaMessage **msg_out)
+{
+ return the_context->ops.Recv(the_context->transport_data, desc, msg_out, CCNxStackTimeout_Never);
+}
+
+int
+Transport_Close(int desc)
+{
+ return the_context->ops.Close(the_context->transport_data, desc);
+}
+
+int
+Transport_PassCommand(void *stackCommand)
+{
+ return the_context->ops.PassCommand(the_context->transport_data, stackCommand);
+}
+
+void
+Transport_Destroy(TransportContext **ctxPtr)
+{
+ assertNotNull(ctxPtr, "Transport_Destroy called with null context");
+ assertNotNull(*ctxPtr, "Transport_Destroy callled with reference to null");
+
+ TransportContext *ctx = *ctxPtr;
+
+ assertTrue(the_context == ctx, "Passed ctx is not the same");
+ assertTrue(ctx->references > 0, "Invalid reference count");
+
+ ctx->references--;
+ if (ctx->references == 0) {
+ ctx->ops.Destroy(&ctx->transport_data);
+ memset(ctx, 0, sizeof(TransportContext));
+ parcMemory_Deallocate((void **) &ctx);
+ ctx = NULL;
+ the_context = NULL;
+ }
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/transport.h b/libccnx-transport-rta/ccnx/transport/common/transport.h
new file mode 100644
index 00000000..1068d37d
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/transport.h
@@ -0,0 +1,262 @@
+/*
+ * 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 transport.h
+ * @brief Defines the Transport API from the App API.
+ *
+ * Application interfaces use this API to communicate
+ * with the transport.
+ *
+ * An API will call transport_Create(type), to create
+ * a transport of the given type. Only type TRANSPORT_RTA
+ * is supported at this time. Only one transport may exist,
+ * so multiple calls to transport_Create() will return
+ * a reference counted pointer to the same transport.
+ * When an API is done, it should call transport_Destroy().
+ *
+ * An API opens connections with the forwarder via
+ * transport_Open(PARCJSON *). The JSON dictionary defines
+ * the properties of the protocol stack associated with the
+ * connection. When done, the API should call transport_Close()
+ * on the connection. Multiple calls with the same JSON
+ * definition will return new connecitons using the same
+ * protocol stack.
+ *
+ * See the documentation in transport_rta/core/rta_Component.h
+ * about how to write a new component for use in a protocol stack.
+ *
+ *
+ * transport_Open() requires a JSON configuration string that
+ * defines the SYSTEM and USER parameters. SYSTEM parameters
+ * define the ProtocolStack. USER parameters are variations
+ * within a ProtocolStack, such as which signing keys to use.
+ *
+ * \code{.c}
+ * {
+ * "SYSTEM" : {
+ * "COMPONENTS" : [ array of identifiers ],
+ * <component name> : { component parameters },
+ * <component name> : { component parameters }
+ * }
+ *
+ * "USER" : {
+ * <component name> : {component parameters},
+ * <component name> : {component parameters},
+ * <component name> : {component parameters}
+ * }
+ * }
+ * \endcode
+ *
+ * The COMPONENTS parameter lists the comonents in the protocol stack.
+ * The names should be taken from components.h (e.g. API_CONNECTOR).
+ *
+ * An example would be:
+ * \code{.c}
+ * {
+ * "SYSTEM" : {
+ * "COMPONENTS" : [
+ * "API_CONNECTOR",
+ * "FC_VEGAS,
+ * "VERIFY_ENUMERATED",
+ * "CODEC_TLV",
+ * "FWD_FLAN"
+ * ],
+ * "FWD_FLAN" : { "port" : 1234 },
+ * "FC_VEGAS" : { "max_cwind": 65536 }
+ * }
+ *
+ * "USER" : {
+ * "CODEC_TLV" : {
+ * "SET_SIGNING_KEYSTORE" : {
+ * "KEYSTORE_NAME" : "/Users/alice/.ccnxkeystore",
+ * "KEYSTORE_PASSWD": "1234abcd"
+ * }
+ * }
+ *
+ * "VERIFY_ENUMERATED" : {
+ * "ADD_TRUSTED_CERTS" : [
+ * <PEM encoded X.509 cert>,
+ * <PEM encoded X.509 cert>,
+ * <PEM encoded X.509 cert> ]
+ * "ADD_TRUSTED_ISSUERS" : [
+ * <PEM encoded X.509 cert> ]
+ * "ADD_TRUSTED_KEYS" : [
+ * <PEM encoded RSA public key>,
+ * <PEM encoded DSA public key>]
+ * }
+ * }
+ * }
+ * \endcode
+ *
+ */
+#ifndef CCNX_TRANSPORT_H
+#define CCNX_TRANSPORT_H
+
+#include <ccnx/transport/common/transport_MetaMessage.h>
+
+#include <ccnx/common/internal/ccnx_TlvDictionary.h>
+
+#include <ccnx/transport/common/transport_Message.h>
+#include <ccnx/transport/common/ccnx_TransportConfig.h>
+
+struct transport_context;
+typedef struct transport_context TransportContext;
+
+typedef enum {
+ TRANSPORT_RTA
+} TransportTypes;
+
+typedef enum TransportIOStatus {
+ TransportIOStatus_Success = 0,
+ TransportIOStatus_Error = 1,
+ TransportIOStatus_Timeout = 2
+} TransportIOStatus;
+
+typedef uint64_t CCNxStackTimeout;
+
+/**
+ * @def CCNxStackTimeout_Never
+ * The receive function is a blocking read that never times out.
+ */
+#define CCNxStackTimeout_Never NULL
+
+/*
+ * @def CCNxStackTimeout_Immediate
+ * The receive function is a non-blocking read that immediately either returns a message or nothing.
+ * Equivalent to StackTimeout_MicroSeconds(0)
+ */
+#define CCNxStackTimeout_Immediate (& (uint64_t) { 0 })
+
+/*
+ * @def CCNxStackTimeout_MicroSeconds
+ * The receive function is a blocking read that either waits no longer than the specified number of microseconds or a message,
+ * whichever comes first.
+ */
+#define CCNxStackTimeout_MicroSeconds(_usec_) (& (uint64_t) { _usec_ })
+
+
+/**
+ * Initialize transport. Creates a thread of execution,
+ * you only need one of these.
+ *
+ * You can only have one of these. Multiple calls return a
+ * reference to the existing one (if same type) or an error.
+ *
+ * NULL means error.
+ */
+TransportContext *Transport_Create(TransportTypes type);
+
+/**
+ * Open a descriptor. You may use a select(2) or poll(2) on it, but
+ * you must only use Transport_{Send, Recv, Close} to modify it.
+ *
+ * All transport operations are non-blocking.
+ *
+ * Transport will take ownership of the transportConfig and destroy it and
+ * everyting contained in it.
+ *
+ * Generate the configuration based on your stacks configuration
+ * methods. For RTA, they are in transport_rta/config/.
+ *
+ * @param [in] transportConfig the transport configuration object
+ *
+ * @return the newly opened descriptor
+ *
+ * @see Transport_Close
+ */
+int Transport_Open(CCNxTransportConfig *transportConfig);
+
+/**
+ * Send a `CCNxMetaMessage` to the transport. The CCNxMetaMessage instance is acquired by
+ * the stack and can be released by the caller immediately after sending if desired.
+ *
+ * The CCNxMetaMessage may be a PARCJSON object to modify USER stack parameters.
+ *
+ * @param [in] desc the file descriptor (e.g. one end of a socket) in to which to write he CCNxMetaMessage.
+ * @param [in] msg_in A CCNxMetaMessage instance to send.
+ *
+ * @return 0 if the message was succesfully sent.
+ * @return -1 and sets errno, otherwise. errno will be set to EWOULDBLOCK if it would block.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *msg = ccnxMetaMessage_CreateFromContentObject(contentObject);
+ *
+ * int status = Transport_Send(desc, msg);
+ *
+ * ccnxMetaMessage_Release(&msg);
+ * }
+ * @endcode
+ *
+ * @see ccnxMetaMessage_Release
+ */
+int Transport_Send(int desc, CCNxMetaMessage *msg_in);
+
+/**
+ * Receive a `CCNxMetaMessage` from the transport. The caller is responsible
+ * for calling {@link ccnxMetaMessage_Release} on the message, if successful.
+ *
+ * @param [in] desc the file descriptor (e.g. one end of a socket) from which to read the CCNxMetaMessage.
+ * @param [in] msg_in A CCNxMetaMessage instance to send.
+ *
+ * @return 0 if the message was succesfully sent.
+ * @return -1 and sets errno, otherwise. errno will be set to EWOULDBLOCK if the call would block
+ * or if SO_RCVTIMEO is exceeded on the underlying socket.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *msg;
+ * int status = Transport_Recv(desc, &msg);
+ *
+ * if (status == 0) {
+ * // do things
+ *
+ * ccnxMetaMessage_Release(&msg);
+ * }
+ * }
+ * @endcode
+ *
+ * @see ccnxMetaMessage_Release
+ */
+TransportIOStatus Transport_Recv(int desc, CCNxMetaMessage **msg_out);
+
+/**
+ * Closes a descriptor. Close is immediate, any pending data is lost.
+ *
+ * @param [in] desc the descriptor to close
+ *
+ * @return 0 on success (the descriptor exists and was open)
+ *
+ * @see Transport_Open
+ */
+int Transport_Close(int desc);
+
+/**
+ * Pass a transport-specific command to the underlying framework.
+ * It must be in a "TransportTypes" format that your chosen
+ * transport understands.
+ */
+int Transport_PassCommand(void *stackCommand);
+
+/**
+ * Destroy a TransportContext instance. Shuts done all descriptors and any pending data is lost.
+ *
+ * @param [in] ctxP A pointer to a pointer to the TransportContext instance to release.
+ */
+void Transport_Destroy(TransportContext **ctxP);
+#endif // CCNX_TRANSPORT_H
diff --git a/libccnx-transport-rta/ccnx/transport/common/transport_Message.c b/libccnx-transport-rta/ccnx/transport/common/transport_Message.c
new file mode 100644
index 00000000..1a79ace2
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/transport_Message.c
@@ -0,0 +1,198 @@
+/*
+ * 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 <strings.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <LongBow/runtime.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <ccnx/transport/common/transport.h>
+#include <ccnx/transport/common/transport_private.h>
+#include <ccnx/transport/common/transport_Message.h>
+
+#define DEBUG_OUTPUT 0
+
+struct transport_message {
+ CCNxTlvDictionary *dictionary;
+ TransportMessage_Free *freefunc;
+ void *info;
+
+ struct timeval creationTime;
+};
+
+static size_t _transport_messages_created = 0;
+static size_t _transport_messages_destroyed = 0;
+
+static void
+_transportMessage_GetTimeOfDay(struct timeval *outputTime)
+{
+#ifdef DEBUG
+ // if in debug mode, time messages
+ gettimeofday(outputTime, NULL);
+#else
+ *outputTime = (struct timeval) { 0, 0 };
+#endif
+}
+
+TransportMessage *
+transportMessage_CreateFromDictionary(CCNxTlvDictionary *dictionary)
+{
+ assertNotNull(dictionary, "Cannot create TransportMessage from NULL Dictionary");
+ if (dictionary == NULL) {
+ return NULL;
+ }
+
+ TransportMessage *tm = parcMemory_AllocateAndClear(sizeof(TransportMessage));
+
+ if (tm != NULL) {
+ tm->dictionary = ccnxTlvDictionary_Acquire(dictionary);
+
+ _transportMessage_GetTimeOfDay(&tm->creationTime);
+
+ _transport_messages_created++;
+
+ if (DEBUG_OUTPUT) {
+ printf("%-35s allocs %zu destroys %zu pointer %p dict %p\n",
+ __func__,
+ _transport_messages_created,
+ _transport_messages_destroyed,
+ (void *) tm,
+ (void *) dictionary);
+ }
+ }
+
+ return tm;
+}
+
+CCNxTlvDictionary *
+transportMessage_GetDictionary(TransportMessage *tm)
+{
+ assertNotNull(tm, "TransportMessage_GetWireMessage called on NULL transport message");
+ return tm->dictionary;
+}
+
+bool
+transportMessage_isValid(const TransportMessage *message)
+{
+ bool result = false;
+ if (message != NULL) {
+ result = true;
+ }
+
+ return result;
+}
+
+void
+transportMessage_AssertValid(const TransportMessage *message)
+{
+ assertTrue(transportMessage_isValid(message), "TransportMessage @ %p is invalid.", (void *) message);
+}
+
+/*
+ * Frees the TransportMessage wrapper, but user is responsible
+ * for destroying the inner pieces.
+ */
+static void
+_transportMessage_Destroy(TransportMessage **msgPtr)
+{
+ assertNotNull(msgPtr, "TransportMessage_Destroy called on NULL transport message pointer");
+ if (msgPtr != NULL) {
+ TransportMessage *msg = *msgPtr;
+ transportMessage_OptionalAssertValid(msg);
+
+ _transport_messages_destroyed++;
+
+ if (DEBUG_OUTPUT) {
+ printf("%-35s allocs %zu destroys %zu pointer %p\n",
+ __func__,
+ _transport_messages_created,
+ _transport_messages_destroyed,
+ (void *) msg);
+ }
+
+ if (msg->freefunc != NULL) {
+ msg->freefunc(&msg->info);
+ }
+
+ parcMemory_Deallocate((void **) &msg);
+ *msgPtr = NULL;
+ }
+}
+
+void
+transportMessage_Destroy(TransportMessage **tmPtr)
+{
+ TransportMessage *tm = *tmPtr;
+ assertNotNull(tmPtr, "called with NULL transport message double pointer");
+
+ assertNotNull(tm, "called with NULL transport message dereference");
+
+ if (tm->dictionary != NULL) {
+ ccnxTlvDictionary_Release(&tm->dictionary);
+ tm->dictionary = NULL;
+ }
+
+ _transportMessage_Destroy(tmPtr);
+}
+
+/*
+ * Add some stack payload to a transport message. Will not be freed.
+ */
+void
+transportMessage_SetInfo(TransportMessage *tm, void *info, TransportMessage_Free *freefunc)
+{
+ assertNotNull(tm, "%s called with NULL transport message", __func__);
+ tm->info = info;
+ tm->freefunc = freefunc;
+}
+
+void *
+transportMessage_GetInfo(const TransportMessage *tm)
+{
+ assertNotNull(tm, "%s called with NULL transport message", __func__);
+ return tm->info;
+}
+
+
+struct timeval
+transportMessage_GetDelay(const TransportMessage *tm)
+{
+ struct timeval now;
+ _transportMessage_GetTimeOfDay(&now);
+ timersub(&now, &tm->creationTime, &now);
+ return now;
+}
+
+bool
+transportMessage_IsControl(const TransportMessage *tm)
+{
+ return ccnxTlvDictionary_IsControl(tm->dictionary);
+}
+
+bool
+transportMessage_IsInterest(const TransportMessage *tm)
+{
+ return ccnxTlvDictionary_IsInterest(tm->dictionary);
+}
+
+bool
+transportMessage_IsContentObject(const TransportMessage *tm)
+{
+ return ccnxTlvDictionary_IsContentObject(tm->dictionary);
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/transport_Message.h b/libccnx-transport-rta/ccnx/transport/common/transport_Message.h
new file mode 100644
index 00000000..5f79fc3b
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/transport_Message.h
@@ -0,0 +1,175 @@
+/*
+ * 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 transport_Message.h
+ * @brief <#Brief Description#>
+ *
+ * NOTE: TransportMessage is being phased out for the CCNxTlvDictionary
+ *
+ */
+#ifndef Libccnx_transport_Message_h
+#define Libccnx_transport_Message_h
+
+#include <ccnx/common/codec/ccnxCodec_NetworkBuffer.h>
+
+#include <ccnx/common/internal/ccnx_TlvDictionary.h>
+
+struct transport_message;
+/**
+ *
+ * @see TransportMessage_CreateFromCcnxMessage
+ * @see TransportMessage_CreateFromMessage
+ */
+typedef struct transport_message TransportMessage;
+
+/**
+ * Stores a reference to the given dictionary
+ *
+ * The caller is responsible for releasing 'dictionary' as the transport message stores its own reference.
+ *
+ * @param [in] dictionary A pointer to a valid `CCNxTlvDictionary`
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+TransportMessage *transportMessage_CreateFromDictionary(CCNxTlvDictionary *dictionary);
+
+bool transportMessage_isValid(const TransportMessage *message);
+
+#ifdef TransportLibrary_DISABLE_VALIDATION
+# define transportMessage_OptionalAssertValid(_instance_)
+#else
+# define transportMessage_OptionalAssertValid(_instance_) transportMessage_AssertValid(_instance_)
+#endif
+/**
+ * Assert that the given `TransportMessage` instance is valid.
+ *
+ * @param [in] message A pointer to a valid TransportMessage instance.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportMessage *a = transportMessage_CreateFromDictionary(dictionary);
+ *
+ * transportMessage_AssertValid(a);
+ *
+ * printf("Instance is valid.\n");
+ *
+ * transportMessage_Release(&b);
+ * }
+ * @endcode
+ */
+void transportMessage_AssertValid(const TransportMessage *message);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxTlvDictionary *transportMessage_GetDictionary(TransportMessage *tm);
+
+/**
+ * Destroy the transport message and everything inside it
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void transportMessage_Destroy(TransportMessage **tmPtr);
+
+typedef void (TransportMessage_Free)(void **);
+
+/**
+ * <#One Line Description#>
+ *
+ * Add some stack payload to a transport message.
+ * Will not be freed.
+ * This is typically used to put a pointer to the RtaConnection in the message.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void transportMessage_SetInfo(TransportMessage *tm, void *info, TransportMessage_Free *freefunc);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void *transportMessage_GetInfo(const TransportMessage *tm);
+
+bool transportMessage_IsControl(const TransportMessage *tm);
+bool transportMessage_IsInterest(const TransportMessage *tm);
+bool transportMessage_IsContentObject(const TransportMessage *tm);
+
+/**
+ * If in DEBUG mode, returns how long the message has been in the system
+ *
+ * If not in DEBUG mode, will always be {.tv_sec = 0, .tv_usec = 0}. The time is based
+ * on gettimeofday().
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+struct timeval transportMessage_GetDelay(const TransportMessage *tm);
+#endif // Libccnx_transport_Message_h
diff --git a/libccnx-transport-rta/ccnx/transport/common/transport_MetaMessage.c b/libccnx-transport-rta/ccnx/transport/common/transport_MetaMessage.c
new file mode 100644
index 00000000..a455e7ac
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/transport_MetaMessage.c
@@ -0,0 +1,193 @@
+/*
+ * 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 <ccnx/transport/common/transport_MetaMessage.h>
+#include <ccnx/common/codec/ccnxCodec_TlvPacket.h>
+#include <ccnx/common/ccnx_WireFormatMessage.h>
+#include <ccnx/common/ccnx_Manifest.h>
+
+CCNxMetaMessage *
+ccnxMetaMessage_CreateFromInterest(const CCNxInterest *interest)
+{
+ return ccnxMetaMessage_Acquire((CCNxMetaMessage *) interest);
+}
+
+CCNxMetaMessage *
+ccnxMetaMessage_CreateFromContentObject(const CCNxContentObject *contentObject)
+{
+ return ccnxMetaMessage_Acquire((CCNxMetaMessage *) contentObject);
+}
+
+CCNxMetaMessage *
+ccnxMetaMessage_CreateFromControl(const CCNxControl *control)
+{
+ return ccnxMetaMessage_Acquire((CCNxMetaMessage *) control);
+}
+
+CCNxMetaMessage *
+ccnxMetaMessage_CreateFromManifest(const CCNxManifest *manifest)
+{
+ return ccnxMetaMessage_Acquire((CCNxMetaMessage *) manifest);
+}
+
+
+CCNxContentObject *
+ccnxMetaMessage_GetContentObject(const CCNxMetaMessage *message)
+{
+ return (CCNxContentObject *) message;
+}
+
+CCNxInterest *
+ccnxMetaMessage_GetInterest(const CCNxMetaMessage *message)
+{
+ return (CCNxInterest *) message;
+}
+
+CCNxInterestReturn *
+ccnxMetaMessage_GetInterestReturn(const CCNxMetaMessage *message)
+{
+ return (CCNxInterestReturn *) message;
+}
+
+CCNxControl *
+ccnxMetaMessage_GetControl(const CCNxMetaMessage *message)
+{
+ return (CCNxControl *) message;
+}
+
+CCNxManifest *
+ccnxMetaMessage_GetManifest(const CCNxMetaMessage *message)
+{
+ return (CCNxManifest *) message;
+}
+
+CCNxMetaMessage *
+ccnxMetaMessage_Acquire(const CCNxMetaMessage *message)
+{
+ return ccnxTlvDictionary_Acquire(message);
+}
+
+void
+ccnxMetaMessage_Release(CCNxMetaMessage **messagePtr)
+{
+ ccnxTlvDictionary_Release(messagePtr);
+}
+
+void
+ccnxMetaMessage_Display(const CCNxMetaMessage *message, int indentation)
+{
+ ccnxTlvDictionary_Display(message, indentation);
+}
+
+bool
+ccnxMetaMessage_IsContentObject(const CCNxMetaMessage *message)
+{
+ return ccnxTlvDictionary_IsContentObject(message);
+}
+
+bool
+ccnxMetaMessage_IsInterest(const CCNxMetaMessage *message)
+{
+ return ccnxTlvDictionary_IsInterest(message);
+}
+
+bool
+ccnxMetaMessage_IsInterestReturn(const CCNxMetaMessage *message)
+{
+ return ccnxTlvDictionary_IsInterestReturn(message);
+}
+
+bool
+ccnxMetaMessage_IsControl(const CCNxMetaMessage *message)
+{
+ return ccnxTlvDictionary_IsControl(message);
+}
+
+bool
+ccnxMetaMessage_IsManifest(const CCNxMetaMessage *message)
+{
+ return ccnxTlvDictionary_IsManifest(message);
+}
+
+/**
+ * Given an iovec encoded version of a TlvDictionary, which is what we get when we call ccnxCodecTlvPacket_DictionaryEncode(),
+ * linearize it into a PARCBuffer so we can treat it as a PARCBuffer.
+ */
+static PARCBuffer *
+_iovecToParcBuffer(const CCNxCodecNetworkBufferIoVec *iovec)
+{
+ PARCBuffer *result = NULL;
+
+ size_t iovcnt = ccnxCodecNetworkBufferIoVec_GetCount((CCNxCodecNetworkBufferIoVec *) iovec);
+ const struct iovec *array = ccnxCodecNetworkBufferIoVec_GetArray((CCNxCodecNetworkBufferIoVec *) iovec);
+
+ size_t totalbytes = 0;
+ for (int i = 0; i < iovcnt; i++) {
+ totalbytes += array[i].iov_len;
+ }
+
+ result = parcBuffer_Allocate(totalbytes);
+ for (int i = 0; i < iovcnt; i++) {
+ parcBuffer_PutArray(result, array[i].iov_len, array[i].iov_base);
+ }
+
+ parcBuffer_Flip(result);
+
+
+ return result;
+}
+
+CCNxMetaMessage *
+ccnxMetaMessage_CreateFromWireFormatBuffer(PARCBuffer *rawMessage)
+{
+ CCNxMetaMessage *result = NULL;
+
+ CCNxWireFormatMessage *message = ccnxWireFormatMessage_Create(rawMessage);
+
+ if (message != NULL) {
+ // Get the dictionary from the ccnxWireFormatMessage.
+ CCNxTlvDictionary *dictionary = ccnxWireFormatMessage_GetDictionary(message);
+
+ // We have a partially unpacked dictionary now, but we need to more fully unpack it for our processing.
+ bool success = ccnxCodecTlvPacket_BufferDecode(rawMessage, dictionary);
+
+ if (success) {
+ result = (CCNxMetaMessage *) dictionary;
+ } else {
+ ccnxWireFormatMessage_Release(&message);
+ result = NULL;
+ }
+ }
+
+ return result;
+}
+
+PARCBuffer *
+ccnxMetaMessage_CreateWireFormatBuffer(CCNxMetaMessage *message, PARCSigner *signer)
+{
+ CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecTlvPacket_DictionaryEncode(message, signer);
+
+ // iovec has the wireformat version of 'interest' now.
+
+ PARCBuffer *result = _iovecToParcBuffer(iovec);
+
+ ccnxCodecNetworkBufferIoVec_Release(&iovec);
+
+ return result;
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/transport_MetaMessage.h b/libccnx-transport-rta/ccnx/transport/common/transport_MetaMessage.h
new file mode 100644
index 00000000..f7dda45c
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/transport_MetaMessage.h
@@ -0,0 +1,515 @@
+/*
+ * 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 ccnx_MetaMessage.h
+ * @brief A CCNx message suitable for sending through the CCNx Portal API.
+ *
+ * A CCNxMetaMessage encapsulates a CCN Interest, ContentObject, or Control message, and can
+ * be read from and written to the CCNx Portal API.
+ *
+ */
+#ifndef libccnx_ccnx_MetaMessage_h
+#define libccnx_ccnx_MetaMessage_h
+
+#include <ccnx/common/ccnx_Interest.h>
+#include <ccnx/common/ccnx_InterestReturn.h>
+#include <ccnx/common/ccnx_ContentObject.h>
+#include <ccnx/common/ccnx_Manifest.h>
+#include <ccnx/common/ccnx_WireFormatMessage.h>
+
+#include <ccnx/common/internal/ccnx_TlvDictionary.h>
+
+#include <ccnx/api/control/cpi_ControlMessage.h>
+
+/**
+ * @typedef CCNxMetaMessage
+ * @brief A CCNxMetaMessage encapsulates a CCN Interest, ContentObject, or Control message.
+ */
+typedef CCNxTlvDictionary CCNxMetaMessage;
+
+
+/**
+ * Create a `CCNxMetaMessage` instance containing the given {@link CCNxInterest}.
+ *
+ * A new reference to the `CCNxInterest` is created.
+ *
+ * @param [in] interest A pointer to a valid `CCNxInterest` instance.
+ *
+ * @return NULL The `CCNxInterest` is not valid, or memory could not be allocated.
+ * @return non-NULL A pointer to a `CCNxMetaMessage` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxMetaMessage_CreateFromInterest(interest);
+ * }
+ * @endcode
+ */
+CCNxMetaMessage *ccnxMetaMessage_CreateFromInterest(const CCNxInterest *interest);
+
+/**
+ * Create a `CCNxMetaMessage` instance containing the given {@link CCNxContentObject}.
+ *
+ * A new reference to the `CCNxContentObject` is created.
+ *
+ * @param [in] contentObject A pointer to a valid `CCNxContentObject` instance.
+ *
+ * @return NULL The `CCNxContentObject` is not valid, or memory could not be allocated.
+ * @return non-NULL A pointer to a `CCNxMetaMessage` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxMetaMessage_CreateFromContentObject(contentObject);
+ * }
+ * @endcode
+ */
+CCNxMetaMessage *ccnxMetaMessage_CreateFromContentObject(const CCNxContentObject *contentObject);
+
+/**
+ * Create a `CCNxMetaMessage` instance containing the given {@link CCNxControl}.
+ *
+ * A new reference to the `CCNxControl` is created.
+ *
+ * @param [in] control A pointer to a valid `CCNxControl` instance.
+ *
+ * @return NULL The `CCNxControl` is not valid, or memory could not be allocated.
+ * @return non-NULL A pointer to a `CCNxMetaMessage` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxMetaMessage_CreateFromControl(control);
+ * }
+ * @endcode
+ */
+CCNxMetaMessage *ccnxMetaMessage_CreateFromControl(const CCNxControl *control);
+
+/**
+ * Create a `CCNxMetaMessage` instance containing the given {@link CCNxManifest}.
+ *
+ * A new reference to the `CCNxManifest` is created.
+ *
+ * @param [in] control A pointer to a valid `CCNxManifest` instance.
+ *
+ * @return NULL The `CCNxManifest` is not valid, or memory could not be allocated.
+ * @return non-NULL A pointer to a `CCNxMetaMessage` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxMetaMessage_CreateFromManifest(manifest);
+ * }
+ * @endcode
+ */
+CCNxMetaMessage *ccnxMetaMessage_CreateFromManifest(const CCNxManifest *manifest);
+
+/**
+ * Print a human readable representation of the given `CCNxMetaMessage` instance.
+ *
+ * @param [in] message A pointer to the `CCNxMetaMessage` to display.
+ * @param [in] indentation The level of indentation to use to pretty-print the output.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxMetaMessage_CreateFromInterest(...);
+ *
+ * ccnxMetaMessage_Display(message, 0);
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ *
+ */
+void ccnxMetaMessage_Display(const CCNxMetaMessage *message, int indentation);
+
+/**
+ * Increase the number of references to a `CCNxMetaMessage`.
+ *
+ * Note that new `CCNxMetaMessage` is not created,
+ * only that the given `CCNxMetaMessage` reference count is incremented.
+ * Discard the reference by invoking {@link ccnxMetaMessage_Release}().
+ *
+ * @param [in] instance A pointer to a `CCNxMetaMessage` instance.
+ * @return The input @p instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ * CCNxMetaMessage *messageReference = ccnxMetaMessage_Acquire(message);
+ *
+ * ccnxMetaMessage_Release(&message);
+ * ccnxMetaMessage_Release(&messageReference);
+ * }
+ * @endcode
+ * @see `ccnxMetaMessage_Release`
+ */
+CCNxMetaMessage *ccnxMetaMessage_Acquire(const CCNxMetaMessage *instance);
+
+/**
+ * Determine whether a specified `CCNxMetaMessage` instance encapsulates an {@link CCNxInterest}.
+ *
+ * Returns `true` if the underlying message is a `CCNxInterest`.
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ *
+ * @return `true` If the underlying message is a `CCNxInterest`.
+ * @return `false` If the underlying message is not a `CCNxInterest`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ *
+ * if (ccnxMetaMessage_IsInterest(message)) {
+ * CCNxInterest *interest = ccnxMetaMessage_GetInterest(message);
+ * ...
+ * ccnxInterest_Release(&interest);
+ * }
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ * @see CCNxInterest
+ */
+bool ccnxMetaMessage_IsInterest(const CCNxMetaMessage *message);
+
+/**
+ * Determine whether a specified `CCNxMetaMessage` instance encapsulates an {@link CCNxInterestReturn}.
+ *
+ * Returns `true` if the underlying message is a `CCNxInterestReturn`.
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ *
+ * @return `true` If the underlying message is a `CCNxInterestReturn`.
+ * @return `false` If the underlying message is not a `CCNxInterestReturn`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ *
+ * if (ccnxMetaMessage_IsInterestReturn(message)) {
+ * CCNxInterestReturn *interestReturn = ccnxMetaMessage_GetInterestReturn(message);
+ *
+ * ...
+ * ccnxInterestReturn_Release(&interestReturn);
+ * }
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ * @see CCNxInterestReturn
+ */
+bool ccnxMetaMessage_IsInterestReturn(const CCNxMetaMessage *message);
+
+/**
+ * Determine whether a specified `CCNxMetaMessage` instance encapsulates a {@link CCNxContentObject}.
+ *
+ * Returns true if the underlying message is a `CCNxContentObject`.
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ *
+ * @return `true` If the underlying message is a `CCNxContentObject`.
+ * @return `false` If the underlying message is not a `CCNxContentObject`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ *
+ * if (ccnxMetaMessage_IsContentObject(message)) {
+ * CCNxContentObject *contentObject = ccnxMetaMessage_GetContentObject(message);
+ * ...
+ * ccnxContentObject_Release(&contentObject);
+ * }
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ * @see `CCNxContentObject`
+ */
+bool ccnxMetaMessage_IsContentObject(const CCNxMetaMessage *message);
+
+/**
+ * Determine whether a specified `CCNxMetaMessage` instance encapsulates a {@link CCNxControl}.
+ *
+ * Returns true if the underlying message is a `CCNxControl`.
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ *
+ * @return `true` If the underlying message is a `CCNxControl`.
+ * @return `false` If the underlying message is not a `CCNxControl`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ *
+ * if (ccnxMetaMessage_IsControl(message)) {
+ * CCNxControl *control = ccnxMetaMessage_GetControl(message);
+ * ...
+ * ccnxControl_Release(&control);
+ * }
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ * @see `CCNxControl`
+ */
+bool ccnxMetaMessage_IsControl(const CCNxMetaMessage *message);
+
+/**
+ * Determine whether a specified `CCNxMetaMessage` instance encapsulates a {@link CCNxManifest}.
+ *
+ * Returns true if the underlying message is a `CCNxManifest`.
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ *
+ * @return `true` If the underlying message is a `CCNxManifest`.
+ * @return `false` If the underlying message is not a `CCNxManifest`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ *
+ * if (ccnxMetaMessage_IsManifest(message)) {
+ * CCNxManifest *control = ccnxMetaMessage_GetManifest(message);
+ * ...
+ * ccnxManifest_Release(&manifest);
+ * }
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ * @see `CCNxManifest`
+ */
+bool ccnxMetaMessage_IsManifest(const CCNxMetaMessage *message);
+
+/**
+ * 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] messagePtr A pointer to a pointer to the instance to release.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ * CCNxMetaMessage *messageReference = ccnxMetaMessage_Acquire(message);
+ *
+ * ccnxMetaMessage_Release(&message);
+ * ccnxMetaMessage_Release(&messageReference);
+ * }
+ * @endcode
+ * @see {@link ccnxMetaMessage_Acquire}
+ */
+void ccnxMetaMessage_Release(CCNxMetaMessage **messagePtr);
+
+/**
+ * Return a new {@link CCNxContentObject} instance created from the `CCNxMetaMessage`.
+ *
+ * The newly created `CCNxContentObject` instance must eventually be released by calling {@link ccnxContentObject_Release}().
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ *
+ * @return A new `CCNxContentObject` instance, which must eventually be released by calling `ccnxContentObject_Release()`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ *
+ * if (ccnxMetaMessage_IsContentObject(message)) {
+ * CCNxContentObject *contentObject = ccnxMetaMessage_GetContentObject(message);
+ * ...
+ * ccnxContentObject_Release(&contentObject);
+ * }
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ *
+ * @see `ccnxContentObject_Release`
+ * @see {@link ccnxMetaMessage_IsContentObject}
+ */
+CCNxContentObject *ccnxMetaMessage_GetContentObject(const CCNxMetaMessage *message);
+
+/**
+ * Return a new {@link CCNxInterest} instance created from the `CCNxMetaMessage`.
+ *
+ * The newly created `CCNxInterest}\` instance must eventually be released by calling {@link ccnxInterest_Release}().
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ *
+ * @return A new `CCNxInterest` instance, which must eventually be released by calling `ccnxInterest_Release()`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ *
+ * if (ccnxMetaMessage_IsInterest(message)) {
+ * CCNxInterest *interest = ccnxMetaMessage_GetInterest(message);
+ * ...
+ * ccnxInterest_Release(&interest);
+ * }
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ *
+ * @see `ccnxInterest_Release`
+ * @see {@link ccnxMetaMessage_IsInterest}
+ */
+CCNxInterest *ccnxMetaMessage_GetInterest(const CCNxMetaMessage *message);
+
+/**
+ * Return a new {@link CCNxInterestReturn} instance created from the `CCNxMetaMessage`.
+ *
+ * The newly created `CCNxInterestReturn}\` instance must eventually be released by calling {@link ccnxInterest_Release}().
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ *
+ * @return A new `CCNxInterest` instance, which must eventually be released by calling `ccnxInterestReturn_Release()`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ *
+ * if (ccnxMetaMessage_IsInterestReturn(message)) {
+ * CCNxInterestReturn *interestReturn = ccnxMetaMessage_GetInterestReturn(message);
+ *
+ * ...
+ * ccnxInterestReturn_Release(&interestReturn);
+ * }
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ *
+ * @see `ccnxInterest_Release`
+ * @see {@link ccnxMetaMessage_IsInterest}
+ */
+CCNxInterest *ccnxMetaMessage_GetInterestReturn(const CCNxMetaMessage *message);
+
+/**
+ * Return a new {@link CCNxControl} instance created from the `CCNxMetaMessage`.
+ *
+ * The newly created `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}().
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ *
+ * @return A new `CCNxControl` instance, which must eventually be released by calling `ccnxControl_Release()`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ *
+ * if (ccnxMetaMessage_IsControl(message)) {
+ * CCNxControl *control = ccnxMetaMessage_GetControl(message);
+ * ...
+ * ccnxControl_Release(&control);
+ * }
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ *
+ * @see `ccnxControl_Release`
+ * @see {@link ccnxMetaMessage_IsControl}
+ */
+CCNxControl *ccnxMetaMessage_GetControl(const CCNxMetaMessage *message);
+
+/**
+ * Return a new {@link CCNxManifest} instance created from the `CCNxMetaMessage`.
+ *
+ * The newly created `CCNxManifest` instance must eventually be released by calling {@link ccnxManifest_Release}().
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ *
+ * @return A new `CCNxManifest` instance, which must eventually be released by calling `ccnxManifest_Release()`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *message = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ *
+ * if (ccnxMetaMessage_IsManifest(message)) {
+ * CCNxManifest *manifest = ccnxMetaMessage_GetManifest(message);
+ * ...
+ * ccnxManifest_Release(&manifest);
+ * }
+ *
+ * ccnxMetaMessage_Release(&message);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxManifest_Release}
+ * @see {@link ccnxMetaMessage_IsManifest}
+ */
+CCNxManifest *ccnxMetaMessage_GetManifest(const CCNxMetaMessage *message);
+
+/**
+ * Return a new {@link CCNxMetaMessage} instance created from a wire format message
+ *
+ * The newly created `CCNxMetaMessage` instance must eventually be released by calling {@link CCNxMetaMessage_Release}().
+ *
+ * @param [in] message A pointer to a `PARCBuffer` instance containing a wire format message.
+ *
+ * @return A new `CCNxMetaMessage` instance, which must eventually be released by calling `ccnxMetaMessage_Release()`.
+ *
+ * Example:
+ * @code
+ * {
+ * }
+ * @endcode
+ *
+ */
+CCNxMetaMessage *ccnxMetaMessage_CreateFromWireFormatBuffer(PARCBuffer *rawMessage);
+
+/**
+ * Return a new {@link PARCBuffer} instance containing an encodeded wire format message created
+ * from the source `CCNxMetaMessage`.
+ *
+ * The newly created `PARCBuffer` instance must eventually be released by calling {@link parcBuffer_Release}().
+ *
+ * @param [in] message A pointer to a `CCNxMetaMessage` instance.
+ * @param [in] signer A pointer to a `PARCSigner` instance.
+ *
+ * @return A new `PARCBuffer` instance, which must eventually be released by calling `parcBuffer_Release()`.
+ *
+ * Example:
+ * @code
+ * {
+ * }
+ * @endcode
+ *
+ */
+PARCBuffer *ccnxMetaMessage_CreateWireFormatBuffer(CCNxMetaMessage *message, PARCSigner *signer);
+
+#endif // libccnx_ccnx_MetaMessage_h
diff --git a/libccnx-transport-rta/ccnx/transport/common/transport_Stack.c b/libccnx-transport-rta/ccnx/transport/common/transport_Stack.c
new file mode 100644
index 00000000..1d529710
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/transport_Stack.c
@@ -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.
+ */
+
+#include <config.h>
+
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_DisplayIndented.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <transport_Stack.h>
+
+struct TransportStack {
+ int topFD;
+ int bottomFD;
+};
+
+static void
+_transportStack_Finalize(TransportStack **instancePtr)
+{
+ assertNotNull(instancePtr, "Parameter must be a non-null pointer to a TransportStack pointer.");
+ TransportStack *instance = *instancePtr;
+
+ transportStack_OptionalAssertValid(instance);
+
+ /* cleanup the instance fields here */
+}
+
+parcObject_ImplementAcquire(transportStack, TransportStack);
+
+parcObject_ImplementRelease(transportStack, TransportStack);
+
+parcObject_ExtendPARCObject(TransportStack, _transportStack_Finalize, transportStack_Copy, transportStack_ToString, transportStack_Equals, transportStack_Compare, transportStack_HashCode, transportStack_ToJSON);
+
+
+void
+transportStack_AssertValid(const TransportStack *instance)
+{
+ assertTrue(transportStack_IsValid(instance),
+ "TransportStack is not valid.");
+}
+
+
+TransportStack *
+transportStack_Create(void)
+{
+ TransportStack *result = parcObject_CreateInstance(TransportStack);
+
+ return result;
+}
+
+int
+transportStack_Compare(const TransportStack *instance, const TransportStack *other)
+{
+ int result = 0;
+
+ return result;
+}
+
+TransportStack *
+transportStack_Copy(const TransportStack *original)
+{
+ TransportStack *result = NULL;
+
+ return result;
+}
+
+void
+transportStack_Display(const TransportStack *instance, int indentation)
+{
+ parcDisplayIndented_PrintLine(indentation, "TransportStack@%p {", instance);
+ /* Call Display() functions for the fields here. */
+ parcDisplayIndented_PrintLine(indentation, "}");
+}
+
+bool
+transportStack_Equals(const TransportStack *x, const TransportStack *y)
+{
+ bool result = false;
+
+ if (x == y) {
+ result = true;
+ } else if (x == NULL || y == NULL) {
+ result = false;
+ } else {
+ /* perform instance specific equality tests here. */
+ }
+
+ return result;
+}
+
+PARCHashCode
+transportStack_HashCode(const TransportStack *instance)
+{
+ PARCHashCode result = 0;
+
+ return result;
+}
+
+bool
+transportStack_IsValid(const TransportStack *instance)
+{
+ bool result = false;
+
+ if (instance != NULL) {
+ result = true;
+ }
+
+ return result;
+}
+
+PARCJSON *
+transportStack_ToJSON(const TransportStack *instance)
+{
+ PARCJSON *result = parcJSON_Create();
+
+ return result;
+}
+
+char *
+transportStack_ToString(const TransportStack *instance)
+{
+ char *result = parcMemory_Format("TransportStack@%p\n", instance);
+
+ return result;
+}
diff --git a/libccnx-transport-rta/ccnx/transport/common/transport_Stack.h b/libccnx-transport-rta/ccnx/transport/common/transport_Stack.h
new file mode 100644
index 00000000..6b38c2a5
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/transport_Stack.h
@@ -0,0 +1,354 @@
+/*
+ * 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 transport_Stack.h
+ * @brief <#Brief Description#>
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_ccnx_transport_Stack
+#define libccnx_ccnx_transport_Stack
+#include <stdbool.h>
+
+#include <parc/algol/parc_JSON.h>
+#include <parc/algol/parc_HashCode.h>
+
+struct TransportStack;
+typedef struct TransportStack TransportStack;
+
+/**
+ * Increase the number of references to a `TransportStack` instance.
+ *
+ * Note that new `TransportStack` is not created,
+ * only that the given `TransportStack` reference count is incremented.
+ * Discard the reference by invoking `transportStack_Release`.
+ *
+ * @param [in] instance A pointer to a valid TransportStack instance.
+ *
+ * @return The same value as @p instance.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ *
+ * TransportStack *b = transportStack_Acquire();
+ *
+ * transportStack_Release(&a);
+ * transportStack_Release(&b);
+ * }
+ * @endcode
+ */
+TransportStack *transportStack_Acquire(const TransportStack *instance);
+
+#ifdef LIBRTA_DISABLE_VALIDATION
+# define transportStack_OptionalAssertValid(_instance_)
+#else
+# define transportStack_OptionalAssertValid(_instance_) transportStack_AssertValid(_instance_)
+#endif
+
+/**
+ * Assert that the given `TransportStack` instance is valid.
+ *
+ * @param [in] instance A pointer to a valid TransportStack instance.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ *
+ * transportStack_AssertValid(a);
+ *
+ * printf("Instance is valid.\n");
+ *
+ * transportStack_Release(&b);
+ * }
+ * @endcode
+ */
+void transportStack_AssertValid(const TransportStack *instance);
+
+/**
+ * Create an instance of TransportStack
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @return non-NULL A pointer to a valid TransportStack instance.
+ * @return NULL An error occurred.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ *
+ * transportStack_Release(&a);
+ * }
+ * @endcode
+ */
+TransportStack *transportStack_Create(void);
+
+/**
+ * Compares @p instance with @p other for order.
+ *
+ * Returns a negative integer, zero, or a positive integer as @p instance
+ * is less than, equal to, or greater than @p other.
+ *
+ * @param [in] instance A pointer to a valid TransportStack instance.
+ * @param [in] other A pointer to a valid TransportStack instance.
+ *
+ * @return <0 Instance is less than @p other.
+ * @return 0 Instance a and instance b compare the same.
+ * @return >0 Instance a is greater than instance b.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ * TransportStack *b = transportStack_Create();
+ *
+ * if (transportStack_Compare(a, b) == 0) {
+ * printf("Instances are equal.\n");
+ * }
+ *
+ * transportStack_Release(&a);
+ * transportStack_Release(&b);
+ * }
+ * @endcode
+ *
+ * @see transportStack_Equals
+ */
+int transportStack_Compare(const TransportStack *instance, const TransportStack *other);
+
+/**
+ * Create an independent copy the given `PARCBuffer`
+ *
+ * A new buffer is created as a complete copy of the original.
+ *
+ * @param [in] original A pointer to a valid TransportStack instance.
+ *
+ * @return NULL Memory could not be allocated.
+ * @return non-NULL A pointer to a new `TransportStack` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ *
+ * TransportStack *copy = transportStack_Copy(&b);
+ *
+ * transportStack_Release(&b);
+ * transportStack_Release(&copy);
+ * }
+ * @endcode
+ */
+TransportStack *transportStack_Copy(const TransportStack *original);
+
+/**
+ * Print a human readable representation of the given `TransportStack`.
+ *
+ * @param [in] instance A pointer to a valid TransportStack instance.
+ * @param [in] indentation The indentation level to use for printing.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ *
+ * transportStack_Display(a, 0);
+ *
+ * transportStack_Release(&a);
+ * }
+ * @endcode
+ */
+void transportStack_Display(const TransportStack *instance, int indentation);
+
+/**
+ * Determine if two `TransportStack` instances are equal.
+ *
+ * The following equivalence relations on non-null `TransportStack` instances are maintained: *
+ * * It is reflexive: for any non-null reference value x, `transportStack_Equals(x, x)` must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y, `transportStack_Equals(x, y)` must return true if and only if
+ * `transportStack_Equals(y x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `transportStack_Equals(x, y)` returns true and
+ * `transportStack_Equals(y, z)` returns true,
+ * then `transportStack_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple invocations of `transportStack_Equals(x, y)`
+ * consistently return true or consistently return false.
+ *
+ * * For any non-null reference value x, `transportStack_Equals(x, NULL)` must return false.
+ *
+ * @param [in] x A pointer to a valid TransportStack instance.
+ * @param [in] y A pointer to a valid TransportStack instance.
+ *
+ * @return true The instances x and y are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ * TransportStack *b = transportStack_Create();
+ *
+ * if (transportStack_Equals(a, b)) {
+ * printf("Instances are equal.\n");
+ * }
+ *
+ * transportStack_Release(&a);
+ * transportStack_Release(&b);
+ * }
+ * @endcode
+ * @see transportStack_HashCode
+ */
+bool transportStack_Equals(const TransportStack *x, const TransportStack *y);
+
+/**
+ * Returns a hash code value for the given instance.
+ *
+ * The general contract of `HashCode` is:
+ *
+ * Whenever it is invoked on the same instance more than once during an execution of an application,
+ * the `HashCode` function must consistently return the same value,
+ * provided no information used in a corresponding comparisons on the instance is modified.
+ *
+ * This value need not remain consistent from one execution of an application to another execution of the same application.
+ * If two instances are equal according to the {@link transportStack_Equals} method,
+ * then calling the {@link transportStack_HashCode} method on each of the two instances must produce the same integer result.
+ *
+ * It is not required that if two instances are unequal according to the
+ * {@link transportStack_Equals} function,
+ * then calling the `transportStack_HashCode`
+ * method on each of the two objects must produce distinct integer results.
+ *
+ * @param [in] instance A pointer to a valid TransportStack instance.
+ *
+ * @return The hashcode for the given instance.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ *
+ * PARCHashCode hashValue = transportStack_HashCode(buffer);
+ * transportStack_Release(&a);
+ * }
+ * @endcode
+ */
+PARCHashCode transportStack_HashCode(const TransportStack *instance);
+
+/**
+ * Determine if an instance of `TransportStack` 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 valid TransportStack instance.
+ *
+ * @return true The instance is valid.
+ * @return false The instance is not valid.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ *
+ * if (transportStack_IsValid(a)) {
+ * printf("Instance is valid.\n");
+ * }
+ *
+ * transportStack_Release(&a);
+ * }
+ * @endcode
+ *
+ */
+bool transportStack_IsValid(const TransportStack *instance);
+
+/**
+ * Release a previously acquired reference to the given `TransportStack` 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] instancePtr A pointer to a pointer to the instance to release.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ *
+ * transportStack_Release(&a);
+ * }
+ * @endcode
+ */
+void transportStack_Release(TransportStack **instancePtr);
+
+/**
+ * Create a `PARCJSON` instance (representation) of the given object.
+ *
+ * @param [in] instance A pointer to a valid TransportStack instance.
+ *
+ * @return NULL Memory could not be allocated to contain the `PARCJSON` instance.
+ * @return non-NULL An allocated C string that must be deallocated via parcMemory_Deallocate().
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ *
+ * PARCJSON *json = transportStack_ToJSON(a);
+ *
+ * printf("JSON representation: %s\n", parcJSON_ToString(json));
+ * parcJSON_Release(&json);
+ *
+ * transportStack_Release(&a);
+ * }
+ * @endcode
+ */
+PARCJSON *transportStack_ToJSON(const TransportStack *instance);
+
+/**
+ * Produce a null-terminated string representation of the specified `TransportStack`.
+ *
+ * The result must be freed by the caller via {@link parcMemory_Deallocate}.
+ *
+ * @param [in] instance A pointer to a valid TransportStack instance.
+ *
+ * @return NULL Cannot allocate memory.
+ * @return non-NULL A pointer to an allocated, null-terminated C string that must be deallocated via {@link parcMemory_Deallocate}.
+ *
+ * Example:
+ * @code
+ * {
+ * TransportStack *a = transportStack_Create();
+ *
+ * char *string = transportStack_ToString(a);
+ *
+ * transportStack_Release(&a);
+ *
+ * parcMemory_Deallocate(&string);
+ * }
+ * @endcode
+ *
+ * @see transportStack_Display
+ */
+char *transportStack_ToString(const TransportStack *instance);
+#endif
diff --git a/libccnx-transport-rta/ccnx/transport/common/transport_private.h b/libccnx-transport-rta/ccnx/transport/common/transport_private.h
new file mode 100644
index 00000000..a06b74be
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/common/transport_private.h
@@ -0,0 +1,38 @@
+/*
+ * 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 transport_private.h
+ * @brief <#Brief Description#>
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef Libccnx_transport_private_h
+#define Libccnx_transport_private_h
+
+#include <ccnx/transport/common/transport_MetaMessage.h>
+#include <ccnx/transport/common/ccnx_TransportConfig.h>
+
+struct transport_operations {
+ void* (*Create)(void);
+ int (*Open)(void *ctx, CCNxTransportConfig *transportConfig);
+ int (*Send)(void *ctx, int desc, CCNxMetaMessage *msg, const struct timeval *timeout);
+ TransportIOStatus (*Recv)(void *ctx, int desc, CCNxMetaMessage **msg, const struct timeval *timeout);
+ int (*Close)(void *ctx, int desc);
+ int (*Destroy)(void **ctx);
+ int (*PassCommand)(void *ctx, void *command);
+};
+#endif // Libccnx_transport_private_h
diff --git a/libccnx-transport-rta/ccnx/transport/librta_About.c b/libccnx-transport-rta/ccnx/transport/librta_About.c
new file mode 100644
index 00000000..ed14330c
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/librta_About.c
@@ -0,0 +1,44 @@
+// DO NOT EDIT THIS FILE. IT IS AUTOMATICALLY GENERATED.
+// longbow-generate-about 1.0.20170215.54ef86fe 2017-02-15T09:29:05Z
+
+#include "librta_About.h"
+
+const char *librta_What = "@(#)" "librta " RELEASE_VERSION " 2017-02-20T14:19:07.130301"
+ "@(#)" "\tCopyright (c) 2017 Cisco and/or its affiliates.";
+
+const char *
+librtaAbout_Name(void)
+{
+ return "librta";
+}
+
+const char *
+librtaAbout_Version(void)
+{
+ return RELEASE_VERSION;
+}
+
+const char *
+librtaAbout_About(void)
+{
+ return "librta "RELEASE_VERSION " 2017-02-20T14:19:07.130301" "\nCopyright (c) 2017 Cisco and/or its affiliates.\n";
+}
+
+const char *
+librtaAbout_MiniNotice(void)
+{
+ return "Copyright (c) 2017 Cisco and/or its affiliates.\n";
+}
+
+const char *
+librtaAbout_ShortNotice(void)
+{
+ return "Copyright (c) 2017 Cisco and/or its affiliates.\n";
+}
+
+const char *
+librtaAbout_LongNotice(void)
+{
+ return "Copyright (c) 2017 Cisco and/or its affiliates.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at:\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n";
+}
+
diff --git a/libccnx-transport-rta/ccnx/transport/librta_About.h b/libccnx-transport-rta/ccnx/transport/librta_About.h
new file mode 100644
index 00000000..91092b13
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/librta_About.h
@@ -0,0 +1,54 @@
+// DO NOT EDIT THIS FILE. IT IS AUTOMATICALLY GENERATED.
+// longbow-generate-about 1.0.20170215.54ef86fe 2017-02-15T09:29:05Z
+
+#ifndef librta_About_h
+#define librta_About_h
+/**
+ * Embedded string containing information for the what(1) command.
+ *
+ */
+extern const char *librta_What;
+
+/**
+ * Return the name as a C string.
+ *
+ * @return The name as a C string.
+ */
+const char *librtaAbout_Name(void);
+
+/**
+ * Return the version as a C string.
+ *
+ * @return The version as a C string.
+ */
+const char *librtaAbout_Version(void);
+
+/**
+ * Return the About text as a C string.
+ *
+ * @return The About text as a C string.
+ */
+const char *librtaAbout_About(void);
+
+/**
+ * Return the minimum copyright notice as a C string.
+ *
+ * @return The minimum copyright notice as a C string.
+ */
+const char *librtaAbout_MiniNotice(void);
+
+/**
+ * Return the short copyright notice as a C string.
+ *
+ * @return The short copyright notice as a C string.
+ */
+const char *librtaAbout_ShortNotice(void);
+
+/**
+ * Return the long copyright notice as a C string.
+ *
+ * @return The long copyright notice as a C string.
+ */
+const char *librtaAbout_LongNotice(void);
+
+#endif // librta_About_h
diff --git a/libccnx-transport-rta/ccnx/transport/test_tools/bent_pipe.c b/libccnx-transport-rta/ccnx/transport/test_tools/bent_pipe.c
new file mode 100644
index 00000000..bf2c68db
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/test_tools/bent_pipe.c
@@ -0,0 +1,850 @@
+/*
+ * 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 <LongBow/debugging.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <math.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_EventTimer.h>
+#include <parc/algol/parc_EventQueue.h>
+#include <parc/algol/parc_EventBuffer.h>
+#include <parc/algol/parc_EventSocket.h>
+#include "bent_pipe.h"
+
+#define MAX_CONN 10
+
+struct packet_wrapper {
+ struct timeval deadline;
+ int ingress_fd;
+ uint8_t *pbuff;
+ size_t pbuff_len;
+
+ TAILQ_ENTRY(packet_wrapper) list;
+};
+
+struct bentpipe_conn {
+ int client_fd;
+ PARCEventQueue *bev;
+ BentPipeState *parent;
+
+ // after reading a header, this is how long the next message is
+ size_t msg_length;
+
+ // queue to send stuff up the connection
+ unsigned bytes_in_queue;
+ unsigned count_in_queue;
+ struct timeval last_deadline;
+ TAILQ_HEAD(, packet_wrapper) output_queue;
+
+ PARCEventTimer *timer_event;
+};
+
+struct bentpipe_state {
+ char *local_name;
+ PARCEventScheduler *base;
+ PARCEventSocket *listenerUnix;
+
+ struct bentpipe_conn conns[MAX_CONN];
+ unsigned conn_count;
+
+ pthread_t router_thread;
+
+ bool use_params;
+ double loss_rate;
+ unsigned buffer_bytes;
+ double mean_sec_delay;
+ double bytes_per_sec;
+
+ pthread_mutex_t startup_mutex;
+ pthread_cond_t startup_cond;
+ unsigned magic;
+ bool startup_running;
+
+ // used to signal into the thread to stop
+ bool killme;
+ PARCEventTimer *keep_alive_event;
+
+ // these track the state of sigpipe before we
+ // go into a write, so we can re-set the state
+
+ // sigpipe_pending indicates that it was pending before our call
+ bool sigpipe_pending;
+
+ // sigpipe_blocked means that it was blocked before our call, so
+ // don't unblock it when we're done
+ bool sigpipe_blocked;
+
+ // debugging output
+ bool chattyOutput;
+};
+
+typedef struct {
+ uint32_t pid;
+ uint32_t fd;
+ uint32_t length;
+ uint32_t pad; // pad out to 16 bytes
+} __attribute__ ((packed)) localhdr;
+
+static int setup_local(BentPipeState*bp);
+static void
+listener_cb(int fd, struct sockaddr *sa, int socklen, void *user_data);
+
+static void *run_bentpipe(void *arg);
+
+static void conn_readcb(PARCEventQueue *bev, PARCEventType event, void *connection);
+static void reflect(struct bentpipe_conn *conn, uint8_t *pbuff, size_t pbuff_len);
+
+static void
+queue_with_delay(struct bentpipe_conn *conn, uint8_t *pbuff, size_t pbuff_len, int i);
+
+static void timer_cb(int fd, PARCEventType what, void *user_data);
+static void set_timer(struct bentpipe_conn *conn, struct timeval delay);
+static void keepalive_cb(int fd, PARCEventType what, void *user_data);
+void conn_errorcb(PARCEventQueue *bev, PARCEventQueueEventType events, void *user_framework);
+
+#define MAGIC 0x01020304
+
+// ===============
+
+static void
+lock_bentpipe(BentPipeState *bp)
+{
+ int res = pthread_mutex_lock(&bp->startup_mutex);
+ assertTrue(res == 0, "error from pthread_mutex_lock: %d", res);
+}
+
+static void
+unlock_bentpipe(BentPipeState *bp)
+{
+ int res = pthread_mutex_unlock(&bp->startup_mutex);
+ assertTrue(res == 0, "error from pthread_mutex_unlock: %d", res);
+}
+
+static void
+wait_bentpipe(BentPipeState *bp)
+{
+ int res = pthread_cond_wait(&bp->startup_cond, &bp->startup_mutex);
+ assertTrue(res == 0, "error from pthread_mutex_unlock: %d", res);
+}
+
+static void
+signal_bentpipe(BentPipeState *bp)
+{
+ int res = pthread_cond_signal(&bp->startup_cond);
+ assertTrue(res == 0, "error from pthread_mutex_unlock: %d", res);
+}
+
+/**
+ * if SIGPIPE was pending before we are called, don't do anything.
+ * Otherwise, mask SIGPIPE
+ */
+static void
+capture_sigpipe(BentPipeState *bp)
+{
+#if !defined(SO_NOSIGPIPE)
+ sigset_t pending;
+ sigemptyset(&pending);
+ sigpending(&pending);
+ bp->sigpipe_pending = sigismember(&pending, SIGPIPE);
+ if (!bp->sigpipe_pending) {
+ sigset_t sigpipe_mask;
+ sigemptyset(&sigpipe_mask);
+ sigaddset(&sigpipe_mask, SIGPIPE);
+
+ sigset_t blocked;
+ sigemptyset(&blocked);
+ pthread_sigmask(SIG_BLOCK, &sigpipe_mask, &blocked);
+ bp->sigpipe_blocked = sigismember(&blocked, SIGPIPE);
+ }
+#endif
+}
+
+static void
+release_sigpipe(BentPipeState *bp)
+{
+#if !defined(SO_NOSIGPIPE)
+ // If sigpipe was previously pending, we didnt block it, so
+ // nothing new to do
+ if (!bp->sigpipe_pending) {
+ sigset_t pending;
+
+ sigset_t sigpipe_mask;
+ sigemptyset(&sigpipe_mask);
+ sigaddset(&sigpipe_mask, SIGPIPE);
+
+ sigemptyset(&pending);
+ sigpending(&pending);
+
+ if (!bp->sigpipe_blocked) {
+ pthread_sigmask(SIG_UNBLOCK, &sigpipe_mask, NULL);
+ }
+ }
+#endif
+}
+
+BentPipeState *
+bentpipe_Create(const char *local_name)
+{
+ assertNotNull(local_name, "Parameter local_name must be non-null");
+
+ struct timeval keepalive_timeout = { .tv_sec = 0, .tv_usec = 500000 };
+
+ BentPipeState *bp = parcMemory_AllocateAndClear(sizeof(struct bentpipe_state));
+ assertNotNull(bp, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(struct bentpipe_state));
+
+ bp->local_name = parcMemory_StringDuplicate((char *) local_name, 1024);
+ bp->conn_count = 0;
+ bp->base = parcEventScheduler_Create();
+ bp->use_params = false;
+ bp->chattyOutput = false;
+
+ if (!bp->base) {
+ fprintf(stderr, "Could not initialize PARCEventScheduler!\n");
+ return NULL;
+ }
+
+ pthread_mutex_init(&bp->startup_mutex, NULL);
+ pthread_cond_init(&bp->startup_cond, NULL);
+ bp->startup_running = false;
+ bp->magic = MAGIC;
+ bp->keep_alive_event = parcEventTimer_Create(bp->base, PARCEventType_Persist, keepalive_cb, bp);
+ parcEventTimer_Start(bp->keep_alive_event, &keepalive_timeout);
+
+ for (int i = 0; i < MAX_CONN; i++) {
+ bp->conns[i].bytes_in_queue = 0;
+ TAILQ_INIT(&bp->conns[i].output_queue);
+ bp->conns[i].timer_event = parcEventTimer_Create(bp->base, 0, timer_cb, &bp->conns[i]);
+ }
+ setup_local(bp);
+ capture_sigpipe(bp);
+ return bp;
+}
+
+void
+bentpipe_Destroy(BentPipeState **bpPtr)
+{
+ BentPipeState *bp;
+ assertNotNull(bpPtr, "%s got null double pointer\n", __func__);
+ bp = *bpPtr;
+ assertNotNull(bp, "%s got null dereference\n", __func__);
+
+ assertFalse(bp->startup_running, "calling destroy on a running bentpipe\n");
+
+ int i;
+ for (i = 0; i < MAX_CONN; i++) {
+ if (bp->conns[i].client_fd > 0) {
+ // this closes it too
+ parcEventQueue_Destroy(&(bp->conns[i].bev));
+ bp->conns[i].client_fd = 0;
+ }
+ parcEventTimer_Destroy(&(bp->conns[i].timer_event));
+ }
+
+ parcEventTimer_Destroy(&(bp->keep_alive_event));
+ parcEventSocket_Destroy(&(bp->listenerUnix));
+ parcEventScheduler_Destroy(&(bp->base));
+ bp->conn_count = 0;
+
+ int failure = unlink(bp->local_name);
+ if (failure) {
+ printf("Error unlinking '%s': (%d) %s\n", bp->local_name, errno, strerror(errno));
+ }
+
+ release_sigpipe(bp);
+ parcMemory_Deallocate((void **) &(bp->local_name));
+ parcMemory_Deallocate((void **) &bp);
+}
+
+void
+bentpipe_SetChattyOutput(BentPipeState *bp, bool chattyOutput)
+{
+ bp->chattyOutput = chattyOutput;
+}
+
+int
+bentpipe_Start(BentPipeState *bp)
+{
+ assertFalse(bp->startup_running, "bentpipe_Start already running");
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+
+ assertTrue(pthread_create(&bp->router_thread, &attr, run_bentpipe, bp) == 0, "pthread_create failed.");
+// if (pthread_create(&bp->router_thread, &attr, run_bentpipe, bp) != 0) {
+// perror("pthread_create router thread");
+// abort();
+// }
+
+ lock_bentpipe(bp);
+ while (!bp->startup_running) {
+ wait_bentpipe(bp);
+ }
+ unlock_bentpipe(bp);
+
+ return 0;
+}
+
+int
+bentpipe_Stop(BentPipeState *bp)
+{
+ assertTrue(bp->magic == MAGIC, "Magic is not magic value! got %08X", bp->magic);
+ assertTrue(bp->startup_running, "Calling stop on a stopped bentpipe\n");
+
+ bp->killme = true;
+
+ // wait until exist
+ lock_bentpipe(bp);
+ while (bp->startup_running) {
+ wait_bentpipe(bp);
+ }
+ unlock_bentpipe(bp);
+
+ return 0;
+}
+
+// ==================================
+
+void
+listener_errorcb(PARCEventScheduler *base, int error, char *errorString, void *addr_unix)
+{
+ fprintf(stderr, "Got an error %d (%s) on the listener. "
+ "Shutting down.\n", error, errorString);
+
+ parcEventScheduler_Stop(base, NULL);
+}
+
+static int
+setup_local(BentPipeState*bp)
+{
+ // cleanup anything left on file system
+ unlink(bp->local_name);
+
+ struct sockaddr_un addr_unix;
+ memset(&addr_unix, 0, sizeof(addr_unix));
+
+ addr_unix.sun_family = PF_UNIX;
+ strcpy(addr_unix.sun_path, bp->local_name);
+
+ if (bp->chattyOutput) {
+ printf("bent_pipe Creating '%s'", bp->local_name);
+ }
+
+ bp->listenerUnix = parcEventSocket_Create(bp->base,
+ listener_cb,
+ listener_errorcb,
+ (void *) bp,
+ (struct sockaddr*) &addr_unix,
+ sizeof(addr_unix));
+
+ assertNotNull(bp->listenerUnix, "parcEventSocket_Create failed: unix %s", bp->local_name);
+
+ return 0;
+}
+
+static void *
+run_bentpipe(void *arg)
+{
+ BentPipeState *bp = (BentPipeState *) arg;
+
+ if (bp->chattyOutput) {
+ printf("%s starting\n", __func__);
+ }
+
+ // The keepalive timer will signal that we have started running
+ parcEventScheduler_Start(bp->base, PARCEventSchedulerDispatchType_Blocking);
+
+ if (bp->chattyOutput) {
+ printf("%s exiting\n", __func__);
+ }
+
+ lock_bentpipe(bp);
+ bp->startup_running = false;
+ signal_bentpipe(bp);
+ unlock_bentpipe(bp);
+
+ pthread_exit(NULL);
+}
+
+static struct bentpipe_conn *
+allocate_connection(BentPipeState *bp)
+{
+ if (bp->conn_count == MAX_CONN) {
+ printf("allocate_connection: connection count is %d, maximum count is %d\n",
+ bp->conn_count, MAX_CONN);
+ return NULL;
+ }
+
+ for (int i = 0; i < MAX_CONN; i++) {
+ if (bp->conns[i].client_fd == 0) {
+ bp->conn_count++;
+ return &bp->conns[i];
+ }
+ }
+
+ // should never get here
+ abort();
+}
+
+static void
+deallocate_connection(BentPipeState *bp, struct bentpipe_conn *conn)
+{
+ assertTrue(bp->conn_count > 0, "invalid state, called deallocate_connection when conn_count is zero");
+
+ conn->client_fd = 0;
+
+ if (bp->chattyOutput) {
+ printf("destroying connection %p eventqueue %p\n", (void *) conn, (void *) conn->bev);
+ }
+
+ parcEventQueue_Disable(conn->bev, PARCEventType_Read);
+ parcEventQueue_Destroy(&(conn->bev));
+
+ // this unschedules any callbacks, but the timer is still allocated
+ // timer_event is freed in bentPipe_Destroy()
+ parcEventTimer_Stop(conn->timer_event);
+
+ bp->conn_count--;
+}
+
+/*
+ * Server accepts a new client
+ */
+static void
+listener_cb(int fd, struct sockaddr *sa, int socklen, void *user_data)
+{
+ BentPipeState *bp = (BentPipeState *) user_data;
+
+ // allocate a connection
+ struct bentpipe_conn *conn = allocate_connection(bp);
+
+ conn->parent = bp;
+ conn->client_fd = 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);
+
+#if defined(SO_NOSIGPIPE)
+ int set = 1;
+ setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &set, sizeof(int));
+#endif
+
+ conn->bev = parcEventQueue_Create(bp->base, fd, PARCEventQueueOption_CloseOnFree | PARCEventQueueOption_DeferCallbacks);
+ if (!conn->bev) {
+ fprintf(stderr, "Error constructing parcEventQueue!");
+ parcEventScheduler_Abort(bp->base);
+ return;
+ }
+
+ parcEventQueue_SetCallbacks(conn->bev, conn_readcb, NULL, conn_errorcb, (void *) conn);
+ parcEventQueue_Enable(conn->bev, PARCEventType_Read);
+
+ if (bp->chattyOutput) {
+ printf("%s accepted connection on fd %d conn %p eventqueue %p\n",
+ __func__, fd, (void *) conn, (void *) conn->bev);
+ }
+}
+
+/*
+ * Return 1 if we read an entire message and we can try another one.
+ * return 0 if we're waiting for bytes.
+ */
+static int
+single_read(PARCEventQueue *bev, struct bentpipe_conn *conn)
+{
+ BentPipeState *bp = conn->parent;
+ PARCEventBuffer *input = parcEventBuffer_GetQueueBufferInput(bev);
+ size_t read_length = parcEventBuffer_GetLength(input);
+ int ret = 0;
+
+ if (bp->chattyOutput) {
+ printf("single_read: connid %d read %zu bytes\n", conn->client_fd, read_length);
+ }
+
+ if (read_length == 0) {
+ // 0 length means EOF, close the connection
+ if (bp->chattyOutput) {
+ printf("single_read: connid %d read_length %zu, EOF, closing connection\n",
+ conn->client_fd,
+ read_length);
+ }
+ deallocate_connection(conn->parent, conn);
+ parcEventBuffer_Destroy(&input);
+ return 0;
+ }
+
+ // head we read the message header, which sets the message length?
+ if (conn->msg_length == 0) {
+ // did we read a whole header?
+ if (read_length >= sizeof(localhdr)) {
+ // yes we did, we can now read the message header then try to read the whole message
+ // Note that this does not remove the header from the buffer
+
+ localhdr *msg_hdr = (localhdr *) parcEventBuffer_Pullup(input, sizeof(localhdr));
+ conn->msg_length = msg_hdr->length;
+
+ assertTrue(conn->msg_length > 0, "single_read: msg_hdr length is 0!");
+ assertTrue(conn->msg_length < 64000, "single_read: msg_hdr length too large: %zu", conn->msg_length);
+
+ if (bp->chattyOutput) {
+ printf("single_read: %s start read_length %zu msg_length %zu\n", __func__, read_length, conn->msg_length);
+ }
+ } else {
+ // no we did not, wait for more data before procesing.
+
+ if (bp->chattyOutput) {
+ printf("single_read: %s short read %zu\n", __func__, read_length);
+ }
+ }
+ }
+
+ // if read_length < sizeof(localhdr), then this is false.
+ // Otherwise, we've read the header and set msg_length, so if read_length
+ // is greater than the sum we have a full message plus header
+
+ if (read_length >= sizeof(localhdr) + conn->msg_length) {
+ size_t pbuff_len = sizeof(localhdr) + conn->msg_length;
+ int bytes_removed;
+
+ uint8_t *pbuff = parcMemory_Allocate(pbuff_len);
+ assertNotNull(pbuff, "parcMemory_Allocate(%zu) returned NULL", pbuff_len);
+
+ // dequeue into packet buffer
+ bytes_removed = parcEventBuffer_Read(input, (void *) pbuff, pbuff_len);
+ assertTrue(bytes_removed == pbuff_len, "parcEventBuffer read wrong length, expected %zu got %d", pbuff_len, bytes_removed);
+
+ // now reset message length for next packet
+ conn->msg_length = 0;
+
+ if (bp->chattyOutput) {
+ printf("connid %d msg_length %zu read_length %zu, resetting low water mark\n",
+ conn->client_fd,
+ pbuff_len,
+ read_length);
+
+ longBowDebug_MemoryDump((char *) pbuff, pbuff_len);
+ }
+
+ // reflect will free the memory
+ reflect(conn, pbuff, pbuff_len);
+
+ // we could do more after this
+ if (read_length > pbuff_len) {
+ ret = 1;
+ }
+ } else {
+ // we do not have an entire message, so skip and wait for more to be read
+ }
+ parcEventBuffer_Destroy(&input);
+ return ret;
+}
+
+static void
+conn_readcb(PARCEventQueue *bev, PARCEventType event, void *user_data)
+{
+ struct bentpipe_conn *conn = (struct bentpipe_conn *) user_data;
+
+ // drain the input buffer
+ while (single_read(bev, conn)) {
+ // empty
+ }
+}
+
+/*
+ * reflect a message to other connections
+ *
+ * We should use the zero-copy deferred write...
+ */
+static void
+reflect(struct bentpipe_conn *conn, uint8_t *pbuff, size_t pbuff_len)
+{
+ int i;
+ BentPipeState *bp = conn->parent;
+
+ for (i = 0; i < MAX_CONN; i++) {
+ if (bp->conns[i].client_fd > 0 && bp->conns[i].client_fd != conn->client_fd) {
+ int res;
+
+ if (bp->chattyOutput) {
+ printf("%s connid %d adding buffer length %zu\n", __func__, conn[i].client_fd, pbuff_len);
+ }
+
+ if (bp->use_params) {
+ uint8_t *copy = parcMemory_Allocate(pbuff_len);
+ assertNotNull(copy, "parcMemory_Allocate(%zu) returned NULL", pbuff_len);
+ memcpy(copy, pbuff, pbuff_len);
+ queue_with_delay(conn, copy, pbuff_len, i);
+ } else {
+ res = parcEventQueue_Write(bp->conns[i].bev, pbuff, pbuff_len);
+ assertTrue(res == 0, "%s got parcEventQueue_Write error\n", __func__);
+
+ localhdr *msg_hdr = (localhdr *) pbuff;
+ assertTrue(msg_hdr->length + sizeof(localhdr) == pbuff_len,
+ "msg_hdr messed up! expected %zu got %zu",
+ msg_hdr->length + sizeof(localhdr),
+ pbuff_len);
+ }
+ }
+ }
+
+ parcMemory_Deallocate((void **) &pbuff);
+}
+
+/**
+ * Queue a packet for later delivery. We calculate the needed delay and insert it
+ * in the connection's output_queue. If there is not a timer running (i.e. there are now
+ * exactly 1 elements in the queue), we start the timer for the connection.
+ *
+ * If the output queue is full, the packet might be freed and not added to the output queue.
+ */
+static void
+queue_with_delay(struct bentpipe_conn *conn, uint8_t *pbuff, size_t pbuff_len, int i)
+{
+ BentPipeState *bp = conn->parent;
+ double delay_sec;
+
+ struct timeval now;
+ struct timeval delay_tv;
+ struct timeval deadline;
+
+ gettimeofday(&now, NULL);
+
+ // 1) Apply loss rate
+ if (drand48() < bp->loss_rate) {
+ if (bp->chattyOutput) {
+ printf("%s random drop\n", __func__);
+ }
+ parcMemory_Deallocate((void **) &pbuff);
+ return;
+ }
+
+ // 2) will it fit?
+ if (pbuff_len + bp->conns[i].bytes_in_queue >= bp->buffer_bytes) {
+ if (bp->chattyOutput) {
+ printf("%s queue full\n", __func__);
+ }
+ parcMemory_Deallocate((void **) &pbuff);
+ return;
+ }
+
+ // 3) Determine delay
+ delay_sec = (double) pbuff_len / bp->bytes_per_sec;
+
+ // 4) exponential delay
+ delay_sec += -1 * log(drand48()) * bp->mean_sec_delay;
+
+ delay_tv.tv_sec = (time_t) floor(delay_sec);
+ delay_tv.tv_usec = (suseconds_t) floor((delay_sec - floor(delay_sec)) * 1E+6);
+
+ struct packet_wrapper *wrapper = parcMemory_Allocate(sizeof(struct packet_wrapper));
+ assertNotNull(wrapper, "parcMemory_Allocate(%zu) returned NULL", sizeof(struct packet_wrapper));
+ wrapper->ingress_fd = conn->client_fd;
+ wrapper->pbuff = pbuff;
+ wrapper->pbuff_len = pbuff_len;
+
+ timeradd(&now, &delay_tv, &deadline);
+
+ wrapper->deadline = deadline;
+ bp->conns[i].last_deadline = deadline;
+ bp->conns[i].bytes_in_queue += pbuff_len;
+
+ bp->conns[i].count_in_queue++;
+ TAILQ_INSERT_TAIL(&bp->conns[i].output_queue, wrapper, list);
+
+ if (bp->chattyOutput) {
+ printf("%s queue %d fd %d count %d\n", __func__, i, bp->conns[i].client_fd, bp->conns[i].count_in_queue);
+ }
+
+ // if this is first item in queue, set a timer
+ if (bp->conns[i].count_in_queue == 1) {
+ set_timer(&bp->conns[i], delay_tv);
+ }
+}
+
+int
+bentpipe_Params(BentPipeState *bp, double loss_rate, unsigned buffer_bytes, double mean_sec_delay, double bytes_per_sec)
+{
+ bp->use_params = true;
+ bp->loss_rate = loss_rate;
+ bp->buffer_bytes = buffer_bytes;
+ bp->mean_sec_delay = mean_sec_delay;
+ bp->bytes_per_sec = bytes_per_sec;
+ return 0;
+}
+
+static
+void
+keepalive_cb(int fd, PARCEventType what, void *user_data)
+{
+ struct bentpipe_state *bp = (struct bentpipe_state *) user_data;
+
+ if (!bp->startup_running) {
+ // indicate to anyone waiting that we're really running
+ if (bp->chattyOutput) {
+ printf("%s signalling startup_running\n", __func__);
+ }
+
+ lock_bentpipe(bp);
+ bp->startup_running = true;
+ signal_bentpipe(bp);
+ unlock_bentpipe(bp);
+ return;
+ }
+
+ if (bp->killme) {
+ parcEventScheduler_Abort(bp->base);
+ return;
+ }
+}
+
+/**
+ * Each connection has its own timer. The timer is used to defer sending packets to a later time,
+ * such as to realize traffic shaping.
+ */
+static void
+timer_cb(int fd, PARCEventType what, void *user_data)
+{
+ struct bentpipe_conn *conn = (struct bentpipe_conn *) user_data;
+ BentPipeState *bp = conn->parent;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+ while (!TAILQ_EMPTY(&conn->output_queue)) {
+ int res;
+ struct packet_wrapper *wrapper = TAILQ_FIRST(&conn->output_queue);
+
+ assertTrue(conn->count_in_queue > 0, "invalid state: count_in_queue is 0");
+
+ if (timercmp(&now, &wrapper->deadline, <)) {
+ break;
+ }
+
+ conn->bytes_in_queue -= wrapper->pbuff_len;
+ conn->count_in_queue--;
+
+ res = parcEventQueue_Write(conn->bev, wrapper->pbuff, wrapper->pbuff_len);
+ assertTrue(res == 0, "got parcEventQueue_Write error\n");
+
+ if (bp->chattyOutput) {
+ printf("%3.9f output conn %d bytes %zu\n",
+ now.tv_sec + now.tv_usec * 1E-6, conn->client_fd, wrapper->pbuff_len);
+ }
+
+ TAILQ_REMOVE(&conn->output_queue, wrapper, list);
+
+ parcMemory_Deallocate((void **) &(wrapper->pbuff));
+ parcMemory_Deallocate((void **) &wrapper);
+ }
+
+ if (!TAILQ_EMPTY(&conn->output_queue)) {
+ struct packet_wrapper *wrapper = TAILQ_FIRST(&conn->output_queue);
+ struct timeval delay;
+ timersub(&wrapper->deadline, &now, &delay);
+
+ if (bp->chattyOutput) {
+ printf("connid %d scheduling next timer delay %.6f\n",
+ conn->client_fd, delay.tv_sec + 1E-6 * delay.tv_usec);
+ }
+
+ assertTrue(delay.tv_sec >= 0 && delay.tv_usec >= 0,
+ "Got negative delay: now %.6f deadline %.6f delay %.6f",
+ now.tv_sec + 1E-6 * now.tv_usec,
+ wrapper->deadline.tv_sec + 1E-6 * wrapper->deadline.tv_usec,
+ delay.tv_sec + 1E-6 * delay.tv_usec);
+
+ if (delay.tv_sec == 0 && delay.tv_usec < 1000) {
+ delay.tv_usec = 1000;
+ }
+
+ set_timer(conn, delay);
+ }
+}
+
+static void
+set_timer(struct bentpipe_conn *conn, struct timeval delay)
+{
+ BentPipeState *bp = conn->parent;
+ // this replaces any prior events
+
+ if (delay.tv_usec < 1000) {
+ delay.tv_usec = 1000;
+ }
+
+ if (delay.tv_sec < 0) {
+ delay.tv_sec = 0;
+ }
+
+ if (bp->chattyOutput) {
+ printf("%s connid %d delay %.6f timer_event %p\n",
+ __func__,
+ conn->client_fd,
+ delay.tv_sec + 1E-6 * delay.tv_usec,
+ (void *) conn->timer_event);
+ }
+
+ parcEventTimer_Start(conn->timer_event, &delay);
+}
+
+void
+conn_errorcb(PARCEventQueue *bev, PARCEventQueueEventType events, void *user_data)
+{
+ if (events & PARCEventQueueEventType_EOF) {
+ struct bentpipe_conn *conn = (struct bentpipe_conn *) user_data;
+
+ if (conn->parent->chattyOutput) {
+ printf("%s Got EOF on connid %d fd %d socket\n",
+ __func__,
+ conn->client_fd,
+ parcEventQueue_GetFileDescriptor(bev));
+ }
+
+ deallocate_connection(conn->parent, conn);
+ }
+
+ if (events & PARCEventQueueEventType_Error) {
+ struct bentpipe_conn *conn = (struct bentpipe_conn *) user_data;
+
+ printf("%s Got error on connid %d fd %d socket: %s\n",
+ __func__,
+ conn->client_fd,
+ parcEventQueue_GetFileDescriptor(bev),
+ strerror(errno));
+
+ deallocate_connection(conn->parent, conn);
+ }
+}
diff --git a/libccnx-transport-rta/ccnx/transport/test_tools/bent_pipe.h b/libccnx-transport-rta/ccnx/transport/test_tools/bent_pipe.h
new file mode 100644
index 00000000..8c726b8b
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/test_tools/bent_pipe.h
@@ -0,0 +1,148 @@
+/*
+ * 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 bent_pipe.h
+ * @brief <#Brief Description#>
+ *
+ * PF_UNIX "forwarder" for testing. It's a bent pipe. Whatever comes down
+ * one connection goes up all the others.
+ *
+ * It runs in its own thread and uses an event model.
+ *
+ * We capture SIG_PIPE when doing a write so this test code does not
+ * trigger a process-wide SIG_PIPE. Unless we have SO_SIGPIPE, then we'll use that.
+ *
+ */
+#ifndef Libccnx_bent_pipe_h
+#define Libccnx_bent_pipe_h
+
+#include <stdbool.h>
+
+struct bentpipe_state;
+/**
+ *
+ * @see bentpipe_Create
+ */
+typedef struct bentpipe_state BentPipeState;
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+BentPipeState *bentpipe_Create(const char *local_name);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void bentpipe_Destroy(BentPipeState **statePtr);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+int bentpipe_Start(BentPipeState *bp);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+int bentpipe_Stop(BentPipeState *bp);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void bentpipe_SetChattyOutput(BentPipeState *bp, bool chattyOutput);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+int bentpipe_Params(BentPipeState *bp, double loss_rate, unsigned buffer_bytes, double mean_sec_delay, double bytes_per_sec);
+#endif // Libccnx_bent_pipe_h
diff --git a/libccnx-transport-rta/ccnx/transport/test_tools/ethersend.c b/libccnx-transport-rta/ccnx/transport/test_tools/ethersend.c
new file mode 100644
index 00000000..9db1a7e3
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/test_tools/ethersend.c
@@ -0,0 +1,211 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ifaddrs.h>
+#include <assert.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+typedef uint8_t u_char;
+typedef uint16_t u_short;
+typedef uint32_t u_int;
+
+
+#include <pcap.h>
+
+#define ETHERTYPE 0x0801
+
+struct _packet {
+ u_int8_t dst[6];
+ u_int8_t src[6];
+ u_int16_t ethertype;
+ u_int8_t data[0];
+} __attribute__((packed));
+
+
+static void send_file(pcap_t *handle, uint8_t smac[], uint8_t dmac[], const char *filename);
+
+
+static
+void
+printhex(u_int8_t *buffer, int length)
+{
+ int i;
+ for (i = 0; i < length; i++) {
+ printf("%02X", buffer[i]);
+ }
+}
+
+
+
+static void
+send_file(pcap_t *handle, uint8_t smac[], uint8_t dmac[], const char *filename)
+{
+ struct stat statbuf;
+
+ struct _packet *packet = malloc(1500);
+ memset(packet, 0, 1500);
+
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror("Error opening file: ");
+ abort();
+ }
+
+ if (fstat(fd, &statbuf) < 0) {
+ perror("fstat error");
+ abort();
+ }
+
+ uint8_t *src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (src == MAP_FAILED) {
+ perror("mmap error");
+ abort();
+ }
+
+ assert(statbuf.st_size <= 1500);
+
+ size_t len = sizeof(struct _packet) + statbuf.st_size;
+ printf("Sending config/query size %zu\n", len);
+
+ memcpy(packet->dst, dmac, 6);
+ memcpy(packet->src, smac, 6);
+
+ packet->ethertype = htons(ETHERTYPE);
+
+ memcpy(packet->data, src, statbuf.st_size);
+
+ int x = pcap_inject(handle, packet, len);
+ printf("%s wrote %d bytes\n", __func__, x);
+
+ free(packet);
+}
+
+static
+void
+get_mac_address(const char *deviceName, u_int8_t *mac)
+{
+ struct ifaddrs *ifap;
+ struct ifaddrs *next;
+
+ int x = getifaddrs(&ifap);
+ if (x != 0) {
+ perror("getifaddrs");
+ exit(EXIT_FAILURE);
+ }
+
+ next = ifap;
+ while (next != NULL) {
+#if defined(__APPLE__)
+ if (strstr(deviceName, next->ifa_name) != NULL && next->ifa_addr->sa_family == AF_LINK)
+#elif defined(__linux__)
+ if (strstr(deviceName, next->ifa_name) != NULL && next->ifa_addr->sa_family == AF_PACKET)
+#else
+#error Unsupported platform
+#endif
+ {
+ memcpy(mac, next->ifa_addr->sa_data + 9, 6);
+ break;
+ }
+ next = next->ifa_next;
+ }
+ freeifaddrs(ifap);
+}
+
+static void
+macStringToArray(const char *string, size_t outputLength, uint8_t output[])
+{
+ assert(outputLength == 6);
+
+ sscanf(string, "%02x:%02x:%02x:%02x:%02x:%02x", &output[0], &output[1], &output[2], &output[3], &output[4], &output[5]);
+}
+
+int
+main(int argc, const char *argv[])
+{
+ if (argc != 4 || argv[1][0] == '-') {
+ printf("usage: ethersend dev dst filename\n");
+ printf("\n");
+ printf("Will send filename as the payload of an ethernet frame to dst\n");
+ printf("\n");
+ printf("example: ethersend eth0 a8:20:66:3b:30:bc interest.bin\n");
+ printf("\n");
+ exit(EXIT_FAILURE);
+ }
+
+ pcap_t *handle; /* Session handle */
+ const char *dev = argv[1]; /* The device to sniff on */
+ char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */
+ struct bpf_program fp; /* The compiled filter */
+ char filter_exp[1024]; /* The filter expression */
+ bpf_u_int32 mask; /* Our netmask */
+ bpf_u_int32 net; /* Our IP */
+
+ sprintf(filter_exp, "ether proto 0x%04X", ETHERTYPE);
+
+ printf("dev = %s\n", dev);
+
+ /* Find the properties for the device */
+ if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
+ fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
+ net = 0;
+ mask = 0;
+ }
+
+ u_int8_t mymac[6];
+ get_mac_address(dev, mymac);
+ printf("My mac address: "); printhex(mymac, 6); printf("\n");
+
+ u_int8_t dmac[6];
+ macStringToArray(argv[2], 6, dmac);
+ printf("dmac address : "); printhex(dmac, 6); printf("\n");
+
+
+ /* Open the session in promiscuous mode */
+ handle = pcap_open_live(dev, 1500, 1, 1000, errbuf);
+ if (handle == NULL) {
+ fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
+ return (2);
+ }
+
+ /* Compile and apply the filter */
+ if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
+ fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
+ return (2);
+ }
+
+ if (pcap_setfilter(handle, &fp) == -1) {
+ fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
+ return (2);
+ }
+
+ send_file(handle, mymac, dmac, argv[3]);
+
+
+ pcap_close(handle);
+ return (0);
+}
+
diff --git a/libccnx-transport-rta/ccnx/transport/test_tools/pktgen.c b/libccnx-transport-rta/ccnx/transport/test_tools/pktgen.c
new file mode 100644
index 00000000..8f1cc087
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/test_tools/pktgen.c
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+/**
+ * Generate packets
+ *
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <LongBow/runtime.h>
+
+typedef enum {
+ MODE_SEND,
+ MODE_REPLY
+} PktGenMode;
+
+typedef enum {
+ ENCAP_ETHER,
+ ENCAP_UDP
+} PktGenEncap;
+
+typedef enum {
+ PKTGEN_STREAM,
+ PKTGEN_STOPWAIT
+} PktGenFlow;
+
+typedef struct {
+ PktGenMode mode;
+ PktGenEncap encap;
+ PktGenFlow flow;
+
+ char *ifname;
+ char *etherOrIp;
+ char *etherType;
+ unsigned count;
+
+ struct timeval startTime;
+ struct timeval stopTime;
+ unsigned packetCount;
+} PktGen;
+
+// ======================================================================
+
+static void
+usage(void)
+{
+ printf("usage: \n");
+ printf(" This program functions as a requester and a responder. They operate in a pair.\n");
+ printf(" The test can run over raw Ethernet encapsulation or over UDP\n");
+ printf(" The <count> parameter can be an integer or use a 'kmg' suffix for 1000, 1E+6, or 1E+9\n");
+ printf("\n");
+ printf(" pktgen send ether <ifname> <dstmac> [ethertype] count <n> (stream | stopwait)\n");
+ printf(" pktgen reply ether <ifname> [count <n>]\n");
+ printf("\n");
+ printf(" This mode sends either a stream or stop-and-wait request to an Ethernet peer\n");
+ printf(" pktgen send udp <ifname> <dstip> <dstport> count <n> (stream | stopwait)\n");
+ printf(" pktgen reply udp <ifname> [count <n>]\n");
+ printf("\n");
+ printf(" Examples:\n");
+ printf(" This uses the standard Ethertype of 0x0801. The replier will stay running forever.\n");
+ printf(" pktgen send ether em1 bc:30:5b:f2:2f:60 count 1M stream\n");
+ printf(" pktgen reply ether em1\n");
+ printf("\n");
+ printf(" This uses a custom ethertype. The replier will stay running forever.\n");
+ printf(" pktgen send ether em1 bc:30:5b:f2:2f:60 0x9000 count 1M stream\n");
+ printf(" pktgen reply ether em1\n");
+ printf("\n");
+ printf(" An example with UDP\n");
+ printf(" pktgen send udp em1 10.1.0.2 9695 count 1M stopwait\n");
+ printf(" pktgen reply udp em1\n");
+ printf("\n");
+}
+
+static PktGen
+parseCommandLine(int argc, char *argv[argc])
+{
+ PktGen pktgen;
+ memset(&pktgen, 0, sizeof(PktGen));
+
+ usage();
+
+ return pktgen;
+}
+
+// ======================================================================
+
+static void
+generateEther(PktGen *pktgen)
+{
+ printf("Generating %u ethernet interest messages\n", pktgen->count);
+}
+
+static void
+replyEther(PktGen *pktgen)
+{
+ printf("replying up to %u ethernet content objects messages\n", pktgen->count);
+}
+
+// ======================================================================
+
+static void
+generateUdp(PktGen *pktgen)
+{
+ printf("Generating %u UDP interest messages\n", pktgen->count);
+}
+
+static void
+replyUdp(PktGen *pktgen)
+{
+ printf("replying up to %u UDP content objects messages\n", pktgen->count);
+}
+
+
+// ======================================================================
+
+static void
+displayStatistics(PktGen *pktgen)
+{
+ printf("stats.... coming soon\n");
+}
+
+// ======================================================================
+
+static void
+runSender(PktGen *pktgen)
+{
+ switch (pktgen->encap) {
+ case ENCAP_ETHER:
+ generateEther(pktgen);
+ break;
+
+ case ENCAP_UDP:
+ generateUdp(pktgen);
+ break;
+
+ default:
+ trapIllegalValue(pktgen.encap, "Unknown encapsulation: %d", pktgen->encap);
+ }
+}
+
+static void
+runReplier(PktGen *pktgen)
+{
+ switch (pktgen->encap) {
+ case ENCAP_ETHER:
+ replyEther(pktgen);
+ break;
+
+ case ENCAP_UDP:
+ replyUdp(pktgen);
+ break;
+
+ default:
+ trapIllegalValue(pktgen.encap, "Unknown encapsulation: %d", pktgen->encap);
+ }
+}
+
+// ======================================================================
+
+int
+main(int argc, char *argv[argc])
+{
+ PktGen pktgen = parseCommandLine(argc, argv);
+
+ switch (pktgen.mode) {
+ case MODE_SEND:
+ runSender(&pktgen);
+ break;
+
+ case MODE_REPLY:
+ runReplier(&pktgen);
+ break;
+
+ default:
+ trapIllegalValue(pktgen.mode, "Unknown mode: %d", pktgen.mode);
+ }
+
+ displayStatistics(&pktgen);
+}
diff --git a/libccnx-transport-rta/ccnx/transport/test_tools/test/.gitignore b/libccnx-transport-rta/ccnx/transport/test_tools/test/.gitignore
new file mode 100644
index 00000000..e1bb21dc
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/test_tools/test/.gitignore
@@ -0,0 +1 @@
+test_bent_pipe
diff --git a/libccnx-transport-rta/ccnx/transport/test_tools/test/test_bent_pipe.c b/libccnx-transport-rta/ccnx/transport/test_tools/test/test_bent_pipe.c
new file mode 100644
index 00000000..0fbe9683
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/test_tools/test/test_bent_pipe.c
@@ -0,0 +1,397 @@
+/*
+ * 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.
+ */
+
+//
+// test_bent_pipe.c
+// Libccnx
+//
+
+#include "../bent_pipe.c"
+
+#include <stdio.h>
+#include <sys/un.h>
+#include <strings.h>
+#include <sys/queue.h>
+#include <errno.h>
+
+#include <LongBow/unit-test.h>
+#include <LongBow/runtime.h>
+
+#include <parc/algol/parc_Memory.h>
+
+#define CHATTY 1
+
+static const char *local_name = "/tmp/alpha";
+
+LONGBOW_TEST_RUNNER(BentPipe)
+{
+ LONGBOW_RUN_TEST_FIXTURE(CreateDestroy);
+ LONGBOW_RUN_TEST_FIXTURE(System);
+}
+
+LONGBOW_TEST_RUNNER_SETUP(BentPipe)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_RUNNER_TEARDOWN(BentPipe)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ================================
+
+LONGBOW_TEST_FIXTURE(CreateDestroy)
+{
+ LONGBOW_RUN_TEST_CASE(CreateDestroy, create_destroy);
+ LONGBOW_RUN_TEST_CASE(CreateDestroy, create_start_stop_destroy);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(CreateDestroy)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(CreateDestroy)
+{
+ 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(CreateDestroy, create_destroy)
+{
+ BentPipeState *bp = bentpipe_Create(local_name);
+ bentpipe_Destroy(&bp);
+}
+
+LONGBOW_TEST_CASE(CreateDestroy, create_start_stop_destroy)
+{
+ BentPipeState *bp = bentpipe_Create(local_name);
+ bentpipe_Start(bp);
+ bentpipe_Stop(bp);
+ bentpipe_Destroy(&bp);
+}
+
+// ================================
+
+BentPipeState *system_bp;
+
+LONGBOW_TEST_FIXTURE(System)
+{
+ LONGBOW_RUN_TEST_CASE(System, two_connections);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(System)
+{
+ system_bp = bentpipe_Create(local_name);
+ bentpipe_Start(system_bp);
+
+ printf("%s created system_bp %p, running %d\n", __func__, (void *) system_bp, system_bp->startup_running);
+
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(System)
+{
+ printf("%s stopping system_bp %p, running %d\n", __func__, (void *) system_bp, system_bp->startup_running);
+
+ bentpipe_Stop(system_bp);
+ bentpipe_Destroy(&system_bp);
+ 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;
+}
+
+int
+connect_to_bentpipe(const char *pipe_name)
+{
+ int res;
+ struct sockaddr_un addr_unix;
+ int fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket PF_LOCAL");
+ }
+
+ assertFalse(fd < 0, "socket PF_LOCAL error");
+
+ memset(&addr_unix, 0, sizeof(addr_unix));
+ addr_unix.sun_family = AF_UNIX;
+ strcpy(addr_unix.sun_path, pipe_name);
+
+ res = connect(fd, (struct sockaddr *) &addr_unix, sizeof(addr_unix));
+ if (res < 0) {
+ perror("connect");
+ }
+ assertTrue(res == 0, "error on connect");
+ return fd;
+}
+
+#define MAXSEND 1024
+#define CONN_COUNT 3
+#define MAXPENDING 128
+
+struct sendlist {
+ uint8_t buffer[MAXSEND];
+ size_t length;
+ unsigned refcount;
+};
+
+struct fdstate {
+ int fd;
+
+ struct sendlist *expected[MAXPENDING];
+ unsigned head;
+ unsigned tail;
+ unsigned count_expected;
+
+ unsigned count_send;
+ unsigned count_recv;
+
+ // these are for the next message being read
+ size_t total_read_length;
+ size_t current_read_length;
+ uint8_t pbuff[MAXSEND + 16];
+};
+
+void
+sendbuffer(int fd, struct fdstate *state)
+{
+ struct sendlist *entry = parcMemory_AllocateAndClear(sizeof(struct sendlist));
+ assertNotNull(entry, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(struct sendlist));
+ size_t len, total_len;
+ localhdr *hdr = (localhdr *) entry->buffer;
+ int i;
+ ssize_t res;
+
+ len = random() % (MAXSEND - sizeof(localhdr) - 1) + 1;
+ hdr->pid = getpid();
+ hdr->fd = fd;
+ hdr->length = (int) len;
+
+ total_len = len + sizeof(localhdr);
+
+ entry->length = total_len;
+ entry->refcount = 0;
+
+ for (i = 0; i < CONN_COUNT; i++) {
+ if (state[i].fd != fd) {
+ entry->refcount++;
+ state[i].expected[ state[i].tail ] = entry;
+ state[i].tail = (state[i].tail + 1) % MAXPENDING;
+ state[i].count_expected++;
+
+ assertFalse(state[i].tail == state[i].head,
+ "%s buffer wrap around on fd %d",
+ __func__,
+ state[i].fd);
+
+ if (CHATTY) {
+ printf("conn %2d added expected cnt %u length %zu\n",
+ i,
+ state[i].count_expected,
+ entry->length);
+ }
+ } else {
+ state[i].count_send++;
+
+ if (CHATTY) {
+ printf("conn %2d sent count %u\n", i, state[i].count_send);
+ }
+ }
+ }
+
+ res = write(fd, entry->buffer, entry->length);
+ assertTrue(res == entry->length, "%s write error %s\n", __func__, strerror(errno));
+}
+
+static int
+compare_sends(struct fdstate *s, uint8_t *buffer, size_t buffer_len)
+{
+ struct sendlist *expected = s->expected[ s->head ];
+ int res;
+
+ s->head = (s->head + 1) % MAXPENDING;
+
+ assertTrue(expected->length == buffer_len, "%s lengths do not match, expected %zu got %zu\n",
+ __func__, expected->length, buffer_len);
+
+ if (expected->length != buffer_len) {
+ return -1;
+ }
+
+ res = memcmp(expected->buffer, buffer, buffer_len);
+ assertTrue(res == 0, "%s buffers did not match\n", __func__);
+ if (res != 0) {
+ return -1;
+ }
+
+ assertTrue(expected->refcount > 0, "%s invalid refcount\n", __func__);
+
+ expected->refcount--;
+ if (expected->refcount == 0) {
+ memset(expected, 0, sizeof(struct sendlist));
+ parcMemory_Deallocate((void **) &expected);
+ }
+
+ return 0;
+}
+
+/*
+ * Create two connections to bent pipe and make sure they reflect
+ */
+LONGBOW_TEST_CASE(System, two_connections)
+{
+ struct fdstate state[CONN_COUNT];
+ fd_set fdset, readset;
+ int number_writes = 100;
+ int count_writes = 0;
+ int i;
+ struct timeval timeout = { 0, 10000 };
+ unsigned pending_expected = 0;
+
+ assertNotNull(system_bp, "%s running with null system_bp\n", __func__);
+
+ FD_ZERO(&fdset);
+ for (i = 0; i < CONN_COUNT; i++) {
+ memset(&state[i], 0, sizeof(struct fdstate));
+ state[i].fd = connect_to_bentpipe(local_name);
+ FD_SET(state[i].fd, &fdset);
+ }
+
+ sleep(1);
+
+ assertTrue(system_bp->conn_count == CONN_COUNT, "bp conn count wrong");
+
+ while (count_writes < number_writes || pending_expected > 0) {
+ int res;
+ memcpy(&readset, &fdset, sizeof(readset));
+
+ res = select(FD_SETSIZE, &readset, NULL, NULL, &timeout);
+ if (res < 0) {
+ perror("select");
+ abort();
+ }
+
+ if (res > 0) {
+ if (CHATTY) {
+ printf("%s got res %d\n", __func__, res);
+ }
+ for (i = 0; i < CONN_COUNT; i++) {
+ if (FD_ISSET(state[i].fd, &readset)) {
+ ssize_t res;
+ localhdr *hdr = (localhdr *) state[i].pbuff;
+
+ if (state[i].total_read_length == 0) {
+ size_t remaining = sizeof(localhdr) - state[i].current_read_length;
+ // we need to read a header
+ res = read(state[i].fd,
+ state[i].pbuff + state[i].current_read_length,
+ remaining);
+
+ assertFalse(res < 0, "%s got read error: %s", __func__, strerror(errno));
+
+ state[i].current_read_length += res;
+ if (state[i].current_read_length == sizeof(localhdr)) {
+ state[i].total_read_length = sizeof(localhdr) + hdr->length;
+
+ if (CHATTY) {
+ printf("%s conn %d fd %d set total length %zu\n",
+ __func__,
+ i,
+ state[i].fd,
+ state[i].total_read_length);
+ }
+ }
+ }
+
+ if (state[i].current_read_length < state[i].total_read_length) {
+ size_t remaining = state[i].total_read_length - state[i].current_read_length;
+ // we need to read a header
+ res = read(state[i].fd,
+ state[i].pbuff + state[i].current_read_length,
+ remaining);
+
+ assertFalse(res < 0, "%s got read error: %s", __func__, strerror(errno));
+ state[i].current_read_length += res;
+ }
+
+ if (state[i].current_read_length == state[i].total_read_length) {
+ // verify that it's the same as the top expected stack
+ res = compare_sends(&state[i], state[i].pbuff, state[i].total_read_length);
+
+ assertTrue(res == 0, "%s invalid receive compare\n", __func__);
+
+ state[i].count_recv++;
+ state[i].count_expected--;
+
+ if (CHATTY) {
+ printf("%s conn %d fd %d cnt_recv %u cnt_expected %u\n",
+ __func__,
+ i, state[i].fd, state[i].count_recv, state[i].count_expected);
+ }
+
+ // done with it
+ state[i].current_read_length = 0;
+ state[i].total_read_length = 0;
+ }
+ }
+ }
+ }
+
+ if ((random() % 4) == 0) {
+ // do a write
+ int out = random() % CONN_COUNT;
+
+ if (CHATTY) {
+ printf("%s sendbuffer for conn %d fd %d\n", __func__, out, state[out].fd);
+ }
+
+ sendbuffer(state[out].fd, state);
+ count_writes++;
+ }
+
+ pending_expected = 0;
+ for (i = 0; i < CONN_COUNT; i++) {
+ pending_expected += state[i].count_expected;
+ }
+ }
+
+ for (i = 0; i < CONN_COUNT; i++) {
+ printf("conn %2d fd %2d send %4u recv %4u\n",
+ i,
+ state[i].fd,
+ state[i].count_send,
+ state[i].count_recv);
+
+ assertTrue(state[i].count_recv == number_writes - state[i].count_send + 1,
+ "%s conn %d incorrect counts\n",
+ __func__);
+ close(state[i].fd);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(BentPipe);
+ exit(longBowMain(argc, argv, testRunner, NULL));
+}
diff --git a/libccnx-transport-rta/ccnx/transport/test_tools/traffic_tools.c b/libccnx-transport-rta/ccnx/transport/test_tools/traffic_tools.c
new file mode 100644
index 00000000..73b321e4
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/test_tools/traffic_tools.c
@@ -0,0 +1,292 @@
+/*
+ * 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 <LongBow/runtime.h>
+#include <parc/algol/parc_Memory.h>
+
+#include "traffic_tools.h"
+#include <ccnx/common/ccnx_NameSegmentNumber.h>
+#include <ccnx/transport/transport_rta/core/rta_Component.h>
+
+#include <ccnx/common/codec/schema_v1/testdata/v1_interest_nameA.h>
+
+#include <ccnx/common/internal/ccnx_InterestDefault.h>
+
+#include <ccnx/api/control/cpi_ControlFacade.h>
+
+/**
+ * @function decode_last_component_as_segment
+ * @abstract Returns true is last name component is a segment, and returns the segment
+ * @discussion
+ * The outputSegment may be null, in which case this function is just a true/false for the
+ * last path segment being an object segment number.
+ *
+ * @param outputSegment is an output parameter of the segment number, if returns true. May be null.
+ * @return <#return#>
+ */
+bool
+trafficTools_GetObjectSegmentFromName(CCNxName *name, uint64_t *outputSegment)
+{
+ assertNotNull(name, "Name must be non-null");
+ bool success = false;
+ size_t segmentCount = ccnxName_GetSegmentCount(name);
+ if (segmentCount > 0) {
+ CCNxNameSegment *lastSegment = ccnxName_GetSegment(name, segmentCount - 1);
+ if (ccnxNameSegment_GetType(lastSegment) == CCNxNameLabelType_CHUNK) {
+ if (outputSegment) {
+ *outputSegment = ccnxNameSegmentNumber_Value(lastSegment);
+ }
+ success = true;
+ }
+ }
+ return success;
+}
+
+bool
+trafficTools_ReadAndVerifySegment(PARCEventQueue *queue, CCNxName *basename, uint64_t expected, PARCBuffer *expectedPayload)
+{
+ TransportMessage *test_tm;
+ CCNxName *test_name;
+ uint64_t segnum;
+ CCNxName *name_copy;
+
+ test_tm = rtaComponent_GetMessage(queue);
+ assertNotNull(test_tm, "got null transport message down the stack, expecting interest\n");
+
+ assertTrue(transportMessage_IsInterest(test_tm),
+ "Got wrong transport message pointer, is not an interest");
+
+ CCNxTlvDictionary *interestDictionary = transportMessage_GetDictionary(test_tm);
+ test_name = ccnxInterest_GetName(interestDictionary);
+
+ bool success = trafficTools_GetObjectSegmentFromName(test_name, &segnum);
+ assertTrue(success, "got error decoding last component as segnum: %s", ccnxName_ToString(test_name));
+ assertTrue(expected == segnum, "Got wrong segnum, expected %" PRIu64 ", got %" PRIu64 "\n",
+ expected, segnum);
+
+ name_copy = ccnxName_Copy(test_name);
+ ccnxName_Trim(name_copy, 1);
+ assertTrue(ccnxName_Compare(basename, name_copy) == 0,
+ "\nName '%s'\ndid not match\nexpected '%s'\nInterest name '%s'\n\n",
+ ccnxName_ToString(name_copy),
+ ccnxName_ToString(basename),
+ ccnxName_ToString(test_name));
+
+ ccnxName_Release(&name_copy);
+
+ if (expectedPayload != NULL) {
+ assertTrue(parcBuffer_Equals(expectedPayload, ccnxInterest_GetPayload(interestDictionary)),
+ "Expected the same Interest payload out as was sent in originally.");
+ }
+
+ transportMessage_Destroy(&test_tm);
+
+ return true;
+}
+
+CCNxContentObject *
+trafficTools_CreateSignedContentObject()
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/hello/dolly");
+ PARCBuffer *payload = parcBuffer_WrapCString("hello");
+
+ CCNxContentObject *result = ccnxContentObject_CreateWithNameAndPayload(name, payload);
+
+ PARCBuffer *keyId = parcBuffer_WrapCString("keyhash");
+ PARCBuffer *sigbits = parcBuffer_WrapCString("siggybits");
+
+ PARCSignature *signature = parcSignature_Create(PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256, sigbits);
+ parcBuffer_Release(&sigbits);
+
+ ccnxContentObject_SetSignature(result, keyId, signature, NULL);
+
+ parcBuffer_Release(&payload);
+ parcBuffer_Release(&keyId);
+ parcSignature_Release(&signature);
+ ccnxName_Release(&name);
+
+ return result;
+}
+
+CCNxContentObject *
+trafficTools_CreateContentObjectWithPayload(PARCBuffer *contents)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/hello/dolly");
+
+ CCNxContentObject *result = ccnxContentObject_CreateWithNameAndPayload(name, contents);
+
+ ccnxName_Release(&name);
+
+ return result;
+}
+
+TransportMessage *
+trafficTools_CreateTransportMessageWithSignedContentObject(RtaConnection *connection)
+{
+ CCNxContentObject *unsignedObject = trafficTools_CreateSignedContentObject();
+ CCNxMetaMessage *message = ccnxMetaMessage_CreateFromContentObject(unsignedObject);
+ TransportMessage *tm = transportMessage_CreateFromDictionary(message);
+
+ transportMessage_SetInfo(tm, connection, NULL);
+
+ ccnxContentObject_Release(&unsignedObject);
+ ccnxMetaMessage_Release(&message);
+
+ return tm;
+}
+
+TransportMessage *
+trafficTools_CreateTransportMessageWithSignedContentObjectWithName(RtaConnection *connection, CCNxName *name, const char *keystorePath, const char *keystorePassword)
+{
+ PARCBuffer *payload = parcBuffer_WrapCString("hello");
+
+ CCNxContentObject *contentObject = ccnxContentObject_CreateWithNameAndPayload(name, payload);
+ PARCBuffer *keyId = parcBuffer_WrapCString("hash of key");
+ PARCBuffer *sigbits = parcBuffer_WrapCString("sig bits");
+ PARCSignature *signature = parcSignature_Create(PARCSigningAlgorithm_RSA, PARCCryptoHashType_SHA256, sigbits);
+ parcBuffer_Release(&sigbits);
+
+ ccnxContentObject_SetSignature(contentObject, keyId, signature, NULL);
+
+ CCNxMetaMessage *message = ccnxMetaMessage_CreateFromContentObject(contentObject);
+ TransportMessage *tm = transportMessage_CreateFromDictionary(message);
+ transportMessage_SetInfo(tm, connection, NULL);
+
+ ccnxMetaMessage_Release(&message);
+ ccnxContentObject_Release(&contentObject);
+ parcSignature_Release(&signature);
+ parcBuffer_Release(&keyId);
+ return tm;
+}
+
+CCNxInterest *
+trafficTools_CreateInterest(void)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/there/were/bells/on/the/hill");
+ CCNxInterest *interest = ccnxInterest_CreateSimple(name);
+ ccnxName_Release(&name);
+
+ return interest;
+}
+
+CCNxTlvDictionary *
+trafficTools_CreateDictionaryInterest(void)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/there/were/bells/on/the/hill");
+ CCNxTlvDictionary *interest = ccnxInterest_CreateSimple(name);
+ ccnxName_Release(&name);
+
+ return interest;
+}
+
+TransportMessage *
+trafficTools_CreateTransportMessageWithInterest(RtaConnection *connection)
+{
+ return trafficTools_CreateTransportMessageWithDictionaryInterest(connection, CCNxTlvDictionary_SchemaVersion_V1);
+}
+
+TransportMessage *
+trafficTools_CreateTransportMessageWithControlMessage(RtaConnection *connection)
+{
+ return trafficTools_CreateTransportMessageWithDictionaryControl(connection, CCNxTlvDictionary_SchemaVersion_V1);
+}
+
+TransportMessage *
+trafficTools_CreateTransportMessageWithRaw(RtaConnection *connection)
+{
+ return trafficTools_CreateTransportMessageWithDictionaryRaw(connection, CCNxTlvDictionary_SchemaVersion_V1);
+}
+
+TransportMessage *
+trafficTools_CreateTransportMessageWithDictionaryInterest(RtaConnection *connection, CCNxTlvDictionary_SchemaVersion schema)
+{
+ CCNxTlvDictionary *interest;
+ CCNxName *name = ccnxName_CreateFromCString("lci:/lost/in/space");
+
+ CCNxInterestInterface *impl = NULL;
+
+ switch (schema) {
+ case CCNxTlvDictionary_SchemaVersion_V1:
+ impl = &CCNxInterestFacadeV1_Implementation;
+ break;
+
+ default:
+ trapIllegalValue(schema, "Unsupported schema version");
+ }
+
+ // impl should be set if we get here.
+ interest = ccnxInterest_CreateWithImpl(impl,
+ name,
+ CCNxInterestDefault_LifetimeMilliseconds,
+ NULL,
+ NULL,
+ CCNxInterestDefault_HopLimit);
+
+
+ TransportMessage *tm = transportMessage_CreateFromDictionary(interest);
+ ccnxTlvDictionary_Release(&interest);
+
+ transportMessage_SetInfo(tm, connection, NULL);
+ ccnxName_Release(&name);
+ return tm;
+}
+
+TransportMessage *
+trafficTools_CreateTransportMessageWithDictionaryRaw(RtaConnection *connection, unsigned schema)
+{
+ PARCBuffer *buffer = parcBuffer_Allocate(sizeof(v1_interest_nameA));
+ parcBuffer_PutArray(buffer, sizeof(v1_interest_nameA), v1_interest_nameA);
+ parcBuffer_Flip(buffer);
+ CCNxTlvDictionary *wireformat = ccnxWireFormatMessage_FromInterestPacketType(schema, buffer);
+
+ TransportMessage *tm = transportMessage_CreateFromDictionary(wireformat);
+ ccnxTlvDictionary_Release(&wireformat);
+ parcBuffer_Release(&buffer);
+
+ transportMessage_SetInfo(tm, connection, NULL);
+ return tm;
+}
+
+TransportMessage *
+trafficTools_CreateTransportMessageWithDictionaryControl(RtaConnection *connection, unsigned schema)
+{
+ char *jsonstring = "{\"CPI_REQUEST\":{\"SEQUENCE\":22,\"REGISTER\":{\"PREFIX\":\"lci:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200}}}";
+
+ PARCJSON *json = parcJSON_ParseString(jsonstring);
+ CCNxTlvDictionary *control = NULL;
+
+ switch (schema) {
+ case 1:
+ control = ccnxControlFacade_CreateCPI(json);
+
+ break;
+
+ default:
+ break;
+ }
+
+
+ TransportMessage *tm = transportMessage_CreateFromDictionary(control);
+ transportMessage_SetInfo(tm, connection, NULL);
+
+ parcJSON_Release(&json);
+ ccnxTlvDictionary_Release(&control);
+
+ return tm;
+}
diff --git a/libccnx-transport-rta/ccnx/transport/test_tools/traffic_tools.h b/libccnx-transport-rta/ccnx/transport/test_tools/traffic_tools.h
new file mode 100644
index 00000000..09a8caf9
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/test_tools/traffic_tools.h
@@ -0,0 +1,284 @@
+/*
+ * 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 traffic_tools.h
+ * @brief <#Brief Description#>
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef Libccnx_traffic_generators_h
+#define Libccnx_traffic_generators_h
+
+#include <ccnx/transport/transport_rta/core/rta_Connection.h>
+#include <ccnx/transport/transport_rta/core/rta_ComponentQueue.h>
+
+#include <ccnx/common/ccnx_ContentObject.h>
+#include <ccnx/common/ccnx_Interest.h>
+
+#include <ccnx/common/internal/ccnx_TlvDictionary.h>
+
+/**
+ * <#One Line Description#>
+ *
+ * Read the provided queue and verify the received message (Interest or Object)
+ * has the given basename (not including segment) and the provided segment number
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+bool trafficTools_ReadAndVerifySegment(PARCEventQueue *queue, CCNxName *basename,
+ uint64_t expectedSegnum, PARCBuffer *expectedPayload);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+bool trafficTools_GetObjectSegmentFromName(CCNxName *name, uint64_t *outputSegment);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CCNxContentObject *trafficTools_CreateSignedContentObject();
+
+/**
+ * Create a `CCNxUnsignedContentObject` with the given payload.
+ *
+ * The payload is contained within the given `PARCBuffer` from its position to its limit.
+ *
+ * @param [in] payload <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see PARCBuffer
+ */
+CCNxContentObject *trafficTools_CreateContentObjectWithPayload(PARCBuffer *payload);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CCNxInterest *trafficTools_CreateInterest(void);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+TransportMessage *trafficTools_CreateTransportMessageWithControlMessage(RtaConnection *connection);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+TransportMessage *trafficTools_CreateTransportMessageWithInterest(RtaConnection *connection);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+TransportMessage *trafficTools_CreateTransportMessageWithSignedContentObject(RtaConnection *connection);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+TransportMessage *trafficTools_CreateTransportMessageWithSignedContentObjectWithName(RtaConnection *connection,
+ CCNxName *name, const char *keystorePath, const char *keystorePassword);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+TransportMessage *trafficTools_CreateTransportMessageWithRaw(RtaConnection *connection);
+
+
+/**
+ * Creates a Dictionary format RAW message
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+TransportMessage *trafficTools_CreateTransportMessageWithDictionaryRaw(RtaConnection *connection, unsigned schema);
+
+/**
+ * Creates a dictionary format Interest message
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+TransportMessage *trafficTools_CreateTransportMessageWithDictionaryInterest(RtaConnection *connection, unsigned schema);
+
+/**
+ * Creates a dictioary format Control message
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+TransportMessage *trafficTools_CreateTransportMessageWithDictionaryControl(RtaConnection *connection, unsigned schema);
+
+/**
+ * Creates an interest in Dictionary format
+ *
+ * Does not have a wire format
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxTlvDictionary *trafficTools_CreateDictionaryInterest(void);
+#endif // Libccnx_traffic_generators_h
diff --git a/libccnx-transport-rta/ccnx/transport/test_tools/write_packets.c b/libccnx-transport-rta/ccnx/transport/test_tools/write_packets.c
new file mode 100644
index 00000000..16c3e5ff
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/transport/test_tools/write_packets.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.
+ */
+
+/**
+ * Utility function to write the test data to data files. The data files will be in a format
+ * you can use to import with "text2pcap". For example:
+ *
+ * text2pcap -u 9695,9695 file
+ *
+ * would add a fake UPD/IP/Ethernet header with UDP ports 9695 for source and destination
+ *
+ */
+
+#include <stdio.h>
+
+#include <ccnx/common/codec/schema_v0/testdata/testrig_truthSet.h>
+#include <ccnx/common/codec/schema_v1/testdata/v1_testrig_truthSet.h>
+
+/*
+ * typedef struct testrig_truth_table
+ * {
+ * const char * testname;
+ * uint8_t * packet;
+ * size_t length;
+ *
+ * TlvErrorCodes expectedError;
+ *
+ * // the array is terminated by a T_INVALID value
+ * // for "arrayIndexOrTypeKey"
+ * TruthTableEntry * entry;
+ * } TruthTable;
+ *
+ */
+
+static void
+writePacket(TruthTable *table)
+{
+ char filename[1024];
+ snprintf(filename, 1024, "%s.txt", table->testname);
+ FILE *fh = fopen(filename, "w+");
+ printf("name %s\n", filename);
+
+ int linewidth = 8;
+ for (int i = 0; i < table->length; i++) {
+ if ((i % linewidth) == 0) {
+ fprintf(fh, "\n%06X ", i);
+ }
+ fprintf(fh, "%02X ", table->packet[i]);
+ }
+ fprintf(fh, "\n");
+ fclose(fh);
+}
+
+static void
+loopTruthTable(TruthTable truthset[])
+{
+ for (int i = 0; truthset[i].packet != NULL; i++) {
+ writePacket(&truthset[i]);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ loopTruthTable(interests_truthSet);
+ loopTruthTable(contentObject_truthSet);
+ loopTruthTable(cpi_truthSet);
+
+ loopTruthTable(v1_interests_truthSet);
+ loopTruthTable(v1_contentObject_truthSet);
+ loopTruthTable(v1_cpi_truthSet);
+}
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(&notify);
+
+ 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), &params);
+ 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), &params);
+ 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, &params);
+
+ 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, &params);
+
+ 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(&params);
+
+ // 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(&notify);
+
+ // 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(&notification);
+
+ 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(&params);
+ 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(&params);
+}
+
+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(&params);
+ 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(&params);
+}
+
+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(&copy);
+}
+
+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(&params);
+ parcMemory_Deallocate((void **) &test);
+ parcMemory_Deallocate((void **) &truth_formatted);
+ parcMemory_Deallocate((void **) &params_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(&params);
+}
+
+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(&params);
+}
+
+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(&params);
+ parcMemory_Deallocate((void **) &test);
+ parcMemory_Deallocate((void **) &truth_formatted);
+ parcMemory_Deallocate((void **) &params_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);
+}